2012-10-24 18 views
11

Ich versuche, DataFrame zu transformieren, so dass einige der Zeilen eine bestimmte Anzahl repliziert werden. Zum Beispiel:Pandas: Funktion auf DataFrame anwenden, die mehrere Zeilen zurückgeben kann

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

    class count 
0  A  1 
1  B  0 
2  C  2 

sollen umgewandelt werden:

class 
0  A 
1  C 
2  C 

Dies ist die Umkehrung der Aggregation mit Zählfunktion. Gibt es einen einfachen Weg, es in Pandas zu erreichen (ohne for loops oder list comprehensions)?

Eine Möglichkeit könnte sein, DataFrame.applymap Funktion zurückzukehren mehrere Zeilen (ähnlich apply Methode von GroupBy). Ich glaube jedoch nicht, dass es jetzt in Pandas möglich ist.

+0

Ich habe auch eine allgemeine Funktion im Hinterkopf, die es erlaubt, mehrere, eine oder null Zeilen zurückzugeben, abhängig von den Werten in der Spalte "count". – btel

+0

Wenn Sie zu dieser Frage in 2017 + kommen, überprüfen Sie meine Antwort für eine effizientere und unkomplizierte Lösung. –

Antwort

17

könnten Sie verwenden groupby:

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

so erhalten Sie

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

Sie den Index des Ergebnisses beheben können jedoch wie Sie

+0

Das löst mein Problem! Danke Wes. – btel

+1

Gute Antwort! Wenn ich Dutzende von anderen Spalten habe, gibt es eine einfache Möglichkeit, diese Spalten im Ergebnis von "f" zu erhalten, außer sie alle explizit einzugeben? –

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

wird eine verschachtelte Liste erstellen:

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

die Sie dann mit Listenkomprehensionen iterieren einen neuen Datenrahmen zu erstellen:

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

Natürlich können Sie es tun in einer einzigen Zeile auch, wenn Sie möchten:

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

Ich weiß, dass dies eine alte Frage, aber ich hatte Probleme, Wes 'Antwort für mehrere Spalten im Datenrahmen arbeiten zu lassen, so dass ich seinen Code ein bisschen allgemeiner gemacht habe. Dachte, ich würde teilen, falls jemand anderes auf diese Frage mit demselben Problem stolpert.

Sie geben im Grunde nur an, in welcher Spalte die Zählungen enthalten sind und Sie erhalten einen erweiterten Datenrahmen als Gegenleistung.

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 

Returns:

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 

Im Hinblick auf die Geschwindigkeit, ist meine Basis df 10 Spalten von ~ 6k Zeilen und wenn sie expandiert ist ~ 100.000 Zeilen dauert ca. 7 Sekunden. Ich bin nicht sicher in diesem Fall, wenn Gruppierung notwendig oder weise ist, da es alle Spalten zur Gruppenform nimmt, aber hey, was auch immer nur 7 Sekunden.

0

Diese Frage ist sehr alt und die Antworten spiegeln nicht die modernen Fähigkeiten der Pandas wider. Sie können iterrows verwenden, um jede Zeile zu durchlaufen, und dann den DataFrame-Konstruktor verwenden, um neue DataFrames mit der korrekten Anzahl von Zeilen zu erstellen. Schließlich verwenden Sie pd.concat, um alle Zeilen zusammen zu verketten.

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 

Dies hat den Vorteil, mit jeder Größe DataFrame zu arbeiten.

Verwandte Themen