Pandas & NumPy Academy · Lezione

Finestre espandibili

Calcoli statistiche cumulative che includono tutte le righe dall'inizio fino a ogni punto usando expanding().sum().

Lezione 2 di 413 passaggi

Finestre espandibili è 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.

Che cos'è una finestra espandibile?

Una finestra espandibile include tutte le righe dall'inizio della Series fino alla riga corrente: la finestra cresce a ogni nuova riga invece di rimanere fissa. Alla riga 0 contiene un solo valore; alla riga 5 ne contiene sei; alla riga 100 ne contiene 101. Le finestre espandibili vengono usate per calcolare statistiche cumulative che rappresentano il totale progressivo, la media progressiva o il massimo progressivo dall'inizio del dataset fino al momento corrente.

import pandas as pd
import numpy as np

sales = pd.Series(
    [100, 150, 120, 200, 180, 160, 220, 190],
    index=pd.date_range('2024-01-01', periods=8)
)

# Expanding mean — grows with each row
df = pd.DataFrame({'sales': sales})
df['cumulative_mean'] = df['sales'].expanding().mean()
print(df)

Il metodo expanding()

Series.expanding(min_periods=1) restituisce un oggetto Expanding. Il parametro min_periods (predefinito a 1) imposta il numero minimo di osservazioni richieste per ottenere il primo risultato non-NaN. A differenza di rolling(n), la finestra di expanding() non è fissa: inizia sempre dall'inizio. Può concatenare qualsiasi aggregazione: .sum(), .mean(), .std(), .min(), .max().

import pandas as pd
import numpy as np

np.random.seed(42)
monthly_revenue = pd.Series(
    np.random.randint(500, 1500, 12),
    index=pd.date_range('2024-01', periods=12, freq='ME'),
    name='revenue'
)

df = pd.DataFrame({'revenue': monthly_revenue})
df['cumsum'] = df['revenue'].expanding().sum()
df['cummean'] = df['revenue'].expanding().mean()
df['cummax'] = df['revenue'].expanding().max()
df['cummin'] = df['revenue'].expanding().min()
print(df)

Pandas cumsum, cumprod, cummax, cummin

Per le statistiche cumulative più comuni, Pandas fornisce metodi diretti su Series e DataFrame senza dover usare expanding(): cumsum(), cumprod(), cummax() e cummin(). Si tratta di implementazioni ottimizzate, leggermente più veloci delle equivalenti con expanding(). Usi queste scorciatoie per metriche aziendali tipiche, come i ricavi da inizio anno (cumsum) o il prezzo massimo storico (cummax).

import pandas as pd
import numpy as np

prices = pd.Series(
    [100, 95, 110, 105, 120, 115, 130, 125],
    index=pd.date_range('2024-01-01', periods=8)
)

df = pd.DataFrame({'price': prices})
df['cummax'] = df['price'].cummax()    # all-time high
df['cummin'] = df['price'].cummin()    # all-time low
df['cumsum'] = df['price'].cumsum()    # cumulative total
df['cumprod'] = (1 + df['price'].pct_change()).cumprod()  # growth index
print(df.round(3))

Calcolo da inizio anno con expanding

Un impiego classico delle finestre espandibili è il calcolo dei ricavi da inizio anno (YTD). Per ogni giorno, si desidera sommare tutti i ricavi dal 1º gennaio dell'anno corrente fino a quel giorno. Si tratta semplicemente di applicare cumsum() all'interno di ciascun gruppo annuale. Lo implementi con groupby(year).cumsum(): il cumsum viene azzerato all'inizio di ogni anno solare e continua ad accumularsi nel corso dell'anno.

import pandas as pd
import numpy as np

np.random.seed(0)
dates = pd.date_range('2023-01-01', '2024-06-30', freq='ME')
daily = pd.DataFrame({
    'date': dates,
    'revenue': np.random.randint(100, 500, len(dates))
})

# Year-to-date cumulative revenue
daily['year'] = daily['date'].dt.year
daily['ytd_revenue'] = daily.groupby('year')['revenue'].cumsum()
daily = daily.drop(columns='year')
print(daily.tail(10).to_string(index=False))

Deviazione standard con finestra espandibile

expanding().std() calcola la deviazione standard progressiva usando tutti i dati dall'inizio fino alla riga corrente. È utile per monitorare se la variabilità di una metrica aumenta o diminuisce nel tempo, ad esempio per verificare se le vendite giornaliere di un prodotto diventano più o meno prevedibili con la maturazione dell'attività. A differenza della deviazione standard mobile, che riflette solo la variabilità recente, la deviazione standard espandibile considera l'intera dispersione storica.

import pandas as pd
import numpy as np

np.random.seed(42)
sales = pd.Series(
    np.concatenate([
        np.random.normal(100, 5, 30),   # stable period
        np.random.normal(100, 25, 30)   # volatile period
    ]),
    index=pd.date_range('2024-01-01', periods=60)
)

df = pd.DataFrame({'sales': sales})
df['expanding_std'] = df['sales'].expanding().std()
df['rolling_30d_std'] = df['sales'].rolling(30).std()
print(df.tail(10).round(2))

Confronto tra rolling ed expanding

La distinzione fondamentale è la seguente: le finestre mobili riflettono le prestazioni recenti (gli ultimi n giorni) e non risentono dei dati molto lontani nel tempo, mentre le finestre espandibili includono tutta la cronologia e quindi convergono lentamente verso valori stabili. Usi rolling quando Le interessano le tendenze recenti (una media mobile su 7 giorni risponde ai cambiamenti recenti più rapidamente di una su 90 giorni). Usi expanding quando desidera statistiche storiche complessive o metriche YTD che non devono mai dimenticare i dati precedenti.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

np.random.seed(10)
data = pd.Series(
    np.random.randn(60).cumsum() + 50,
    index=pd.date_range('2024-01-01', periods=60)
)

df = pd.DataFrame({'value': data})
df['rolling_10'] = data.rolling(10).mean()
df['expanding'] = data.expanding().mean()

df.plot(figsize=(12, 5), title='Rolling 10-day Mean vs. Expanding Mean')
plt.ylabel('Value')
plt.show()

Expanding apply per statistiche cumulative personalizzate

expanding().apply(func) applica una funzione personalizzata a tutti i dati dall'inizio fino alla riga corrente, proprio come rolling().apply(). Ciò consente di calcolare statistiche cumulative non disponibili tra quelle integrate, ad esempio il coefficiente di variazione cumulativo (std/mean), la mediana progressiva o un indice di Sharpe cumulativo. La funzione riceve un array NumPy contenente tutti i valori osservati fino a quel momento e deve restituire uno scalare.

import pandas as pd
import numpy as np

np.random.seed(0)
returns = pd.Series(
    np.random.normal(0.001, 0.02, 60),
    index=pd.date_range('2024-01-01', periods=60)
)

# Cumulative Sharpe ratio (mean/std of returns)
def sharpe(arr):
    if len(arr) < 2:
        return float('nan')
    return arr.mean() / arr.std() * (252 ** 0.5)  # annualised

df = pd.DataFrame({'returns': returns})
df['cum_sharpe'] = df['returns'].expanding().apply(sharpe, raw=True)
print(df.tail(8).round(4))

Finestre espandibili e gestione dei NaN

Per impostazione predefinita, le finestre espandibili ignorano i valori NaN: un NaN al centro della Series viene ignorato nel calcolo della somma o della media cumulativa. Può verificarlo con skipna=True (il valore predefinito nella maggior parte delle aggregazioni Pandas). Quando lavora con serie temporali che contengono valori mancanti effettivi (ad esempio, nessuna contrattazione nei fine settimana), le statistiche espandibili vengono calcolate solo sui valori non-NaN osservati fino a quel momento, che è generalmente il comportamento desiderato.

import pandas as pd
import numpy as np

data = pd.Series([10, np.nan, 20, 30, np.nan, 40, 50])

# cumsum skips NaN by default
df = pd.DataFrame({'value': data})
df['cumsum'] = df['value'].cumsum()      # NaN propagates in cumsum!
df['expanding_sum'] = df['value'].expanding().sum()  # NaN skipped

print(df)
print('\nNote: cumsum propagates NaN; expanding().sum() skips it.')

Finestre espandibili per benchmark progressivi

Le finestre espandibili sono ideali per calcolare benchmark progressivi: il massimo storico, il miglior trimestre di sempre o il tasso di errore più basso dall'avvio. Questi KPI richiedono di conservare tutti i valori storici, mentre una finestra mobile dimenticherebbe i record più vecchi. L'uso di cummax() o cummin() restituisce il valore migliore (o peggiore) progressivo in ogni momento, rendendo semplice monitorare quando vengono stabiliti nuovi record.

import pandas as pd
import numpy as np

np.random.seed(5)
sales = pd.Series(
    np.random.randint(100, 300, 20),
    index=pd.date_range('2024-01-01', periods=20),
    name='daily_sales'
)

df = pd.DataFrame({'sales': sales})
df['all_time_max'] = df['sales'].cummax()
df['new_record'] = df['sales'] == df['all_time_max']

print('Days where a new sales record was set:')
print(df[df['new_record']].drop(columns='new_record'))

Esempio pratico: rendimenti cumulativi

In finanza, i rendimenti cumulativi mostrano quanto è cresciuto un investimento dalla data iniziale a ciascuna data successiva. A partire dai rendimenti percentuali giornalieri, il prodotto cumulativo di (1 + daily_return) fornisce il fattore di crescita totale. In questo caso, un prodotto espandibile è più naturale di un prodotto mobile: si desidera monitorare la crescita totale dall'inizio dell'investimento, non solo quella degli ultimi n giorni.

import pandas as pd
import numpy as np

np.random.seed(7)
start_price = 100
daily_returns = pd.Series(
    np.random.normal(0.0005, 0.015, 252),
    index=pd.date_range('2024-01-01', periods=252)
)

# Cumulative return = product of (1 + r_i)
cum_returns = (1 + daily_returns).cumprod()
portfolio_value = start_price * cum_returns

print(f'Start value: ${start_price:.2f}')
print(f'End value:   ${portfolio_value.iloc[-1]:.2f}')
print(f'Total return: {(portfolio_value.iloc[-1]/start_price - 1)*100:.1f}%')

Quando usare expanding, rolling o cumsum

Linee guida per scegliere l'approccio corretto:

  • Usi cumsum() / cummax() / cummin() per semplici aggregazioni cumulative: sono l'opzione più veloce.
  • Usi expanding().mean() / std() quando ha bisogno di medie o varianze cumulative progressive.
  • Usi expanding().apply(custom_func) per statistiche storiche complesse non disponibili tra quelle integrate.
  • Usi rolling(n) invece di expanding quando solo la cronologia recente deve influenzare il valore corrente.

Verifica rapida

Verifichi la Sua comprensione delle finestre espandibili illustrate in questa lezione.

Riepilogo della lezione

In questa lezione ha imparato che expanding() calcola statistiche cumulative su una finestra crescente dall'inizio della Series, cumsum/cummax/cummin sono scorciatoie ottimizzate per le operazioni cumulative più comuni e le finestre espandibili sono ideali per record storici, metriche YTD e rendimenti cumulativi degli investimenti. Ora esamineremo le medie mobili ponderate esponenzialmente (EWMA), che attribuiscono un peso maggiore alle osservazioni recenti.

Gratis per iniziare

Impara Python con un tutor IA — gratis

Scrivi ed esegui vero codice nel tuo browser, ricevi aiuto istantaneo da un tutor IA disponibile 24/7, e riprendi da dove hai lasciato sul web o nell'app.

Corsi
30
Lezioni
120

Domande Frequenti

La lezione «Finestre espandibili» è gratuita?

Sì — il testo completo di «Finestre espandibili» è 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 «Finestre espandibili»?

Calcoli statistiche cumulative che includono tutte le righe dall'inizio fino a ogni punto usando expanding().sum(). 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 «Finestre espandibili»?

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. Finestre mobili
  2. Finestre espandibili
  3. Media mobile ponderata esponenzialmente
  4. Ranghi e percentili all'interno dei gruppi
← Torna a Pandas & NumPy Academy