Misure di Posizione: Five-Number Summary, Outlier e Boxplot
Parte (2/2)
Potrebbe interessarti:
- Trasformazione delle Variabili: Standardizzazione Z-Score - (1/3)
- Misure di Posizione: Percentili, Quartili e Intervallo Interquartile - (1/2)
- Misure di Variabilità: Intervallo, Varianza e Deviazione Standard - (1/2)
Il progetto Python, che comprende gli script mostrati in questo post, e quelli relativi alla creazione dei grafici Matplotlib, è disponibile assieme al corso Fondamenti di Statistica per il ML.
Il Riassunto a Cinque Numeri (Five-Number Summary)
A partire dai quartili, è possibile ottenere informazioni importanti sia sulla posizione centrale sia sulla variabilità dei dati. In particolare:
- il secondo quartile $Q2$ (mediana) rappresenta una misura di posizione centrale;
- le differenze di $Q1$ e $Q3$ con la mediana $Q2$ descrivono la dispersione rispettivamente del secondo e del terzo quarto dei dati, cioè della parte centrale della distribuzione.
Tuttavia, i quartili, da soli, non forniscono informazioni sulla variabilità nelle code della distribuzione. Per completare l’analisi è quindi necessario considerare anche il valore minimo (Min) e il valore massimo (Max) del dataset. In questo modo è possibile misurare la dispersione:
- nel primo quarto dei dati: $(Q1 - \text{Min})$;
- nell’ultimo quarto dei dati: $(\text{Max} - Q2)$.
Il riassunto a cinque numeri (Five-Number Summary) di un dataset è quindi costituito dai seguenti valori:
$\text{Min},\; Q_1,\; Q_2,\; Q_3,\; \text{Max}$

Essi stanno alla base della costruzione del boxplot, uno degli strumenti grafici più utilizzati per rappresentare in modo immediato la distribuzione dei dati, la variabilità e la presenza di eventuali outlier.
In Python possiamo calcolare il Five-Number Summary in questo modo:
import numpy as np
rng = np.random.default_rng(seed=42)
# Generazione di una distribuzione empirica approssimativamente gaussiana
n = 5_000
data = rng.normal(loc=100, scale=15, size=n)
# Calcolo dei quartili e dell'intervallo interquartile
Q1, Q2, Q3 = np.quantile(data, [0.25, 0.50, 0.75])
min = np.min(data)
max = np.max(data)
print("Riassunto a 5 numeri:")
print(f"Min: {min:.2f}")
print(f"Max: {max:.2f}")
print(f"Q1: {Q1:.2f}")
print(f"Q2: {Q2:.2f}")
print(f"Q3: {Q3:.2f}")
L’output è:
Min: 45.27
Max: 151.81
Q1: 89.62
Q2: 99.94
Q3: 109.47
Identificazione degli Outlier
Nell’analisi dei dati, l’identificazione degli outlier – cioè osservazioni che si discostano nettamente dal comportamento generale del dataset – è un passaggio importante. Questi valori richiedono sempre un’attenzione particolare, poiché possono avere origini diverse:
- errori di misura o di registrazione;
- osservazioni provenienti da popolazioni differenti;
- valori estremi ma reali, che rappresentano fenomeni rari ma possibili.
Ad esempio:

E’ importante sottolineare che:
non tutte le osservazioni estreme sono outlier:
in alcuni casi possono semplicemente riflettere una distribuzione asimmetrica (skewed), senza essere anomalie.gli outlier non sono necessariamente “errori”:
in molti contesti (finanza, medicina, controllo qualità) possono contenere informazioni preziose e indicare fenomeni rilevanti. Per questo motivo, la loro gestione deve essere sempre guidata da considerazioni statistiche e dal contesto applicativo.
Un esempio classico è la distribuzione della ricchezza individuale: patrimoni estremamente elevati (come quello di grandi imprenditori) risultano fortemente distanti dalla maggior parte dei valori e possono essere considerati outlier, pur essendo perfettamente validi.
Se l’outlier è chiaramente dovuto a un errore (di misura o di inserimento), può essere rimosso. Se invece non esiste una spiegazione evidente, la decisione se mantenerlo o meno richiede cautela, poiché la sua presenza può influenzare significativamente alcune misure statistiche (come la media e la deviazione standard).
Identificazione tramite Quartili e IQR
Uno dei metodi più utilizzati per individuare potenziali outlier si basa sui quartili e sull’intervallo interquartile (IQR):
$$ IQR = Q_3 - Q_1 $$A partire da questi, si definiscono i cosiddetti limiti:
$$ \begin{align*} \text{Lower limit} & = Q_1 - 1.5 \cdot IQR\\ \text{Upper limit} & = Q_3 + 1.5 \cdot IQR\\ \end{align*} $$Le osservazioni che sono inferiori al limite inferiore, oppure superano il limite superiore vengono considerate potenziali outlier.
Consideriamo un dataset in cui $Q1 = 46.76$ e $Q3 = 50.13$. L’intervallo interquartile risulta:
$$ IQR = Q_3 - Q_1 = 53.24 - 46.76 = 6.48 $$A partire da questo valore, possiamo determinare i limiti per l’individuazione dei potenziali outlier:
$$ \begin{align*} \text{Lower limit} & = 46.76 - 1.5 \cdot 6.48 =37.04\\ \text{Upper limit} & = 53.24 + 1.5 \cdot 6.48= 62.96 \end{align*} $$Di conseguenza:
- tutte le osservazioni inferiori a $37.04$ sono considerate potenziali outlier inferiori;
- tutte le osservazioni superiori a $62.96$ sono considerate potenziali outlier superiori:

In Python:
import numpy as np
# Seed per riproducibilità
np.random.seed(42)
# Dati principali
n = 1000
data = np.random.normal(loc=50, scale=5, size=n)
# Outlier aggiunti
outliers = np.array([10, 15, 90, 95])
# Dataset completo
data_with_outliers = np.concatenate([data, outliers])
# Calcolo dei quartili e dell'intervallo interquartile
Q1, Q2, Q3 = np.quantile(data, [0.25, 0.50, 0.75])
IQR = Q3 - Q1
# Calcolo dei limiti
lower_limit = Q1 - 1.5 * IQR
upper_limit = Q3 + 1.5 * IQR
print(f"Q1: {Q1:.2f}")
print(f"Q2: {Q2:.2f}")
print(f"Q3: {Q3:.2f}")
print(f"IQR: {IQR:.2f}")
print(f"Lower limit: {lower_limit:.2f}")
print(f"Upper limit: {upper_limit:.2f}")
L’output è:
Q1: 46.76
Q2: 50.13
Q3: 53.24
IQR: 6.48
Lower limit: 37.05
Upper limit: 62.96
Effetto degli Outlier sulle Misure Statistiche
Quando un dataset contiene outlier o presenta una distribuzione asimmetrica (skewed), la scelta delle misure statistiche diventa particolarmente importante. In questi casi:
- la mediana rappresenta una misura di posizione più robusta rispetto alla media;
- l’intervallo interquartile (IQR) è una misura di dispersione più affidabile rispetto alla deviazione standard.
Questo accade perché media e deviazione standard utilizzano tutte le osservazioni, inclusi gli outlier, risultando quindi fortemente influenzate da valori estremi. Al contrario, mediana e IQR sono misure robuste, poiché si basano sulla posizione relativa dei dati e non risentono significativamente della presenza di valori anomali.
Il grafico sottostante confronta due distribuzioni: la prima senza outlier e la seconda con tre valori anomali. Nella seconda distribuzione possiamo osservare come gli outlier spostino la media verso la coda sinistra e aumentino la deviazione standard. Di conseguenza, l’intervallo $\mu \pm \sigma$, che rappresenta la dispersione dei dati intorno alla media, risulta sensibilmente più ampio:

In Python:
import numpy as np
# Dataset #1 e #2 con alcuni outlier
data1 = np.array([46, 47, 48, 48, 49, 49, 50, 50, 50, 51, 51, 52, 52, 53, 54])
data2 = np.concatenate((data1, [75, 85, 95]))
titles = ("Media, Mediana, Deviazione Standard e IQR",
"Effetto degli Outlier su Media, Mediana, Deviazione Standard e IQR")
for i, (data, title) in enumerate(zip((data1, data2), titles)):
# Calcolo delle statistiche
mean = np.mean(data)
median = np.median(data)
std = np.std(data)
q1 = np.percentile(data, 25)
q3 = np.percentile(data, 75)
iqr = q3 - q1
print(f"[{title}]")
print(f"Media: {mean:.1f}")
print(f"Mediana: {median:.1f}")
print(f"Deviazione standard: {std:.1f}")
print(f"Q1: {q1:.1f}")
print(f"Q3: {q3:.1f}")
print(f"IQR: {iqr:.1f}\n")
L’output è:
[Media, Mediana, Deviazione Standard e IQR]
Media: 50.0
Mediana: 50.0
Deviazione standard: 2.2
Q1: 48.5
Q3: 51.5
IQR: 3.0
[Effetto degli Outlier su Media, Mediana, Deviazione Standard e IQR]
Media: 55.8
Mediana: 50.5
Deviazione standard: 13.6
Q1: 49.0
Q3: 52.8
IQR: 3.8
In sintesi, se i dati sono asimmetrici o contengono outlier:
- usiamo la mediana come misura di posizione;
- usiamo l’IQR come misura di dispersione;
Altrimenti, se i dati sono approssimativamente simmetrici e privi di outlier:
- la media è una buona misura di posizione;
- la deviazione standard è una misura adeguata della variabilità.
Il Grafico Boxplot
Il boxplot (box-and-whisker plot) è una rappresentazione grafica basata sul riassunto a cinque numeri e consente di visualizzare in modo immediato:
- la posizione centrale dei dati;
- la dispersione;
- la forma della distribuzione (simmetria o asimmetria);
- la presenza di outlier.
Per costruire un boxplot è utile introdurre il concetto di valori adiacenti, che sono le osservazioni più estreme che rimangono all’interno dei limiti definiti tramite IQR; cioè i valori più lontani che non sono considerati outlier. Se il dataset non contiene outlier, i valori adiacenti coincidono semplicemente con il valore minimo e massimo del dataset.
La costruzione di un boxplot avviene attraverso i seguenti passaggi:
- Calcoliamo i quartili.
- Individuaiamo i valori adiacenti e i potenziali outlier.
- Tracciamo un asse orizzontale su cui poter riportare i numeri ottenuti nei passaggi (1) e (2). Sopra questo asse, segnamo i quartili e i valori adiacenti con delle linee verticali.
- Uniamo i quartili per formare un rettangolo, quindi colleghiamo il rettangolo ai valori adiacenti con delle linee.
- Tracciamo una linea all’interno del box in corrispondenza della mediana .
- Segnamo ogni potenziale valore anomalo con un asterisco.
Ad esempio:

Il boxplot fornisce numerose informazioni a colpo d’occhio:
- la linea all’interno del box rappresenta la mediana;
- gli estremi del box corrispondono a $Q1$ e $Q2$;
- la lunghezza del box rappresenta l’intervallo interquartile (IQR);
- i baffi si estendono fino ai valori adiacenti;
- eventuali punti al di fuori dei baffi indicano degli outlier.
A differenza di altri grafici (come gli istogrammi), il boxplot non mostra la forma dettagliata della distribuzione, ma offre una sintesi compatta e robusta. Per questo motivo, è spesso utilizzato insieme ad altre rappresentazioni grafiche per un’analisi più completa dei dati.
In Python, tramite Matplotlib, possiamo disegnare un boxplot mediante la funzione Axes.boxplot:
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(12, 5))
# ...
# Creazione boxplot
ax.boxplot(data, vert=False, showfliers=False)
# Annotazioni quartili e mediana
q13_y, q2_y = 1.12, 0.84
ax.text(Q1, q13_y, f"Q1", ha='left')
ax.text(Q2, q2_y, f"Q2", ha='left')
ax.text(Q3 - 0.4, q13_y, f"Q3", ha='left')
# Valori adiacenti e annotazioni
IQR = Q3 - Q1
lower_limit = Q1 - 1.5 * IQR
upper_limit = Q3 + 1.5 * IQR
adjacent_lower = np.min(data[data >= lower_limit])
adjacent_upper = np.max(data[data <= upper_limit])
ax.text(adjacent_lower, 0.87, f"Val adiac inf", ha='center')
ax.text(adjacent_upper, 0.87, f"Val adiac sup", ha='center')