Betrachten Sie den folgenden Datenrahmen:
A B C D
0 foo one 0.162003 0.087469
1 bar one -1.156319 -1.526272
2 foo two 0.833892 -1.666304
3 bar three -2.026673 -0.322057
4 foo two 0.411452 -0.954371
5 bar two 0.765878 -0.095968
6 foo one -0.654890 0.678091
7 foo three -1.789842 -1.130922
Die folgenden Befehle funktionieren:
> df.groupby('A').apply(lambda x: (x['C'] - x['D']))
> df.groupby('A').apply(lambda x: (x['C'] - x['D']).mean())
aber keine der folgenden Arbeiten:
> df.groupby('A').transform(lambda x: (x['C'] - x['D']))
ValueError: could not broadcast input array from shape (5) into shape (5,3)
> df.groupby('A').transform(lambda x: (x['C'] - x['D']).mean())
TypeError: cannot concatenate a non-NDFrame object
Warum? Das Beispiel in der Dokumentation scheint darauf hinzudeuten, dass das Aufrufen transform
einer Gruppe die zeilenweise Operationsverarbeitung ermöglicht:
# Note that the following suggests row-wise operation (x.mean is the column mean)
zscore = lambda x: (x - x.mean()) / x.std()
transformed = ts.groupby(key).transform(zscore)
Mit anderen Worten, ich dachte, dass die Transformation im Wesentlichen eine bestimmte Art der Anwendung ist (die nicht aggregiert). Wo irre ich mich
Als Referenz ist unten die Konstruktion des ursprünglichen Datenrahmens oben aufgeführt:
df = pd.DataFrame({'A' : ['foo', 'bar', 'foo', 'bar',
'foo', 'bar', 'foo', 'foo'],
'B' : ['one', 'one', 'two', 'three',
'two', 'two', 'one', 'three'],
'C' : randn(8), 'D' : randn(8)})
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.zscore
),transform
erhalten Sie eine Lambda-Funktion, die davon ausgeht, dass es sich bei jedemx
Element um ein Element innerhalb der handeltgroup
, und geben außerdem einen Wert pro Element in der Gruppe zurück. Was vermisse ich?apply
übergibt den gesamten df,transform
übergibt aber jede Spalte einzeln als Serie. 2)apply
kann jede Formausgabe zurückgeben (Skalar / Serie / Datenrahmen / Array / Liste ...), währendtransform
eine Sequenz (1D-Serie / Array / Liste) mit derselben Länge wie die Gruppe zurückgegeben werden muss. Deshalb braucht das OPapply()
nichttransform()
. Dies ist eine gute Frage, da der Arzt beide Unterschiede nicht klar erklärt hat. (ähnlich der Unterscheidung zwischenapply/map/applymap
oder anderen Dingen ...)Antworten:
Zwei Hauptunterschiede zwischen
apply
undtransform
Es gibt zwei Hauptunterschiede zwischen der
transform
und derapply
Groupby-Methode.apply
Übergibt implizit alle Spalten für jede Gruppe als DataFrame an die benutzerdefinierte Funktion.transform
jede Spalte für jede Gruppe einzeln als Serie an die benutzerdefinierte Funktion übergeben.apply
kann einen Skalar oder eine Serie oder einen Datenrahmen (oder ein Numpy-Array oder sogar eine Liste) zurückgeben .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 gleichzeitigapply
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 wurdetransform
.Beispiele
Lassen Sie uns einige Beispieldaten erstellen und die Gruppen untersuchen, damit Sie sehen können, wovon ich spreche:
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.
Übergeben wir diese Funktion nun sowohl an groupby
apply
als auch an dietransform
Methoden, um zu sehen, welches Objekt an sie übergeben wird: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
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 subtrahierena
vonb
innen unserer benutzerdefinierten Funktion würden wir einen Fehler bekommentransform
. Siehe unten: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: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 diedisplay
Funktion aus demIPython.display
Modul verwenden möchte, damit die DataFrames in einem Jupyter-Notizbuch gut in HTML ausgegeben werden: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 undtransform
muss daher eine Folge von zwei Zeilen zurückgeben. Ist dies nicht der Fall, wird ein Fehler ausgelöst: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:
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:quelle
np
ist nicht definiert. Ich gehe davon aus, dass Anfänger es begrüßen würden, wenn Sieimport numpy as np
in Ihre Antwort aufnehmen.Da ich mich mit
.transform
Operation im Vergleich zu Operation ähnlich verwirrt fühlte,.apply
fand ich einige Antworten, die etwas Licht in das Problem brachten. Diese Antwort war zum Beispiel sehr hilfreich.Mein Takeout ist so weit , dass
.transform
Wille Arbeit (oder viel) mitSeries
(Spalten) isoliert voneinander . Dies bedeutet, dass in Ihren letzten beiden Anrufen:Sie haben darum gebeten
.transform
, Werte aus zwei Spalten zu übernehmen, und "es" sieht tatsächlich nicht beide gleichzeitig (sozusagen).transform
Schauen Sie sich die Datenrahmenspalten nacheinander an und geben Sie eine Reihe (oder Gruppe von Reihen) zurück, die aus Skalaren besteht, die sich mehrmals wiederholenlen(input_column)
.Dieser Skalar, der verwendet werden sollte
.transform
, um das zu erstellen ,Series
ist das Ergebnis einer Reduktionsfunktion, die auf eine Eingabe angewendet wirdSeries
(und jeweils nur auf EINE Reihe / Spalte).Betrachten Sie dieses Beispiel (in Ihrem Datenrahmen):
wird ergeben:
Das ist genau das Gleiche, als würden Sie es jeweils nur für eine Spalte verwenden:
Nachgeben:
Beachten Sie, dass
.apply
im letzten Beispiel (df.groupby('A')['C'].apply(zscore)
) genau so funktioniert, es jedoch fehlschlägt, wenn Sie versuchen, es in einem Datenrahmen zu verwenden:gibt Fehler:
Wo sonst ist es
.transform
nützlich? Im einfachsten Fall wird versucht, die Ergebnisse der Reduktionsfunktion wieder dem ursprünglichen Datenrahmen zuzuweisen.Nachgeben:
Die gleichen Versuch , mit
.apply
würdeNaNs
insum_C
. Denn.apply
würde ein reduziertes zurückgebenSeries
, das es nicht zurücksenden kann:Geben:
Es gibt auch Fälle, in denen
.transform
die Daten gefiltert werden:Ich hoffe, das bringt etwas mehr Klarheit.
quelle
.transform()
könnte auch zum Füllen fehlender Werte verwendet werden. Insbesondere, wenn Sie den Gruppenmittelwert oder die Gruppenstatistik anNaN
Werte in dieser Gruppe senden möchten . Leider war die Pandas-Dokumentation auch für mich nicht hilfreich..groupby().filter()
macht das gleiche. Vielen Dank für Ihre Erklärung.apply()
und.transform()
macht mich auch sehr verwirrt.df.groupby().transform()
für eine Untergruppe df nicht funktionieren kann, ich erhalte immer den Fehler,ValueError: transform must return a scalar value for each group
weiltransform
SpaltenIch werde ein sehr einfaches Snippet verwenden, um den Unterschied zu veranschaulichen:
Der DataFrame sieht folgendermaßen aus:
Diese Tabelle enthält 3 Kunden-IDs. Jeder Kunde hat drei Transaktionen durchgeführt und jedes Mal 1,2,3 Dollar bezahlt.
Jetzt möchte ich die Mindestzahlung finden, die jeder Kunde geleistet hat. Es gibt zwei Möglichkeiten:
Verwenden von
apply
:grouping.min ()
Die Rückkehr sieht so aus:
Verwenden von
transform
:grouping.transform (min)
Die Rückkehr sieht so aus:
Beide Methoden geben ein
Series
Objekt zurück, aber daslength
des ersten ist 3 und daslength
des zweiten ist 9.Wenn Sie antworten möchten
What is the minimum price paid by each customer
, ist dieapply
Methode die geeignetere.Wenn Sie antworten
What is the difference between the amount paid for each transaction vs the minimum payment
möchten, möchten Sie Folgendes verwendentransform
:Apply
funktioniert hier nicht einfach, weil es eine Serie der Größe 3 zurückgibt, aber die Länge des ursprünglichen df beträgt 9. Sie können es nicht einfach wieder in den ursprünglichen df integrieren.quelle
ist wie
oder
quelle