Mehrere Aggregationen derselben Spalte mit pandas GroupBy.agg ()

127

Gibt es eine integrierte Pandas-Methode, um zwei verschiedene Aggregationsfunktionen f1, f2auf dieselbe Spalte anzuwenden df["returns"], ohne agg()mehrmals aufrufen zu müssen ?

Beispieldatenrahmen:

import pandas as pd
import datetime as dt

pd.np.random.seed(0)
df = pd.DataFrame({
         "date"    :  [dt.date(2012, x, 1) for x in range(1, 11)], 
         "returns" :  0.05 * np.random.randn(10), 
         "dummy"   :  np.repeat(1, 10)
}) 

Der syntaktisch falsche, aber intuitiv richtige Weg wäre:

# Assume `f1` and `f2` are defined for aggregating.
df.groupby("dummy").agg({"returns": f1, "returns": f2})

Offensichtlich erlaubt Python keine doppelten Schlüssel. Gibt es eine andere Art und Weise zum Ausdrücken des Eingangs agg()? Vielleicht würde eine Liste von Tupeln [(column, function)]besser funktionieren, um mehrere Funktionen auf dieselbe Spalte anzuwenden? Aber es agg()scheint, als würde es nur ein Wörterbuch akzeptieren.

Gibt es dafür eine Problemumgehung, außer eine Hilfsfunktion zu definieren, die nur beide darin enthaltenen Funktionen anwendet? (Wie würde das überhaupt mit Aggregation funktionieren?)

ely
quelle
Verwandte - Aggregation in Pandas
Jezrael
2
Ab 0,25 bietet pandas eine intuitivere Syntax für mehrere Aggregationen sowie das Umbenennen von Ausgabespalten. Siehe die Dokumentation zu benannten Aggregationen .
CS95
Zu Ihrer
Information,
1
Zu Ihrer Information, die akzeptierte Antwort ist ebenfalls veraltet - übergeben Sie agg () kein Diktat von Diktaten.
cs95
@ cs95: Ich weiß, dass es veraltet ist. Ich sage, SO wird mit alten veralteten Lösungen aus alten Versionen übersät. SO hat keine Möglichkeit, dies zu markieren - außer Kommentare.
smci

Antworten:

159

Sie können die Funktionen einfach als Liste übergeben:

In [20]: df.groupby("dummy").agg({"returns": [np.mean, np.sum]})
Out[20]:         
           mean       sum
dummy                    
1      0.036901  0.369012

oder als Wörterbuch:

In [21]: df.groupby('dummy').agg({'returns':
                                  {'Mean': np.mean, 'Sum': np.sum}})
Out[21]: 
        returns          
           Mean       Sum
dummy                    
1      0.036901  0.369012
bmu
quelle
4
Gibt es eine Möglichkeit, die Namen der Ergebnisspalten anzugeben?
Ben
3
@ Ben Ich denke, Sie müssen danach eine Umbenennung verwenden. Beispiel von Tom Augspurger (siehe Zelle 25)
Stewbaca
1
@ Ben: Ich habe ein Beispiel hinzugefügt
bmu
10
@sparc_spread Das Übergeben mehrerer Funktionen als Liste wird in der Pandas-Dokumentation ausführlich beschrieben . Das Umbenennen und Übergeben mehrerer Funktionen als Wörterbuch wird in einer zukünftigen Version von Pandas nicht mehr empfohlen. Details finden Sie im Änderungsprotokoll von 0,20 , das ich auch an anderer Stelle in SO zusammengefasst habe .
Joelostblom
3
Es wurde bereits gesagt, aber die Verwendung von Wörterbüchern zum Umbenennen von Ausgabespalten nach Alter ist veraltet. Sie können stattdessen eine Liste von Tupeln angeben. Siehe diese Antwort.
CS95
101

TLDR; Pandas groupby.aggverfügt über eine neue, einfachere Syntax zum Festlegen von (1) Aggregationen in mehreren Spalten und (2) mehreren Aggregationen in einer Spalte. Um dies für Pandas> = 0,25 zu tun , verwenden Sie

df.groupby('dummy').agg(Mean=('returns', 'mean'), Sum=('returns', 'sum'))

           Mean       Sum
dummy                    
1      0.036901  0.369012

ODER

df.groupby('dummy')['returns'].agg(Mean='mean', Sum='sum')

           Mean       Sum
dummy                    
1      0.036901  0.369012

Pandas> = 0,25: Benannte Aggregation

Pandas hat das Verhalten GroupBy.aggzugunsten einer intuitiveren Syntax für die Angabe benannter Aggregationen geändert . Weitere Informationen finden Sie im Abschnitt zu den 0,25-Dokumenten zu Verbesserungen sowie zu den relevanten GitHub-Problemen GH18366 und GH26512 .

Aus der Dokumentation,

Um die spaltenspezifische Aggregation mit Kontrolle über die Namen der Ausgabespalten zu unterstützen, akzeptiert pandas die spezielle Syntax in GroupBy.agg(), die als "benannte Aggregation" bezeichnet wird

  • Die Schlüsselwörter sind die Namen der Ausgabespalten
  • Die Werte sind Tupel, deren erstes Element die auszuwählende Spalte und das zweite Element die auf diese Spalte anzuwendende Aggregation ist. Pandas stellt dem pandas.NamedAgg namedtuple die Felder ['column', 'aggfunc'] zur Verfügung, um die Argumente klarer zu machen. Wie üblich kann die Aggregation ein aufrufbarer oder ein String-Alias ​​sein.

Sie können jetzt ein Tupel über Schlüsselwortargumente übergeben. Die Tupel folgen dem Format von (<colName>, <aggFunc>).

import pandas as pd

pd.__version__                                                                                                                            
# '0.25.0.dev0+840.g989f912ee'

# Setup
df = pd.DataFrame({'kind': ['cat', 'dog', 'cat', 'dog'],
                   'height': [9.1, 6.0, 9.5, 34.0],
                   'weight': [7.9, 7.5, 9.9, 198.0]
})

df.groupby('kind').agg(
    max_height=('height', 'max'), min_weight=('weight', 'min'),)

      max_height  min_weight
kind                        
cat          9.5         7.9
dog         34.0         7.5

Alternativ können Sie pd.NamedAgg(im Wesentlichen ein benanntes Tupel) verwenden, um die Dinge expliziter zu machen.

df.groupby('kind').agg(
    max_height=pd.NamedAgg(column='height', aggfunc='max'), 
    min_weight=pd.NamedAgg(column='weight', aggfunc='min')
)

      max_height  min_weight
kind                        
cat          9.5         7.9
dog         34.0         7.5

Für Serien ist es noch einfacher. Übergeben Sie die aggfunc einfach an ein Schlüsselwortargument.

df.groupby('kind')['height'].agg(max_height='max', min_height='min')    

      max_height  min_height
kind                        
cat          9.5         9.1
dog         34.0         6.0       

Wenn Ihre Spaltennamen keine gültigen Python-IDs sind, verwenden Sie ein Wörterbuch mit dem Entpacken:

df.groupby('kind')['height'].agg(**{'max height': 'max', ...})

Pandas <0,25

Wenn Sie in neueren Versionen von Pandas bis zu 0,24 ein Wörterbuch zum Festlegen von Spaltennamen für die Aggregationsausgabe verwenden, erhalten Sie Folgendes FutureWarning:

df.groupby('dummy').agg({'returns': {'Mean': 'mean', 'Sum': 'sum'}})
# FutureWarning: using a dict with renaming is deprecated and will be removed 
# in a future version

Die Verwendung eines Wörterbuchs zum Umbenennen von Spalten ist in Version 0.20 veraltet. Bei neueren Versionen von Pandas kann dies einfacher angegeben werden, indem eine Liste von Tupeln übergeben wird. Wenn Sie die Funktionen auf diese Weise angeben , müssen alle Funktionen für diese Spalte als Tupel von (Name, Funktion) Paaren angegeben werden.

df.groupby("dummy").agg({'returns': [('op1', 'sum'), ('op2', 'mean')]})

        returns          
            op1       op2
dummy                    
1      0.328953  0.032895

Oder,

df.groupby("dummy")['returns'].agg([('op1', 'sum'), ('op2', 'mean')])

            op1       op2
dummy                    
1      0.328953  0.032895
cs95
quelle
4
Dies sollte die beste Antwort sein, da mit der neueren Version der Benutzeroberfläche eine klarere und sauberere Lösung verwendet wird.
NKSHELL
Die für die benannte Aggregation verwendeten Beispiele lösen nicht das ursprüngliche Problem der Verwendung mehrerer Aggregationen in derselben Spalte. Können Sie beispielsweise für die Höhe sowohl nach min als auch nach max aggregieren, ohne zuvor eine Teilmenge für zu setzen df.groupby('kind')['height']?
Sieger
1
@victor Ich habe oben in der Antwort eine TLDR hinzugefügt, die die Frage direkt anspricht. Und die Antwort auf Ihre zweite Frage lautet ja, bitte schauen Sie sich die Bearbeitung meiner Antwort an.
CS95
Ein allgemeinerer Code zum letzten Beispiel Ihrer Antwort> = 0,25 für die Aggregation mehrerer Spalten wie diese wäre großartig gewesen. df.groupby("kind").agg(**{ 'max height': pd.NamedAgg(column='height', aggfunc=max), 'min weight': pd.NamedAgg(column='weight', aggfunc=min) })
Onur Ece
6

Würde so etwas funktionieren:

In [7]: df.groupby('dummy').returns.agg({'func1' : lambda x: x.sum(), 'func2' : lambda x: x.prod()})
Out[7]: 
              func2     func1
dummy                        
1     -4.263768e-16 -0.188565
Chang She
quelle
2
Nein, das funktioniert nicht. Wenn Sie sich die Dokumentzeichenfolge ansehen, aggregateheißt es explizit, dass dictdie Schlüssel bei der Übergabe von a Spaltennamen sein müssen. Entweder haben Sie Ihr Beispiel eingegeben, ohne nach diesem Fehler zu suchen, oder Pandas bricht hier seine eigenen Dokumente.
Ely
N / MI hat den zusätzlichen Anruf returnsdort nicht gesehen. Das ist also die Serienversion von Aggregat? Ich möchte die DataFrame-Version des Aggregats erstellen und mehrere verschiedene Aggregationen gleichzeitig auf jede Spalte anwenden.
Ely
1
Versuchen Sie Folgendes: df.groupby ('dummy'). Agg ({'return': {'func1': lambda x: x.sum (), 'func2': lambda x: x.mean ()}})
Chang Sie
Es gibt einen Assertionsfehler ohne Meldung. Nach dem Aussehen des Codes (pandas.core.internals.py, Zeilen 406-408, Version 0.7.3) sieht es so aus, als würde am Ende überprüft, ob nicht mehr Spalten zurückgegeben werden, als Schlüssel in der ersten enthalten sind Ebene des Aggregationswörterbuchs.
ely
Funktioniert gut auf Master. Sie möchten versuchen, ein Update durchzuführen?
Chang She