Più aggregazioni della stessa colonna utilizzando pandas GroupBy.agg ()


128

Esiste un modo integrato panda per applicare due diverse funzioni di aggregazione f1, f2alla stessa colonna df["returns"], senza dover chiamare agg()più volte?

Dataframe di esempio:

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)
}) 

Il modo sintatticamente sbagliato, ma intuitivamente giusto per farlo sarebbe:

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

Ovviamente, Python non consente chiavi duplicate. C'è un altro modo per esprimere l'input a agg()? Forse un elenco di tuple [(column, function)]funzionerebbe meglio, per consentire più funzioni applicate alla stessa colonna? Ma agg()sembra che accetti solo un dizionario.

C'è una soluzione alternativa per questo oltre a definire una funzione ausiliaria che applica semplicemente entrambe le funzioni al suo interno? (Come funzionerebbe comunque con l'aggregazione?)



2
Dalla 0.25 in poi, panda fornisce una sintassi più intuitiva per più aggregazioni, oltre a rinominare le colonne di output. Vedere la documentazione sulle aggregazioni denominate .
cs95

Cordiali saluti, questa domanda è stata posta su panda 0.8.x nel 9/2012
smci

1
Cordiali saluti, anche la risposta accettata è deprecata: non passare ad agg () un dict of dicts.
cs95

@ cs95: So che è deprecato, sto dicendo che SO sta diventando disseminato di vecchie soluzioni obsolete da vecchie versioni. SO non ha modo di contrassegnarlo, a parte i commenti.
smci

Risposte:


159

Puoi semplicemente passare le funzioni come un elenco:

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

o come dizionario:

In [21]: df.groupby('dummy').agg({'returns':
                                  {'Mean': np.mean, 'Sum': np.sum}})
Out[21]: 
        returns          
           Mean       Sum
dummy                    
1      0.036901  0.369012

5
C'è un modo per specificare i nomi delle colonne dei risultati?
Ben

3
@ Ben penso che devi usare una rinomina in seguito. esempio di Tom Augspurger (vedere cella 25)
Stewbaca

1
@ Ben: ho aggiunto un esempio
bmu

10
@sparc_spread Il passaggio di più funzioni come elenco è ben descritto nella documentazione di panda . Rinominare e passare più funzioni come un dizionario sarà deprecato in una versione futura di panda. I dettagli sono nel registro delle modifiche 0.20 , che ho anche riassunto altrove su SO .
joelostblom

3
È già stato detto, ma l'uso dei dizionari per rinominare le colonne di output dall'età è deprecato. Puoi invece specificare un elenco di tuple. Vedi questa risposta.
cs95

101

TLDR; Pandas groupby.aggha una nuova sintassi più semplice per specificare (1) aggregazioni su più colonne e (2) più aggregazioni su una colonna. Quindi, per fare questo per i panda> = 0,25 , usa

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

           Mean       Sum
dummy                    
1      0.036901  0.369012

O

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

           Mean       Sum
dummy                    
1      0.036901  0.369012

Panda> = 0,25: aggregazione denominata

Pandas ha cambiato il comportamento di GroupBy.agga favore di una sintassi più intuitiva per la specifica di aggregazioni denominate. Consulta la sezione dei documenti 0.25 sui miglioramenti e sui problemi rilevanti di GitHub GH18366 e GH26512 .

Dalla documentazione,

Per supportare l'aggregazione specifica della colonna con il controllo sui nomi delle colonne di output, pandas accetta la sintassi speciale in GroupBy.agg(), nota come "aggregazione denominata", dove

  • Le parole chiave sono i nomi delle colonne di output
  • I valori sono tuple il cui primo elemento è la colonna da selezionare e il secondo elemento è l'aggregazione da applicare a quella colonna. Pandas fornisce a pandas.NamedAgg namedtuple i campi ['column', 'aggfunc'] per rendere più chiaro quali sono gli argomenti. Come al solito, l'aggregazione può essere un alias richiamabile o una stringa.

È ora possibile passare una tupla tramite argomenti di parole chiave. Le tuple seguono il formato di (<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

In alternativa, puoi usare pd.NamedAgg(essenzialmente una namedtuple) che rende le cose più esplicite.

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

È ancora più semplice per Series, basta passare l'aggfunc a un argomento di parola chiave.

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       

Infine, se i nomi delle colonne non sono identificatori python validi, usa un dizionario con decompressione:

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

Panda <0,25

Nelle versioni più recenti dei panda che portano fino a 0.24, se si utilizza un dizionario per specificare i nomi delle colonne per l'output di aggregazione, si otterrà 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

L'utilizzo di un dizionario per rinominare le colonne è deprecato nella v0.20. Nelle versioni più recenti dei panda, questo può essere specificato più semplicemente passando un elenco di tuple. Se si specificano le funzioni in questo modo, tutte le funzioni per quella colonna devono essere specificate come tuple di coppie (nome, funzione).

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

        returns          
            op1       op2
dummy                    
1      0.328953  0.032895

O,

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

            op1       op2
dummy                    
1      0.328953  0.032895

4
Questa dovrebbe essere la risposta migliore perché si utilizza una soluzione più chiara e pulita utilizzando la versione più recente dell'interfaccia.
NKSHELL

Gli esempi utilizzati per l'aggregazione denominata non risolvono il problema originale dell'utilizzo di più aggregazioni sulla stessa colonna. Ad esempio, puoi aggregare sia il minimo che il massimo per l'altezza senza prima sottoinserimento per df.groupby('kind')['height']?
vincitore

1
@victor ho aggiunto un TLDR nella parte superiore della risposta che affronta direttamente la domanda. E la risposta alla tua seconda domanda è sì, dai un'occhiata alla modifica sulla mia risposta.
cs95

Un codice più generico per l'ultimo esempio della tua risposta> = 0.25 per gestire l'aggregazione di più colonne in questo modo sarebbe stato fantastico. df.groupby("kind").agg(**{ 'max height': pd.NamedAgg(column='height', aggfunc=max), 'min weight': pd.NamedAgg(column='weight', aggfunc=min) })
Onur Ece

6

Qualcosa come questo funzionerebbe:

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

2
No, non funziona. Se guardi la doc string perché aggregatedice esplicitamente che quando dictviene passato a, le chiavi devono essere nomi di colonna. Quindi o il tuo esempio è qualcosa che hai digitato senza controllare questo errore, oppure Pandas rompe i propri documenti qui.
ely

N / MI non ha visto la chiamata extra a returnslì dentro. Quindi questa è la versione in serie di aggregato? Sto cercando di fare la versione DataFrame di aggregate e voglio applicare diverse aggregazioni diverse a ciascuna colonna tutte in una volta.
ely

1
Prova questo: df.groupby ('dummy'). Agg ({'returns': {'func1': lambda x: x.sum (), 'func2': lambda x: x.mean ()}})
Chang Lei il

Fornisce un errore di asserzione senza messaggio. Dall'aspetto del codice (pandas.core.internals.py, righe 406-408, versione 0.7.3) sembra che faccia un controllo alla fine per assicurarsi che non restituisca più colonne di quante siano le chiavi nella prima livello del dizionario di aggregazione.
ely

Funziona bene su master. Vuoi provare ad aggiornare?
Chang She
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.