Zwei Hauptunterschiede zwischen applyundtransform
Es gibt zwei Hauptunterschiede zwischen der transformund der applyGroupby-Methode.
- Eingang:
applyÜbergibt implizit alle Spalten für jede Gruppe als DataFrame an die benutzerdefinierte Funktion.
- Dabei wird
transformjede Spalte für jede Gruppe einzeln als Serie an die benutzerdefinierte Funktion übergeben.
- Ausgabe:
- Die an übergebene benutzerdefinierte Funktion
applykann einen Skalar oder eine Serie oder einen Datenrahmen (oder ein Numpy-Array oder sogar eine Liste) zurückgeben .
- Die an übergebene benutzerdefinierte Funktion
transformmuss eine Sequenz (eine eindimensionale Serie, ein Array oder eine Liste) mit derselben Länge wie die Gruppe zurückgeben .
Funktioniert also jeweils transformnur für eine Serie und gleichzeitig applyfür den gesamten DataFrame.
Überprüfen der benutzerdefinierten Funktion
Es kann sehr hilfreich sein, die Eingabe in Ihre benutzerdefinierte Funktion zu überprüfen, die an applyoder übergeben wurde transform.
Beispiele
Lassen Sie uns einige Beispieldaten erstellen und die Gruppen untersuchen, damit Sie sehen können, wovon ich spreche:
import pandas as pd
import numpy as np
df = pd.DataFrame({'State':['Texas', 'Texas', 'Florida', 'Florida'],
'a':[4,5,1,3], 'b':[6,10,3,11]})
State a b
0 Texas 4 6
1 Texas 5 10
2 Florida 1 3
3 Florida 3 11
Erstellen wir eine einfache benutzerdefinierte Funktion, die den Typ des implizit übergebenen Objekts ausgibt und dann einen Fehler auslöst, damit die Ausführung gestoppt werden kann.
def inspect(x):
print(type(x))
raise
Übergeben wir diese Funktion nun sowohl an groupby applyals auch an die transformMethoden, um zu sehen, welches Objekt an sie übergeben wird:
df.groupby('State').apply(inspect)
<class 'pandas.core.frame.DataFrame'>
<class 'pandas.core.frame.DataFrame'>
RuntimeError
Wie Sie sehen können, wird ein DataFrame an die inspectFunktion übergeben. Sie fragen sich vielleicht, warum der Typ DataFrame zweimal ausgedruckt wurde. Pandas führt die erste Gruppe zweimal aus. Auf diese Weise wird festgestellt, ob es einen schnellen Weg gibt, die Berechnung abzuschließen, oder nicht. Dies ist ein kleines Detail, über das Sie sich keine Sorgen machen sollten.
Jetzt machen wir dasselbe mit transform
df.groupby('State').transform(inspect)
<class 'pandas.core.series.Series'>
<class 'pandas.core.series.Series'>
RuntimeError
Es wird eine Serie übergeben - ein völlig anderes Pandas-Objekt.
Es transformdarf also immer nur mit einer Serie gleichzeitig gearbeitet werden. Es ist nicht unmöglich, auf zwei Spalten gleichzeitig zu wirken. Also, wenn wir versuchen , Spalte und subtrahieren avon binnen unserer benutzerdefinierten Funktion würden wir einen Fehler bekommen transform. Siehe unten:
def subtract_two(x):
return x['a'] - x['b']
df.groupby('State').transform(subtract_two)
KeyError: ('a', 'occurred at index a')
Wir erhalten einen KeyError, da Pandas versucht, den anicht vorhandenen Serienindex zu finden . Sie können diesen Vorgang mit ausführen, applyda der gesamte DataFrame vorhanden ist:
df.groupby('State').apply(subtract_two)
State
Florida 2 -2
3 -8
Texas 0 -2
1 -5
dtype: int64
Die Ausgabe ist eine Serie und etwas verwirrend, da der ursprüngliche Index beibehalten wird, aber wir haben Zugriff auf alle Spalten.
Anzeige des übergebenen Pandas-Objekts
Es kann noch hilfreicher sein, das gesamte Pandas-Objekt in der benutzerdefinierten Funktion anzuzeigen, sodass Sie genau sehen können, mit was Sie arbeiten. Sie können printAnweisungen verwenden, indem ich die displayFunktion aus dem IPython.displayModul verwenden möchte, damit die DataFrames in einem Jupyter-Notizbuch gut in HTML ausgegeben werden:
from IPython.display import display
def subtract_two(x):
display(x)
return x['a'] - x['b']
Bildschirmfoto:

Die Transformation muss eine eindimensionale Sequenz mit der gleichen Größe wie die Gruppe zurückgeben
Der andere Unterschied besteht darin, dass transformeine eindimensionale Sequenz mit der gleichen Größe wie die Gruppe zurückgegeben werden muss. In diesem speziellen Fall hat jede Gruppe zwei Zeilen und transformmuss daher eine Folge von zwei Zeilen zurückgeben. Ist dies nicht der Fall, wird ein Fehler ausgelöst:
def return_three(x):
return np.array([1, 2, 3])
df.groupby('State').transform(return_three)
ValueError: transform must return a scalar value for each group
Die Fehlermeldung beschreibt das Problem nicht wirklich. Sie müssen eine Sequenz mit der gleichen Länge wie die Gruppe zurückgeben. Eine solche Funktion würde also funktionieren:
def rand_group_len(x):
return np.random.rand(len(x))
df.groupby('State').transform(rand_group_len)
a b
0 0.962070 0.151440
1 0.440956 0.782176
2 0.642218 0.483257
3 0.056047 0.238208
Das Zurückgeben eines einzelnen skalaren Objekts funktioniert auch für transform
Wenn Sie nur einen einzelnen Skalar von Ihrer benutzerdefinierten Funktion zurückgeben, transformwird er für jede der Zeilen in der Gruppe verwendet:
def group_sum(x):
return x.sum()
df.groupby('State').transform(group_sum)
a b
0 9 16
1 9 16
2 4 14
3 4 14
transformmuss eine Zahl, eine Zeile oder dieselbe Form wie das Argument zurückgeben. Wenn es sich um eine Zahl handelt, wird die Zahl auf alle Elemente in der Gruppe festgelegt. Wenn es sich um eine Zeile handelt, wird sie an alle Zeilen in der Gruppe gesendet. In Ihrem Code gibt die Lambda-Funktion eine Spalte zurück, die nicht an die Gruppe gesendet werden kann.