Guardi i numeri di dicembre e sorridi: un altro record. Le vendite non stanno solo salendo, stanno esplodendo. Ma c’è una trappola nascosta in quella curva che punta verso l’alto, un dettaglio che sfugge a molti fogli Excel e che rischia di farti sbagliare completamente gli ordini per l’anno prossimo.
Quando il tuo business cresce, non cambia solo la quantità di merce che vendi; cambia il comportamento stesso dei tuoi dati.
Quella stagionalità che credevi fissa e prevedibile inizia a dilatarsi, a pulsare con più forza. Se tratti questi dati come un blocco unico, o peggio, se applichi le solite formule standard senza guardare sotto il cofano, ti ritroverai con previsioni che sembrano corrette sulla carta ma falliscono nella realtà.
Non serve un dottorato in statistica per evitare questo errore, ma serve occhio critico.
In questa guida non ci limitiamo a lanciare uno script Python: analizzeremo una serie storica per isolare il battito cardiaco del tuo business dal rumore di fondo.
L’obiettivo? Capire se la tua crescita è solida o se stai solo cavalcando un’onda che non hai ancora compreso del tutto.
Introduzione alla Decomposizione delle Serie Storiche
In analisi dei dati, una serie storica è spesso il risultato di diverse forze che agiscono contemporaneamente. La funzione seasonal_decompose agisce come un “prisma” che scompone la luce: prende un singolo segnale (le vendite, il traffico web, etc.) e lo divide nelle sue componenti fondamentali:
-
Trend: La direzione a lungo termine (crescita o decrescita).
-
Stagionalità: Modelli che si ripetono a intervalli regolari (es. ogni estate o ogni fine mese).
-
Residuo: Ciò che rimane dopo aver rimosso trend e stagionalità; rappresenta il rumore casuale o gli eventi imprevisti.
Il codice che segue simula una situazione tipica del business: un’attività che cresce nel tempo e la cui stagionalità diventa sempre più marcata man mano che il volume d’affari aumenta (modello moltiplicativo).
Ecco lo script completo e pronto all’uso:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.tsa.seasonal import seasonal_decompose
# 1. Generazione di dati sintetici
np.random.seed(42) # Per rendere i risultati riproducibili
time = np.arange(0, 120) # 10 anni di dati mensili (120 punti)
# Definiamo un trend lineare crescente
trend = 0.5 * time + 20
# Definiamo una stagionalità che dipende dal tempo (moltiplicativa)
# Notare come moltiplichiamo il seno per il trend per far crescere l'ampiezza
seasonal_pattern = 1 + 0.3 * np.sin(2 * np.pi * time / 12)
# Creazione della serie finale: Trend * Stagionalità + Rumore Bianco
data_mult = trend * seasonal_pattern + np.random.normal(0, 1.5, 120)
# Creazione del DataFrame con indice temporale mensile
df = pd.DataFrame({'Sales': data_mult},
index=pd.date_range(start='2020-01-01', periods=120, freq='ME'))
# 2. Decomposizione della serie storica
# Usiamo model='multiplicative' perché l'ampiezza stagionale cresce col trend
result = seasonal_decompose(df['Sales'], model='multiplicative')
# 3. Visualizzazione dei risultati
fig, axes = plt.subplots(4, 1, figsize=(10, 10), sharex=True)
# Serie Originale
df['Sales'].plot(ax=axes[0], color='blue', title='Serie Storica Originale (Dati Osservati)')
axes[0].set_ylabel('Valore')
# Componente Trend
result.trend.plot(ax=axes[1], color='red', title='Trend (Media Mobile)')
axes[1].set_ylabel('Trend')
# Componente Stagionale
result.seasonal.plot(ax=axes[2], color='green', title='Stagionalità (Fattore Moltiplicativo)')
axes[2].set_ylabel('Stagionalità')
# Residui (Rumore)
result.resid.plot(ax=axes[3], color='purple', title='Residui (Componente Irregolare)', linestyle='None', marker='o')
axes[3].set_ylabel('Residui')
plt.tight_layout()
plt.show()
-
Librerie: Usiamo
numpyper i calcoli,pandasper gestire i dati in tabelle temporali estatsmodels, che è lo standard per la statistica in Python. -
Time: Creiamo un asse temporale di 120 punti (pensa a 10 anni di rilevazioni mensili).
Costruzione del Fenomeno (Il “Motore”)
trend = 0.5 * time + 20 seasonal_pattern = 1 + 0.3 * np.sin(2 * np.pi * time / 12) data_mult = trend * seasonal_pattern + np.random.normal(0, 1.5, 120)
-
Qui stiamo “creando il mondo”. Definiamo un trend che sale costantemente.
-
La stagionalità è creata con una funzione seno: il moltiplicatore
0.3indica che nei picchi avremo un +30% rispetto al trend, e nei minimi un -30%. -
Moltiplicando
trend * seasonal_pattern, facciamo sì che le oscillazioni diventino più grandi graficamente man mano che il trend sale.
Creazione del DataFrame
df = pd.DataFrame({'Sales': data_mult},
index=pd.date_range(start='2020-01-01', periods=120, freq='ME'))
Trasformiamo i numeri in un oggetto pandas. Assegniamo delle date reali partendo dal 2020. Questo è fondamentale perché statsmodels ha bisogno di sapere che i dati hanno una cadenza temporale (mensile, in questo caso).
La Decomposizione
result = seasonal_decompose(df['Sales'], model='multiplicative')
Questa è la riga “magica”.
Diciamo all’algoritmo: “Prendi la colonna Sales e smontala ipotizzando che le componenti siano moltiplicate tra loro”.
L’algoritmo userà medie mobili per isolare il trend e calcolerà la media dei cicli per trovare la stagionalità.
Infine, generiamo 4 grafici sovrapposti per confrontare visivamente l’originale con le tre componenti estratte.
Se il grafico dei Residui non mostra forme particolari (sembra solo “nebbia” o puntini sparsi), significa che la nostra scomposizione ha avuto successo.
Vediamo ora come cambia l’analisi applicando un modello Additivo a questi stessi dati, per capire come riconoscere un errore di interpretazione:
Quando forziamo un modello Additivo su dati che hanno una natura Moltiplicativa, stiamo dicendo all’algoritmo: “Ignora il fatto che le oscillazioni crescono; sottrai sempre la stessa quantità fissa”.
Ecco il codice per fare il confronto e cosa dovresti osservare.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.tsa.seasonal import seasonal_decompose
# --- 1. Ripristiniamo i dati moltiplicativi (quelli con oscillazioni crescenti) ---
np.random.seed(42)
time = np.arange(0, 120)
trend = 0.5 * time + 20
seasonal_pattern = 1 + 0.3 * np.sin(2 * np.pi * time / 12)
data_mult = trend * seasonal_pattern + np.random.normal(0, 1.5, 120)
df = pd.DataFrame({'Sales': data_mult}, index=pd.date_range(start='2020-01-01', periods=120, freq='ME'))
# --- 2. Applichiamo ENTRAMBI i modelli ---
# Modello Corretto (Moltiplicativo)
res_mult = seasonal_decompose(df['Sales'], model='multiplicative')
# Modello Sbagliato (Additivo)
res_add = seasonal_decompose(df['Sales'], model='additive')
# --- 3. Visualizziamo i Residui a confronto ---
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8), sharex=True)
# Residui Moltiplicativi (Corretto)
ax1.scatter(df.index, res_mult.resid, color='green', alpha=0.6)
ax1.axhline(1, color='black', linestyle='--') # In mult. il centro è 1
ax1.set_title('Residui Modello CORRETTO (Moltiplicativo)')
ax1.set_ylabel('Rapporto')
# Residui Additivi (Sbagliato)
ax2.scatter(df.index, res_add.resid, color='red', alpha=0.6)
ax2.axhline(0, color='black', linestyle='--') # In add. il centro è 0
ax2.set_title('Residui Modello ERRATO (Additivo)')
ax2.set_ylabel('Differenza Assoluta')
plt.tight_layout()
plt.show()
Come “leggere” l’errore nei grafici
La differenza tra i due modelli si manifesta chiaramente nel grafico dei residui (la componente irregolare):
1. Il Segnale nei Residui (Modello Additivo/Errato) 🚩
Nel grafico rosso noterai un pattern a “ventaglio” o a “imbuto”:
-
All’inizio della serie, i residui sono piccoli.
-
Man mano che il trend sale, i residui diventano sempre più grandi e oscillanti.
-
Perché? Perché il modello additivo cerca di sottrarre una costante (es. 10 unità) quando la stagionalità reale è ormai arrivata a 30 unità. Quello scarto di 20 unità finisce “scartato” nei residui, che smettono di essere rumore casuale.
2. Il Rumore Bianco (Modello Moltiplicativo/Corretto) ✅
Nel grafico verde, i punti dovrebbero essere distribuiti in modo uniforme lungo tutta la linea temporale. Non c’è una crescita della varianza. Questo indica che il modello ha “estratto” con successo tutta l’informazione stagionale, lasciando dietro di sé solo puro rumore casuale.
Riepilogo Diagnostico
| Osservazione nei Residui | Diagnosi | Azione Correttiva |
| Ampiezza costante | Modello Additivo OK | Nessuna |
| Ampiezza che aumenta col tempo | Errore: Hai usato Additivo su dati Moltiplicativi | Passa a model='multiplicative' |
| Andamento ciclico residuo | Periodo stagionale errato | Controlla il parametro period |
Nota Tecnica:
seasonal_decompose è un metodo ingenuo (naive). È ottimo per capire i dati, ma per le previsioni vere e proprie esistono modelli più avanzati (come Holt-Winters o SARIMA).
💡 Casi Critici: Valori Zero o Negativi
C’è un limite tecnico importante: il modello moltiplicativo fallisce matematicamente se i dati contengono zeri o valori negativi.
Il problema: La decomposizione moltiplicativa implica divisioni (o logaritmi se si usa l’approccio alternativo). Dividere per zero è impossibile.
La soluzione
In questi casi si hanno due strade:
- Traslazione: Aggiungere una costante a tutta la serie per rendere tutti i valori positivi (es. [math]Sales + 1[/math]).
- Modello Additivo: Utilizzare comunque il modello additivo, accettando l’errore nei residui, oppure trasformare prima i dati con una radice quadrata o altre tecniche di stabilizzazione della varianza.
⚠️ 4 Errori che Rovinano le Previsioni (e come evitarli)
Anche con il codice corretto, è facile sbagliare l’interpretazione. Ecco le quattro trappole in cui cadono più spesso gli analisti (e come uscirne).
1. Confondere la Stagionalità Crescente con il Rumore
Quando l’ampiezza delle oscillazioni aumenta nel tempo, l’istinto ci dice che i dati stanno diventando “più disordinati” o rumorosi. Spesso è l’esatto opposto.
L’Errore: Pensare che la varianza crescente sia solo caos casuale.
La Verità: Se i picchi diventano sempre più alti e i minimi sempre più profondi in modo regolare, non è rumore: è struttura. È il segnale inequivocabile di una relazione moltiplicativa.
👉 Cosa fare: Se vedi un pattern a “imbuto” nei dati grezzi, passa subito al modello multiplicative.
2. Il “Default Mentale” del Modello Additivo
Chi inizia tende a usare il modello additivo per inerzia, perché è il più semplice da immaginare:
[math]Serie = Trend + Stagionalità + Rumore[/math]
L’Errore: Applicare questa formula su un business in forte crescita (o decrescita).
La Verità: L’additivo funziona solo quando l’ampiezza stagionale è costante (es. vendo sempre 100 unità in più a Natale, sia che fatturi 1.000 o 1.000.000).
👉 Cosa fare: Guarda il grafico storico. Le “onde” stagionali hanno sempre la stessa altezza visiva? Usa Additivo. Crescono? Usa Moltiplicativo.
3. Ignorare i Residui (Il vero test della verità)
Molti si fermano dopo aver visto i grafici di Trend e Stagionalità. Grave errore.
L’Errore: Non analizzare l’ultimo grafico, quello del “Resid” o “Residual”.
La Verità: I residui sono la tua diagnostica. Se mostrano onde, trend, imbuti o forme riconoscibili, il modello ha fallito nel catturare tutte le informazioni.
👉 Cosa fare: I residui devono sembrare “nebbia” o sintonizzazione tv assente: puntini sparsi senza logica apparente. Solo allora il modello è valido.
4. Il Crash dei Valori [math]\le 0[/math]
Il modello moltiplicativo ha un tallone d’Achille matematico: richiede dati strettamente positivi ([math]Serie > 0[/math]).
L’Errore: Tentare una decomposizione moltiplicativa su serie che contengono zeri (es. giorni senza vendite) o valori negativi (es. resi superiori alle vendite).
La Verità: Il codice andrà in errore o produrrà grafici privi di senso (artefatti).
👉 Cosa fare: Prima di decomporre, controlla i minimi. Se hai degli zeri, aggiungi una piccola costante a tutta la serie (es. [math]+1[/math] o [math]+0.01[/math]) per evitare la divisione per zero.
Riepilogo Diagnostico Finale
| Caratteristica | Modello Additivo | Modello Moltiplicativo |
|---|---|---|
| Equazione | [math]Y_t = T_t + S_t + R_t[/math] | [math]Y_t = T_t \times S_t \times R_t[/math] |
| Stagionalità | Costante (es. sempre [math]+100[/math] unità) | Proporzionale (es. sempre [math]+10\%[/math]) |
| Residui OK se… | L’ampiezza delle fluttuazioni è costante | L’ampiezza delle fluttuazioni cresce col trend |
| Limiti | Nessuno sui valori | Non supporta valori [math]\leq 0[/math] |
Questo esercizio è particolarmente efficace perché non si limita a mostrare come far girare il codice, ma insegna a dubitare del risultato.
-
L’importanza del “Stress Test”: Forzare il modello additivo su dati moltiplicativi è un momento “Aha!” per lo studente. Mostra visivamente che il residuo non è solo “scarto”, ma contiene informazioni preziose che il modello non è riuscito a catturare.
-
Il Pattern a Imbuto: L’esercizio evidenzia il cosiddetto effetto heteroscedasticity (eteroschedasticità) nei residui del modello errato. In ambito professionale, identificare questo “ventaglio” nei grafici è una competenza fondamentale per chi si occupa di risk management o logistica.
-
Il Vincolo Matematico: Citare il limite dei valori ≤ 0 prepara l’analista al mondo reale, dove spesso i dati sono sporchi o presentano ritorni/storni (valori negativi) che bloccherebbero un software meno flessibile.
Esercizio:
Domanda: Come decideresti se usare un modello additivo o multiplicativo per la decomposizione?
Risposta: La scelta dipende dalla relazione tra le componenti:
-
Modello additivo (
Osservazione = Trend + Stagionalità + Residuo):-
Usare quando l’ampiezza della stagionalità è costante nel tempo
-
Quando il trend è lineare o quasi lineare
-
Esempio: vendite che oscillano di ±100 unità indipendentemente dal livello
-
-
Modello moltiplicativo (
Osservazione = Trend × Stagionalità × Residuo):-
Usare quando l’ampiezza della stagionalità è proporzionale al livello del trend
-
Quando la serie mostra crescita esponenziale o varianza crescente
-
Esempio: vendite che oscillano del ±10% rispetto al livello corrente
-
Test pratico: Plot della serie. Se le fluttuazioni stagionali crescono con il trend → multiplicativo. Se rimangono costanti in valore assoluto → additivo.
Immagina di essere il manager della logistica. Se usi un modello additivo quando le vendite sono moltiplicative, ordinerai troppa poca merce a Natale (stock-out) e troppa a Ferragosto (costi di magazzino). L’errore matematico diventa una perdita economica.
Serie Storiche
👉 Serie storiche: cos’è il megatrend?
👉 Serie storiche: tecniche previsive e approccio grafico
👉Python e pandas: operazioni sulle Series
👉 Analisi di serie temporali con Python: guida pratica da zero a pandas
👉 Padroneggiare le serie temporali con pandas: datetime, aggregazioni e pivot
👉 Introduzione al modello ARIMA per l’analisi delle serie temporali
👉 Guida alla decomposizione STL di serie temporali con Python & statsmodels





