Zwei Hauptunterschiede zwischen apply
undtransform
Es gibt zwei Hauptunterschiede zwischen der transform
und der apply
Groupby-Methode.
- Eingang:
apply
Übergibt implizit alle Spalten für jede Gruppe als DataFrame an die benutzerdefinierte Funktion.
- Dabei wird
transform
jede Spalte für jede Gruppe einzeln als Serie an die benutzerdefinierte Funktion übergeben.
- Ausgabe:
- Die an übergebene benutzerdefinierte Funktion
apply
kann einen Skalar oder eine Serie oder einen Datenrahmen (oder ein Numpy-Array oder sogar eine Liste) zurückgeben .
- Die an übergebene benutzerdefinierte Funktion
transform
muss eine Sequenz (eine eindimensionale Serie, ein Array oder eine Liste) mit derselben Länge wie die Gruppe zurückgeben .
Funktioniert also jeweils transform
nur für eine Serie und gleichzeitig apply
fü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 apply
oder ü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 apply
als auch an die transform
Methoden, 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 inspect
Funktion ü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 transform
darf 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 a
von b
innen 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 a
nicht vorhandenen Serienindex zu finden . Sie können diesen Vorgang mit ausführen, apply
da 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 print
Anweisungen verwenden, indem ich die display
Funktion aus dem IPython.display
Modul 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 transform
eine eindimensionale Sequenz mit der gleichen Größe wie die Gruppe zurückgegeben werden muss. In diesem speziellen Fall hat jede Gruppe zwei Zeilen und transform
muss 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, transform
wird 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
transform
muss 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.