2015-12-05 9 views
6

Ich habe einen großen Datenrahmen, und ich gruppiere von einer bis n Spalten und möchte eine Funktion auf diese Gruppen über zwei Spalten (z. B. foo und bar) anwenden.Wie man groupby über mehrere Spalten transformieren kann

Hier ist ein Beispiel Datenrahmen:

foo_function = lambda x: np.sum(x.a+x.b) 

df = pd.DataFrame({'a':[1,2,3,4,5,6], 
        'b':[1,2,3,4,5,6], 
        'c':['q', 'q', 'q', 'q', 'w', 'w'], 
        'd':['z','z','z','o','o','o']}) 

# works with apply, but I want transform: 
df.groupby(['c', 'd'])[['a','b']].apply(foo_function) 
# transform doesn't work! 
df.groupby(['c', 'd'])[['a','b']].transform(foo_function) 
TypeError: cannot concatenate a non-NDFrame object 

Aber transform offenbar nicht in der Lage ist, mehrere Spalten miteinander zu kombinieren, da sie separat in jeder Spalte sehen (im Gegensatz zu rechnen). Was ist die nächstbeste Alternative in Bezug auf Geschwindigkeit/Eleganz? z.B. Ich könnte apply verwenden und dann df['new_col'] erstellen, indem ich pd.match benutze, aber das würde die Anpassung über manchmal multiple groupby Spalten (col1 und col2) erfordern, die wirklich hacky scheint/eine ziemlich Menge Code nehmen würde.

-> Gibt es eine Funktion wie groupby(). Transform, die Funktionen verwenden kann, die über mehrere Spalten arbeiten? Wenn das nicht existiert, was ist der beste Hack?

Antwort

5

Circa Pandas Version 0.18, es scheint, die ursprüngliche Antwort (unten) funktioniert nicht mehr.

Stattdessen, wenn Sie eine groupby Berechnung über mehrere Spalten zu tun, führen Sie die mehrspaltigen Berechnung ersten und dann die groupby:

df = pd.DataFrame({'a':[1,2,3,4,5,6], 
        'b':[1,2,3,4,5,6], 
        'c':['q', 'q', 'q', 'q', 'w', 'w'], 
        'd':['z','z','z','o','o','o']}) 
df['e'] = df['a'] + df['b'] 
df['e'] = (df.groupby(['c', 'd'])['e'].transform('sum')) 
print(df) 

Ausbeuten

a b c d e 
0 1 1 q z 12 
1 2 2 q z 12 
2 3 3 q z 12 
3 4 4 q o 8 
4 5 5 w o 22 
5 6 6 w o 22 

Ursprüngliche Antwort:

Der Fehler mich Te le gram m:

TypeError: cannot concatenate a non-NDFrame object 

legt nahe, dass, um eine NDFrame (wie eine Serie oder Dataframe) zu verketten, sollte die foo_function zurück. Wenn Sie eine Serie zurück, dann:

In [99]: df.groupby(['c', 'd']).transform(lambda x: pd.Series(np.sum(x['a']+x['b']))) 
Out[99]: 
    a b 
0 12 12 
1 12 12 
2 12 12 
3 8 8 
4 22 22 
5 22 22 
+0

Dies wirft einen Schlüsselfehler in 0.19.1. Es war mein Verständnis, dass die Transformation eine Reihe (jede Spalte) an die Funktion übergibt. Aber seltsamerweise übergibt es auch einmal einen Datenrahmen, wenn das Folgende mit den obigen Daten ausgeführt wird. 'df.groupby (['c', 'd']). transform (lambda x: drucken (type (x)))'.Das sieht wie ein Fehler aus –

+0

@TedPetrou: Danke für das Hinzeigen. Als Leistungsverbesserung könnte "transform" - wie "apply" - versuchen, die Funktion auf (Sub-) Datenrahmen anzuwenden. Unter bestimmten Umständen (z. B. vektorisierte Funktion und viele Spalten) könnte dies schneller sein als die einmalige Anwendung der Funktion für jede Gruppe und Spalte. – unutbu

+0

@TedPetrou: In Bezug auf den 'KeyError' - jetzt, wo ich auf meine ursprüngliche Antwort zurückblicke, glaube ich nicht, dass die von mir vorgeschlagene Lösung eine gute Lösung ist. Die 'Transformation' ruft die Funktion einmal für jede Gruppe auf. Sie erhalten eine bessere Leistung, wenn Sie vektorisierte Funktionen bei größeren Eingaben weniger oft aufrufen. Daher ist es sinnvoller, 'df ['a'] + df ['b']' für die gesamten Spalten * vor dem Aufruf von 'transform' zu berechnen. Ich habe den Beitrag oben geändert, um zu zeigen, was ich meine. – unutbu

1

So wie ich die Frage lesen, möchten Sie in der Lage sein, etwas zu tun, willkürlich mit den beiden Einzelwerte aus den beiden Spalten. Sie müssen nur sicherstellen, dass ein Datenrahmen von der gleichen Größe zurückzukehren, wie Sie in geben bekommen Ich denke, der beste Weg ist, einfach eine neue Spalte zu machen, wie folgt aus:.

df = pd.DataFrame({'a':[1,2,3,4,5,6], 
        'b':[1,2,3,4,5,6], 
        'c':['q', 'q', 'q', 'q', 'w', 'w'], 
        'd':['z','z','z','o','o','o']}) 
df['e']=0 

def f(x): 
    y=(x['a']+x['b'])/sum(x['b']) 
    return pd.DataFrame({'e':y,'a':x['a'],'b':x['b']}) 

df.groupby(['c','d']).transform(f) 

:

a b e 
0 1 1 0.333333 
1 2 2 0.666667 
2 3 3 1.000000 
3 4 4 2.000000 
4 5 5 0.909091 
5 6 6 1.090909 

Wenn Sie einen sehr komplizierten Datenrahmen haben, können Sie Ihre Spalten (zB df.groupby(['c'])['a','b','e'].transform(f))

Dieser Pick sicher sehr unelegant sieht für mich, aber es ist noch viel schneller als apply auf große Datensätze.

Eine weitere Alternative ist die Verwendung von set_index, um alle benötigten Spalten zu erfassen und dann nur eine Spalte an transform weiterzuleiten.