2012-10-24 16 views
11

Próbuję przekształcić DataFrame, tak aby niektóre wiersze były replikowane określoną liczbę razy. Na przykład:pandy: zastosuj funkcję do DataFrame, która może zwrócić wiele wierszy.

df = pd.DataFrame({'class': ['A', 'B', 'C'], 'count':[1,0,2]}) 

    class count 
0  A  1 
1  B  0 
2  C  2 

powinny być przekształcone do:

class 
0  A 
1  C 
2  C 

Jest to odwrotna sumowania z funkcją zliczania. Czy istnieje prosty sposób, aby osiągnąć to w pandach (bez użycia pętli lub listy)?

Jedną z możliwości może być, aby funkcja DataFrame.applymap powróciła do wielu wierszy (podobnie jak metoda apply z GroupBy). Jednak nie sądzę, by było to możliwe teraz w pandach.

+0

Mam również na uwadze ogólną funkcję, która pozwoli na zwrócenie wielu, jednego lub zera wierszy w zależności od wartości w kolumnie 'count'. – btel

+0

Jeśli przychodzisz na to pytanie w 2017+, sprawdź moją odpowiedź na bardziej wydajne i proste rozwiązanie. –

Odpowiedz

17

Można użyć GroupBy:

def f(group): 
    row = group.irow(0) 
    return DataFrame({'class': [row['class']] * row['count']}) 
df.groupby('class', group_keys=False).apply(f) 

więc masz

In [25]: df.groupby('class', group_keys=False).apply(f) 
Out[25]: 
    class 
0  A 
0  C 
1  C 

można naprawić indeks rezultacie jednak chcesz

+0

To rozwiązuje mój problem! Dzięki, Wes. – btel

+1

Dobra odpowiedź! Jeśli mam tuziny innych kolumn, czy istnieje prosty sposób na zachowanie tych kolumn w wyniku 'f' innego niż ich jawne wpisanie? –

1
repeated_items = [list(row[1]*row[2]) for row in df.itertuples()] 

stworzy zagnieżdżony listę:

[['A'], [], ['C', 'C']] 

które można następnie iterowaniu z listowych, aby utworzyć nową ramkę danych:

new_df = pd.DataFrame({"class":[j for i in repeated_items for j in i]}) 

Oczywiście, można to zrobić w jednym wierszu, jeśli chcesz:

new_df = pd.DataFrame({"class":[j for i in [list(row[1]*row[2]) for row in df.itertuples()] for j in i]}) 
3

wiem, że to jest stare pytanie, ale miałem problem z uzyskaniem odpowiedzi Wesa na pracę dla wielu kolumn w ramce danych, więc zrobiłem jego kod nieco bardziej ogólny. Pomyślałem, że podzielę się, jeśli ktoś inny natknie się na to pytanie z tym samym problemem.

Po prostu określasz, która kolumna ma liczniki, a otrzymasz w zamian rozszerzoną ramkę danych.

import pandas as pd 
df = pd.DataFrame({'class 1': ['A','B','C','A'], 
        'class 2': [ 1, 2, 3, 1], 
        'count': [ 3, 3, 3, 1]}) 
print df,"\n" 

def f(group, *args): 
    row = group.irow(0) 
    Dict = {} 
    row_dict = row.to_dict() 
    for item in row_dict: Dict[item] = [row[item]] * row[args[0]] 
    return pd.DataFrame(Dict) 

def ExpandRows(df,WeightsColumnName): 
    df_expand = df.groupby(df.columns.tolist(), group_keys=False).apply(f,WeightsColumnName).reset_index(drop=True) 
    return df_expand 


df_expanded = ExpandRows(df,'count') 
print df_expanded 

Powroty:

class 1 class 2 count 
0  A  1  3 
1  B  2  3 
2  C  3  3 
3  A  1  1 

    class 1 class 2 count 
0  A  1  1 
1  A  1  3 
2  A  1  3 
3  A  1  3 
4  B  2  3 
5  B  2  3 
6  B  2  3 
7  C  3  3 
8  C  3  3 
9  C  3  3 

W odniesieniu do prędkości, moja baza df wynosi 10 kolumny przez ~ 6k wiersze i po rozwinięciu wynosi ~ 100000 rzędy trwa ~ 7 sekund. W tym przypadku nie jestem pewien, czy grupowanie jest konieczne czy mądre, ponieważ bierze wszystkie kolumny do postaci grupowej, ale hej, co tylko 7 sekund.

0

To pytanie jest bardzo stare, a odpowiedzi nie odzwierciedlają nowoczesnych możliwości pandy. Można użyć iterrows, aby wykonać pętlę nad każdym wierszem, a następnie użyć konstruktora DataFrame do utworzenia nowych obiektów DataFrames z odpowiednią liczbą wierszy. Na koniec użyj pd.concat, aby połączyć ze sobą wszystkie wiersze.

pd.concat([pd.DataFrame(data=[row], index=range(row['count'])) 
      for _, row in df.iterrows()], ignore_index=True) 

    class count 
0  A  1 
1  C  2 
2  C  2 

Ma to zaletę pracy z dowolnym rozmiarem ramki DataFrame.