pandas offre funzioni di aggregazione pronte all'uso (come sum(), mean(), max()) ma a volte il calcolo che ci serve non esiste tra le opzioni standard.

In questi casi possiamo scrivere la nostra funzione Python e applicarla direttamente al dataframe con agg().

In questa micro-lezione vedremo come fare passo dopo passo.

La libreria e il dataframe

Per prima cosa importiamo le librerie:

import pandas as pd
import numpy as np

Adesso creiamo un dataframe di esempio.

Immagina di gestire i dati di un team di vendita: ogni riga è una vendita, con il nome del venditore, la regione e l'importo.

df = pd.DataFrame({
    'venditore': ['Anna', 'Anna', 'Anna', 'Marco', 'Marco', 'Giulia', 'Giulia', 'Giulia'],
    'regione':   ['Nord', 'Nord', 'Sud',  'Nord',  'Sud',   'Sud',   'Sud',   'Nord'],
    'importo':   [1200,   850,    2100,   3400,    980,     760,     1900,    1100]
})

Ecco l’aspetto del nostro dataframe:

venditore

regione

importo

Anna

Nord

1200

Anna

Nord

850

Anna

Sud

2100

Marco

Nord

3400

Marco

Sud

980

Giulia

Sud

760

Giulia

Sud

1900

Giulia

Nord

1100

Come funziona agg()

agg() è il metodo che pandas usa per applicare funzioni di aggregazione a un gruppo di righe.

Di solito lo vedi così:

(df
 .groupby('venditore', as_index=False)['importo']
 .agg('sum')
)

In poche parole, stai dicendo a pandas: prendi la colonna importo, applica la funzione sum e raggruppa per venditore.

Ma al posto di sum puoi usare qualsiasi funzione, compresa una che hai scritto tu.

Se non hai familiarità con groupby(), puoi consultare questa micro-lezione.

Scrivere una funzione di aggregazione personalizzata

Una funzione di aggregazione personalizzata riceve in input una Series e restituisce un singolo valore.

Supponiamo di voler calcolare il range delle vendite per ogni venditore, cioè la differenza tra la vendita più alta e quella più bassa.

Non esiste una funzione standard per questo. Perciò, dobbiamo crearne una:

def range_vendite(s):
    return s.max() - s.min()

La funzione riceve s, che è una Series e restituisce la differenza tra il valore massimo e il valore minimo minimo.

La funzione di per sé non è legata al nostro dataframe, ma è generica.

Nel prossimo passaggio vedremo come applicarla al nostro dataframe.

Attenzione!

La funzione deve sempre restituire un singolo valore scalare: un numero, una stringa, un booleano.

Se restituisce una lista o una Series, pandas non saprà come gestirla nell'output finale.

Applicarla con groupby() e agg()

Una volta definita la funzione, puoi passarla quando usi agg() come se fosse una funzione normale:

(df
 .groupby('venditore', as_index=False)['importo']
 .agg(range_vendite)
)

Ed ecco qui il risultato:

venditore

importo

Anna

1250

Giulia

1140

Marco

2420

In questo caso la colonna importo contiene il risultato della nostra funzione di aggregazione personalizzata, ovvero il range delle vendite.

Anna ha vendite tra 850€ e 2100€: il range è 1250€. Marco ha vendite tra 980€ e 3400€: range di 2420€: è il più variabile del team. Giulia è la più costante, con un range di 1140€.

Questo tipo di informazione, la variabilità delle performance, non si ottiene con nessuna delle funzioni standard di pandas.

Usare più aggregazioni insieme

Puoi combinare funzioni standard e funzioni personalizzate in un unico utilizzo di agg(), usando un dizionario.

In questo modo puoi anche dare un nome specifico alle colonne delle aggregazioni.

Ecco come fare:

(df.groupby('venditore', as_index=False)['importo']
 .agg(
    totale='sum',
    media='mean',
    variabilità=range_vendite
 )
)

Ed ecco qui il risultato:

venditore

totale

media

variabilità

Anna

4150

1383.3

1250

Giulia

3760

1253.3

1140

Marco

4380

2190.0

2420

Puoi anche scrivere funzioni più elaborate. Per esempio, una che calcola la percentuale di vendite sopra una certa soglia:

def pct_sopra_soglia(s, soglia=1000):
    return round((s.gt(soglia).sum() / len(s)) * 100, 1)

Ed ecco come applicarla al dataframe:

(df.
 groupby('venditore', as_index=False)['importo']
 .agg(
     totale='sum',
     pct_grandi=pct_sopra_soglia
)

Ed ecco qui il risultato:

venditore

totale

pct_grandi

Anna

4150

66.7

Giulia

3760

50.0

Marco

4380

50.0

Anna ha il 66.7% delle sue vendite sopra i 1000€: due su tre. Marco e Giulia sono al 50%.

Conclusione

Le aggregazioni personalizzate sono uno degli strumenti più potenti di pandas proprio perché non ti limitano alle funzioni predefinite.

Basta scrivere una funzione Python che riceve una Series e restituisce un valore e tutto il resto lo gestisce agg().

Con questa tecnica puoi calcolare metriche specifiche o qualsiasi altra statistica che ha senso per il tuo contesto.

Alla prossima micro-lezione 👋

Se vuoi imparare la data analysis con micro-lezioni che vanno dritto al sodo, iscriviti alla newsletter: