0Pricing
Pandas & NumPy Academy · Lezione

apply() con GroupBy

Passi a groupby().apply() una funzione che opera su più righe per calcolare riepiloghi complessi a livello di gruppo, non esprimibili con agg().

apply() con GroupBy è una lezione Pandas & NumPy Academy gratuita su CoddyKit. Questa è la lezione 2 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Pandas & NumPy Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Pandas & NumPy Academy include 4 lezioni in totale.

Perché GroupBy richiede apply()

Il metodo integrato agg() gestisce aggregazioni semplici come somma, media e conteggio, producendo un singolo valore scalare per gruppo. Tuttavia, alcuni calcoli a livello di gruppo richiedono di esaminare l'intero sotto-DataFrame del gruppo, non solo una singola colonna. groupby().apply(func) passa alla funzione l'intero DataFrame del gruppo e raccoglie i risultati, consentendo di creare riepiloghi complessi che agg() non è in grado di esprimere.

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'region': ['North', 'North', 'South', 'South', 'North'],
    'product': ['A', 'B', 'A', 'A', 'C'],
    'revenue': [100, 250, 180, 90, 300]
})
print(df)

Restituire uno scalare per gruppo

Quando la funzione passata a groupby().apply() restituisce uno scalare, il risultato è una Series indicizzata in base alle chiavi dei gruppi, identica a quella prodotta da agg(). Questa forma è utile quando lo scalare richiede una logica che coinvolge più colonne, ad esempio per calcolare il rapporto tra i ricavi del prodotto principale e i ricavi totali del gruppo, cosa che non può essere espressa in una singola specifica di colonna di agg().

def top_product_share(group):
    top = group['revenue'].max()
    total = group['revenue'].sum()
    return top / total

share = df.groupby('region').apply(top_product_share)
print(share)

Restituire una Series per gruppo

Quando la funzione restituisce una pd.Series, il risultato ha un MultiIndex: il livello esterno è la chiave del gruppo, mentre quello interno è l'indice della Series. È utile per calcolare più statistiche per gruppo con una sola chiamata a apply, producendo una tabella di riepilogo in cui ogni gruppo contiene più righe di metriche.

def group_stats(group):
    return pd.Series({
        'total': group['revenue'].sum(),
        'top_product': group.loc[group['revenue'].idxmax(), 'product'],
        'n_products': group['product'].nunique()
    })

result = df.groupby('region').apply(group_stats)
print(result)

Restituire un DataFrame per gruppo

Quando la funzione restituisce un pd.DataFrame, groupby().apply() concatena verticalmente tutti i sotto-DataFrame. Questa è la forma più potente: consente di filtrare o trasformare le righe all'interno di ogni gruppo e di restituire un sottoinsieme modificato. Ad esempio, è possibile mantenere solo i 2 prodotti con i ricavi più elevati all'interno di ogni area geografica.

def top2_per_region(group):
    return group.nlargest(2, 'revenue')

top2 = df.groupby('region').apply(top2_per_region)
print(top2.reset_index(drop=True))

Calcolare gli Z-score all'interno dei gruppi

Un uso comune di groupby().apply() consiste nel calcolare la standardizzazione all'interno dei gruppi. Invece di normalizzare i ricavi rispetto a tutti gli ordini, mescolando così le diverse aree geografiche, calcoli lo Z-score dei ricavi all'interno di ciascuna area. Uno Z-score pari a 2 nel Nord significa "2 deviazioni standard sopra la media del Nord": un riferimento più significativo rispetto a 2 deviazioni standard sopra la media globale.

def within_group_zscore(group):
    mean = group['revenue'].mean()
    std = group['revenue'].std()
    group = group.copy()
    group['revenue_z'] = (group['revenue'] - mean) / std
    return group

df_z = df.groupby('region').apply(within_group_zscore).reset_index(drop=True)
print(df_z)

Aggregazione personalizzata: media winsorizzata

La media winsorizzata limita i valori estremi prima di calcolare la media, risultando più robusta della media semplice per distribuzioni asimmetriche. Questa aggregazione personalizzata non può essere espressa con le funzioni standard di agg(), ma è semplice da realizzare con groupby().apply(): limiti i valori al 5° e al 95° percentile all'interno di ogni gruppo, quindi calcoli la media sui valori così limitati.

def winsorised_mean(group, lower_pct=0.05, upper_pct=0.95):
    col = group['revenue']
    lo = col.quantile(lower_pct)
    hi = col.quantile(upper_pct)
    clipped = col.clip(lo, hi)
    return clipped.mean()

wins_mean = df.groupby('region').apply(winsorised_mean)
print('Winsorised mean by region:')
print(wins_mean)

Finestra mobile personalizzata per gruppo

Le finestre mobili applicate all'intero DataFrame ignorano i confini dei gruppi. Se calcoli una media mobile su 3 righe di una serie temporale che alterna due prodotti, la finestra attraversa erroneamente i confini tra i prodotti. Applica la finestra mobile all'interno di un groupby().apply() per assicurarti che ogni calcolo rimanga entro il proprio gruppo, ad esempio per calcolare la media mobile dei ricavi su 3 mesi per ogni area geografica.

def group_rolling_mean(group, window=3):
    group = group.sort_values('order_date').copy()
    group['rolling_revenue'] = group['revenue'].rolling(window, min_periods=1).mean()
    return group

# df_ts has order_date column
# df_rolled = df_ts.groupby('region').apply(group_rolling_mean).reset_index(drop=True)
print('Rolling window per group applied inside apply()')

Parametro include_groups (Pandas 2.2+)

In Pandas 2.2 e versioni successive, groupby().apply() genera un FutureWarning se le chiavi del groupby compaiono nel DataFrame ricevuto dalla funzione. Passa include_groups=False per escludere le colonne usate per il groupby dal sotto-DataFrame passato alla funzione. In questo modo eviti sia l'avviso sia operazioni accidentali sulle colonne chiave all'interno del corpo della funzione.

def revenue_only(group):
    # group does not include 'region' column when include_groups=False
    return group['revenue'].sum()

# result = df.groupby('region').apply(revenue_only, include_groups=False)
print('include_groups=False avoids FutureWarning in Pandas 2.2+')

Combinare i risultati di apply() con reset_index

Quando groupby().apply() restituisce un DataFrame per gruppo, il risultato ha un MultiIndex che include la chiave del gruppo come livello esterno. Usa reset_index(drop=True) per riportare l'indice a un semplice intervallo di interi. In alternativa, usa reset_index(level=0) per trasformare la chiave del gruppo in una colonna, rendendola esplicita nell'output.

result = df.groupby('region').apply(top2_per_region)
print('With MultiIndex:')
print(result.head())

print('\nAfter reset_index:')
print(result.reset_index(drop=True))

Quando preferire transform() ad apply()

groupby().apply() serve per calcoli complessi a livello di gruppo che restituiscono una forma diversa da quella dell'input. groupby().transform() serve per calcoli che aggiungono una nuova colonna allineata all'indice originale, ad esempio riportando la media del gruppo in ogni riga per la normalizzazione. Usi transform quando il risultato deve avere la stessa forma del DataFrame originale; usi apply quando la forma del risultato è diversa.

# transform: adds group mean back to each row
df['group_mean_revenue'] = df.groupby('region')['revenue'].transform('mean')

# apply: computes one scalar per group
group_totals = df.groupby('region')['revenue'].apply(sum)

print(df[['region', 'revenue', 'group_mean_revenue']])

Suggerimento sulle prestazioni: eviti apply() per le aggregazioni semplici

groupby().apply() è significativamente più lento di groupby().agg() per le operazioni semplici, perché apply() crea un oggetto Python completo per ogni gruppo. Per somme, medie e conteggi, usi sempre agg(). Riservi apply() ai casi che richiedono effettivamente l'intero DataFrame del gruppo, come la logica condizionale su più colonne, le statistiche personalizzate o il filtraggio all'interno dei gruppi.

# SLOW: apply for simple sum
slow = df.groupby('region').apply(lambda g: g['revenue'].sum())

# FAST: native agg
fast = df.groupby('region')['revenue'].sum()

print('Both produce the same result:')
print(fast.equals(slow))

Verifica rapida

Verifichi la sua comprensione dei concetti di analisi dei dati presentati in questa lezione.

Riepilogo della lezione

In questa lezione ha imparato a: restituire scalari, Series e DataFrame da groupby().apply(), calcolare Z-score all'interno dei gruppi e statistiche robuste personalizzate e scegliere tra apply(), agg() e transform() per le operazioni a livello di gruppo. Prossimamente esploreremo map() e applymap() per le operazioni elemento per elemento su Series e DataFrame.

Domande Frequenti

La lezione «apply() con GroupBy» è gratuita?

Sì — il testo completo di «apply() con GroupBy» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Pandas & NumPy Academy, passa a CoddyKit PRO. Il corso Pandas & NumPy Academy include 4 lezioni in totale.

Cosa imparerò in «apply() con GroupBy»?

Passi a groupby().apply() una funzione che opera su più righe per calcolare riepiloghi complessi a livello di gruppo, non esprimibili con agg(). Eserciti Pandas & NumPy Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare Pandas & NumPy Academy?

Non è richiesta alcuna esperienza precedente. Pandas & NumPy Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 2 di 4.

Quanto tempo richiede la lezione «apply() con GroupBy»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione Pandas & NumPy Academy?

Sì. Ogni lezione Pandas & NumPy Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. apply() su colonne e righe
  2. apply() con GroupBy
  3. map() e applymap() per operazioni elemento per elemento
  4. Concatenare metodi con pipe()
← Torna a Pandas & NumPy Academy