2012-12-13 9 views
15

Eine andere Pandas Frage.pandas 'transformiert nicht funktioniert sortieren groupby output

Lesen Wes McKinney ausgezeichnetes Buch über Datenanalyse und Pandas, stieß ich auf die folgende Sache, die ich dachte funktionieren sollte:

Angenommen, ich habe ein paar Informationen über Tipps.

In [119]: 

tips.head() 
Out[119]: 
total_bill tip  sex  smoker day time size tip_pct 
0 16.99 1.01 Female False Sun  Dinner 2 0.059447 
1 10.34 1.66 Male False Sun  Dinner 3 0.160542 
2 21.01 3.50 Male False Sun  Dinner 3 0.166587 
3 23.68 3.31 Male False Sun  Dinner 2 0.139780 
4 24.59 3.61 Female False Sun  Dinner 4 0.146808 

und ich mag die fünf größten Tipps in Bezug auf die Gesamtrechnung wissen, das heißt, tip_pct für Raucher und Nichtraucher getrennt. So funktioniert das:

def top(df, n=5, column='tip_pct'): 
    return df.sort_index(by=column)[-n:] 

In [101]: 

tips.groupby('smoker').apply(top) 
Out[101]: 
      total_bill tip sex smoker day time size tip_pct 
smoker         
False 88 24.71 5.85 Male False Thur Lunch 2 0.236746 
185 20.69 5.00 Male False Sun  Dinner 5 0.241663 
51 10.29 2.60 Female False Sun  Dinner 2 0.252672 
149 7.51 2.00 Male False Thur Lunch 2 0.266312 
232 11.61 3.39 Male False Sat  Dinner 2 0.291990 

True 109 14.31 4.00 Female True Sat  Dinner 2 0.279525 
183 23.17 6.50 Male True Sun  Dinner 4 0.280535 
67 3.07 1.00 Female True Sat  Dinner 1 0.325733 
178 9.60 4.00 Female True Sun  Dinner 2 0.416667 
172 7.25 5.15 Male True Sun  Dinner 2 0.710345 

gut genug, aber dann wollte ich Pandas verwenden verwandeln das gleiche wie dies zu tun:

def top_all(df): 
    return df.sort_index(by='tip_pct') 

tips.groupby('smoker').transform(top_all) 

sondern ich diese:

TypeError: Transform function invalid for data types 

Warum? Ich weiß, dass Transformation ein Array mit den gleichen Dimensionen zurückgeben muss, die es als Eingabe akzeptiert, also dachte ich, ich würde diese Anforderung erfüllen, indem ich nur die beiden Slices (Raucher und Nichtraucher) des ursprünglichen DataFrames sortiere, ohne ihre jeweiligen Dimensionen zu ändern . Kann jemand erklären, warum es gescheitert ist?

Antwort

36

transformtransform ist nicht so gut dokumentiert, aber es scheint, dass die Funktionsweise der Transformationsfunktion nicht die gesamte Gruppe als Datenframe ist, sondern eine einzelne Spalte einer einzelnen Gruppe. Ich denke nicht, dass es wirklich für das gedacht ist, was Sie versuchen, und Ihre Lösung mit apply ist in Ordnung.

Also angenommen tips.groupby('smoker').transform(func). Es gibt zwei Gruppen, nennen Sie sie group1 und group2. Die Transformation ruft func(group1) und func(group2) nicht auf. Stattdessen ruft es func(group1['total_bill']), dann func(group1['tip']) usw. und dann func(group2['total_bill']), func(group2['total_bill']). Hier ein Beispiel:

>>> print d 
    A B C 
0 -2 5 4 
1 1 -1 2 
2 0 2 1 
3 -3 1 2 
4 5 0 2 
>>> def foo(df): 
...  print ">>>" 
...  print df 
...  print "<<<" 
...  return df 
>>> print d.groupby('C').transform(foo) 
>>> 
2 0 
Name: A 
<<< 
>>> 
2 2 
Name: B 
<<< 
>>> 
1 1 
3 -3 
4 5 
Name: A 
<<< 
>>> 
1 -1 
3 1 
4 0 
Name: B 
# etc. 

Sie können sehen, dass foo zunächst nur mit der A-Säule der C = 1 Gruppe des ursprünglichen Datenrahmen genannt wird, dann die B-Säule dieser Gruppe, dann der A-Säule der C = 2 Gruppe, usw.

Dies macht Sinn, wenn Sie darüber nachdenken, was Transformation ist. Es ist zum Anwenden von Transformationsfunktionen auf die Gruppen gedacht. Aber im Allgemeinen sind diese Funktionen nicht sinnvoll, wenn sie auf die gesamte Gruppe angewendet werden, nur auf eine bestimmte Spalte. Zum Beispiel ist das Beispiel in der Pandas-Dokumentation über Z-Standardisierung mit transform. Wenn Sie einen DataFrame mit Spalten für Alter und Gewicht haben, wäre es nicht sinnvoll, in Bezug auf den Gesamtmittelwert dieser beiden Variablen zu z-standardisieren. Es bedeutet nicht einmal etwas, das den Gesamtwert einer Menge von Zahlen, von denen einige Alter und einige Gewichte sind. Sie müssen das Alter in Bezug auf das Durchschnittsalter und das Gewicht in Bezug auf das Durchschnittsgewicht z-standardisieren, was bedeutet, dass Sie für jede Spalte getrennt transformieren möchten.

Also im Grunde müssen Sie hier nicht transform verwenden. apply ist die geeignete Funktion hier, weil apply tatsächlich auf jeder Gruppe als ein einzelnes DataFrame funktioniert, während transform auf jede Spalte jeder Gruppe funktioniert.

+1

Ausgezeichnete Antwort. Danke vielmals! –

+6

Warum ist Transformation so schlecht dokumentiert? Es macht mich verrückt.Ich denke nicht, dass Sie es ganz richtig haben (obwohl ich keine Ahnung habe, wie es tatsächlich funktioniert), als wenn Sie Druckanweisungen in Ihre Funktionen schreiben, scheint es klar zu sein, dass die Transformation Spalten als Reihen und Datenrahmen weitergibt. Es ist wirklich seltsam und ich möchte genau verstehen, was hinter den Kulissen passiert, kann aber keine Informationen darüber finden, wie die Transformation tatsächlich implementiert wird. –