Misure di Variabilità: Intervallo, Varianza e Deviazione Standard
Parte (1/2)
Potrebbe interessarti:
- Misure di Variabilità: il Teorema di Chebyshev e la Regola Empirica - (2/2)
- Misure di Tendenza Centrale: Media, Mediana e Moda
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.
La variabilità (chiamata anche diffusione o dispersione) indica quanto i dati sono distanti da un valore centrale (come la media o la mediana). Può essere considerata come una misura di dispersione, o di differenza tra i punteggi, e permette di rispondere a domande fondamentali:
- I valori sono tutti simili o molto diversi tra loro?
- La media è davvero un “valore tipico” che rappresenta bene il gruppo?
- Quanto è variabile o instabile il fenomeno?
Due distribuzioni possono avere la stessa media, ma essere profondamente diverse: una può avere dati concentrati vicino al centro, l’altra dati molto sparsi. Senza misurare la dispersione, conosciamo la “posizione” del fenomeno, ma non la sua affidabilità.
Immaginiamo un’azienda dove lo stipendio medio è $2{,}000$€1 La dispersione ci dice come sono distribuiti realmente questi soldi:
Bassa Dispersione:
gli stipendi sono tutti simili (es. tra $1{,}800$€ e $2{,}200$€). Il fenomeno è omogeneo e la media è molto rappresentativa.Alta Dispersione:
esiste una forte disuguaglianza (es. molti guadagnano $800$€, e pochi dirigenti guadagnano $10{,}000$€).
In questo caso la media è “non è attendibile” poichè poco rappresentativa, nel senso che non rappresenta quasi nessuno dei dipendenti.
In sintesi, una bassa dispersione indica che i dati sono concentrati attorno a un valore tipico, come la media, suggerendo un fenomeno relativamente uniforme e più prevedibile. Al contrario, un’alta dispersione indica una maggiore variabilità dei dati, associata a un fenomeno più eterogeneo.
Per calcolare il grado di variabilità o dispersione di un dataset, possiamo usare l’intervallo, la deviazione standard e la varianza.
L’Intervallo (Range)
L’intervallo (range) è la misura più semplice della variabilità ed è piuttosto intuitiva. È la distanza tra il punteggio più alto e quello più basso di un dataset, e mostra quanto è ampia la distribuzione tra il punto più basso e quello più alto.
In generale, la formula per l’intervallo è:
$$r = h - l$$dove:
- $r$ è l’intervallo (range),
- $h$ è il punteggio più alto nel dataset,
- $l$ è il punteggio più basso nel dataset.
Prendiamo ad esempio la seguente serie di punteggi (qui riportati in ordine decrescente):
$$ \{12, 18, 15, 24, 17, 21, 14, 19, 27, 16\} $$In questo esempio, l’intervallo è:
$$r = 27 - 12 = 15$$
L’intervallo di un dataset è una misura di variabilità semplice da calcolare, ma considera esclusivamente il valore minimo e quello massimo. Per questo motivo, misure come la deviazione standard e l’intervallo interquartile sono generalmente preferite, poiché tengono conto in modo più completo della distribuzione dei dati.
L’Intervallo Interquartile (IQR)
L’Intervallo Interquartile (IQR), o scarto interquartile, è una misura di dispersione che descrive quanto i dati sono distribuiti nella parte centrale di una distribuzione, ignorando i valori estremi.
Lo descriviamo nel capitolo Misure di Posizione, dopo aver introdotto i Percentili e i Quartili.
Varianza della Popolazione
Per descrivere in modo completo una distribuzione di dati, non è sufficiente conoscere una misura di posizione (come la media), ma è necessario anche quantificare quanto i dati sono dispersi attorno ad essa. Due delle principali misure di dispersione sono la varianza e la deviazione standard.
La varianza della popolazione $\sigma^2$ (sigma quadro) misura la dispersione media dei valori rispetto alla media della popolazione. In altre parole, indica quanto, mediamente, le osservazioni si discostano dal valore medio. Si calcola come segue:
$$ \sigma^2 = \frac{1}{N} \sum_{i=1}^{N} (x_i - \mu)^2 $$dove:
- $N$ è la dimensione della popolazione;
- $x_i$ sono i valori osservati;
- $\mu$ è la media della popolazione.
Valori elevati indicano dati molto dispersi, mentre valori piccoli indicano dati concentrati attorno alla media:

L’elevamento al quadrato degli scarti $(x_i - \mu)^2$ evita che le differenze positive e negative si annullino e attribuisce maggiore peso agli scostamenti più grandi, rendendo la varianza molto sensibile ai dati estremi.
Inoltre, la varianza è espressa nell’unità di misura originale, elevata al quadrato. Ad esempio, se i dati sono misurati in metri, la varianza è espressa in metri quadrati ($m^2$). Per questo motivo, il suo valore può risultare poco intuitivo e difficile da interpretare direttamente.
In Python, possiamo calcolare $\sigma^2$ in questo modo:
data = [12, 15, 14, 10, 18, 16, 13, 17, 11, 14]
# Calcolo con le API standard di Python
import statistics
# statistics.pvariance() calcola direttamente la varianza della popolazione.
variance = statistics.pvariance(data)
print(f"Varianza della popolazione: {variance:.2f}")
# Varianza della popolazione: 6.00
# Calcolo con Numpy
import numpy as np
# Rendiamo il parametro "ddof=0" esplicito:
variance = np.var(data, ddof=0)
print(f"Varianza della popolazione: {variance:.2f}")
# Varianza della popolazione: 6.00
np.var() utilizza per default il parametro ddof settato a $0$.
Il parametro ddof significa “Delta Degrees of Freedom”,
e indica a NumPy quanto sottrarre dal numero di osservazioni $N$
quando calcola varianza e deviazione standard.
Possiamo pensare a ddof semplicemente come al numero di gradi di libertà
da sottrarre a $N$ nel denominatore.
In np.var() il divisore utilizzato è $\left(N - \text{ddof}\right)$.
Quindi settando ddof=0, usiamo $N$ come denominatore.
Deviazione Standard della Popolazione
La deviazione standard della popolazione $\sigma$ (sigma) è la radice quadrata della varianza. La formula corrispondente per la deviazione standard della popolazione è:
$$ \sigma = \sqrt{\sigma^2} = \sqrt{\frac{1}{N} \sum_{i=1}^{N} (x_i - \mu)^2} $$Questa trasformazione consente di esprimere la dispersione nella stessa unità di misura dei dati originali, rendendo la misura più facilmente interpretabile.

In Python:
import math
data = [12, 15, 14, 10, 18, 16, 13, 17, 11, 14]
# Calcolo con le API standard di Python.
# La deviazione standard è la radice quadrata della varianza:
std_dev = math.sqrt(statistics.pvariance(data))
print(f"Deviazione standard della popolazione: {std_dev:.2f}")
# Deviazione standard della popolazione: 2.45
# oppure possiamo usare direttamente pstdev(),
# che specifica per la deviazione standard della popolazione
std_dev = statistics.pstdev(data)
print(f"Deviazione standard della popolazione: {std_dev:.2f}")
# Deviazione standard della popolazione: 2.45
# Calcolo con Numpy
# Con NumPy possiamo utilizzare np.std():
std_dev = np.std(data, ddof=0)
print(f"Deviazione standard della popolazione: {std_dev:.2f}")
# Deviazione standard della popolazione: 2.45
Varianza Campionaria
Nella pratica statistica, spesso non si ha accesso all’intera popolazione, ma solo a un campione di dati. In questo caso, le misure di dispersione vengono calcolate sui dati osservati e utilizzate per stimare la variabilità della popolazione.
La varianza campionaria $s^2$ misura la dispersione dei dati del campione rispetto alla media campionaria, ed è calcolata come:
$$ s^2 = \frac{1}{n - 1} \sum_{i=1}^{n} (x_i - \bar{x})^2 $$dove:
- $n$ è la dimensione del campione;
- $x_i$ sono i valori osservati;
- $\bar{x}$ è la media della popolazione.
Quando la popolazione è ampia e il campione è di dimensioni ridotte (in genere inferiore a 30 osservazioni), la varianza calcolata dividendo per $n$ tende a sottostimare la varianza della popolazione. Per correggere questa distorsione, si utilizza la correzione di Bessel, che consiste nel dividere per $(n - 1)$ anziché per $n$. In questo modo si ottiene una stima leggermente più elevata, ma non distorta della varianza della popolazione.
In Python, possiamo calcolare $s^2$ in questo modo:
# Popolazione
population = [10, 12, 14, 15, 18, 20, 21, 23, 25, 27,
30, 31, 33, 35, 38, 40, 42, 45, 47, 50]
# Calcolo con le API standard di Python
# -------------------------------------
import random
import statistics
# Per la riproducibilità
random.seed(42)
# Estrazione casuale di un campione di 8 elementi
sample = random.sample(population, k=8)
print(f"Campione: {sample}")
# Campione: [15, 10, 25, 23, 42, 14, 31, 12]
# Varianza campionaria.
# statistics.variance() calcola direttamente la varianza campionaria,
# utilizzando n-1 al denominatore.
variance = statistics.variance(sample)
print(f"Varianza campionaria: {variance:.2f}")
# Varianza campionaria: 120.86
# Calcolo con Numpy
# -----------------
import numpy as np
# Per la riproducibilità
rng = np.random.default_rng(42)
# usiamo il campione precedente per confrontare i risultati del calcolo
new_extraction = False
if new_extraction:
# Eseguiamo nuovamente l'estrazione del campione di 8 elementi,
# usando le API di Numpy. Anche usando lo stesso seed,
# Python random e NumPy possono estrarre campioni diversi,
# perché utilizzano generatori e algoritmi differenti.
# Di conseguenza, i due esempi non necessariamente producono
# lo stesso valore della varianza
sample = rng.choice(population, size=8, replace=False)
print(f"Campione: {sample}")
# Varianza campionaria.
# Qui è importante specificare "ddof=1"
# per calcolare il denominatore n−1,
# ottenendo così la varianza campionaria corretta
variance = np.var(sample, ddof=1)
print(f"Varianza campionaria: {variance:.2f}")
# Varianza campionaria: 120.86
Deviazione Standard Campionaria
La deviazione standard campionaria $s$ è la radice quadrata della varianza campionaria:
$$ s = \sqrt{s^2} = \sqrt{\frac{1}{n - 1} \sum_{i=1}^{n} (x_i - \bar{x})^2} $$La procedura per calcolare la varianza campionaria e la deviazione standard campionaria è la stessa utilizzata per calcolare la varianza della popolazione e la deviazione standard della popolazione, tranne per il fatto che la somma dei quadrati viene divisa per $(n - 1)$ anziché per $N$.
In Python:
# Calcolo con le API standard di Python
# -------------------------------------
# Deviazione standard campionaria
std_dev = statistics.stdev(sample)
print(f"Deviazione standard campionaria: {std_dev:.2f}")
# Deviazione standard campionaria: 10.99
# Calcolo con Numpy
# -----------------
# Anche qui, "ddof=1" fa sì che il calcolo utilizzi n−1 al denominatore
std_dev = np.std(sample, ddof=1)
print(f"Deviazione standard campionaria: {std_dev:.2f}")
# Deviazione standard campionaria: 10.99
Considerazioni sulla Dispersione dei Dati
La varianza e la deviazione standard sono misure di dispersione che quantificano quanto i dati si discostano tra loro e rispetto al valore medio. In generale, una deviazione standard più elevata indica una maggiore variabilità dei dati:

Una maggiore variabilità non è necessariamente positiva o negativa: il suo significato dipende dal fenomeno analizzato e dal contesto dello studio.
Per esempio, immaginiamo due macchine che producono bulloni con diametro medio di $10$ mm. La macchina A ha $\sigma = 0.02$ mm, mentre la macchina B ha $\sigma = 0.5$ mm. Le due macchine hanno la stessa media, ma la seconda produce pezzi molto meno uniformi. In un processo industriale questa maggiore variabilità sarebbe probabilmente negativa.
Al contrario, se stessimo studiando il reddito degli abitanti di una città, una deviazione standard elevata non sarebbe un “errore”: sarebbe una caratteristica importante dei dati, perché ci direbbe che esistono forti differenze tra i redditi osservati.
Come la media, anche la deviazione standard (e quindi la varianza) è sensibile ai valori estremi (outlier), che possono influenzarne significativamente il valore.
A differenza della varianza, la deviazione standard è espressa nelle stesse unità di misura dei dati originali, il che la rende più immediata e intuitiva da interpretare.
Simulare il Campionamento di una Popolazione
Quando lavoriamo con dati reali, raramente possiamo osservare l’intera popolazione di interesse. Nella maggior parte dei casi analizziamo invece un campione, cioè un sottoinsieme delle osservazioni disponibili, e utilizziamo le statistiche calcolate sul campione per ottenere informazioni sulla popolazione.
Questa idea è alla base della statistica inferenziale, e abbiamo usato questo concetto quando abbiamo calcolato la Varianza Campionaria e la Deviazione Standard Campionaria.
Python permette di simulare facilmente questo processo, e di osservare cosa accade quando estraiamo uno o più campioni da una popolazione.
Per prima cosa costruiamo una popolazione artificiale. Supponiamo di conoscere l’altezza, espressa in centimetri, di una popolazione composta da $100{,}000$ individui. Per semplicità generiamo i valori utilizzando una Distribuzione Normale2 con media $\mu = 175$ e deviazione standard $\sigma = 10$. In Python possiamo utilizzare NumPy:
import numpy as np
# Creiamo un generatore di numeri pseudo-casuali. e
# Per garantire la riproducibilità dei risultati,
# impostiamo il suo seme a un numero intero costante
rng = np.random.default_rng(seed=42)
# creiamo una popolazione composta da 100,000 individui, con media 175 cm
# e deviazione standard di 10 cm
population = rng.normal(loc=175, scale=10, size=100_000)
print("Numero di individui:", len(population))
# Numero di individui: 100000
Come abbiamo già visto, possiamo calcolarne la media e deviazione standard in questo modo:
# calcoliamo la media mu e la deviazione standard sigma della popolazione
population_mean = np.mean(population)
population_std = np.std(population)
print(f"Media della popolazione: {population_mean:.2f}")
print(f"Deviazione standard della popolazione: {population_std:.2f}")
# Media della popolazione: 174.96
# Deviazione standard della popolazione: 10.04
I valori ottenuti, rispettivamente $174.96$ e $10.04$, sono molto vicini a $\mu = 175$ e $\sigma = 10$.
Supponiamo ora di non poter osservare tutti i $100{,}000$ individui. Decidiamo quindi di estrarre un campione casuale semplice (un sottoinsieme di una popolazione in cui ogni elemento ha la stessa e identica probabilità di essere estratto) composto da $30$ osservazioni. Possiamo utilizzare:
# creiamo un campione casuale semplice di 30 elementi,
# effettuando il campionamento senza reinserimento
sample = rng.choice(population, size=30, replace=False)
np.set_printoptions(precision=2)
print(f"Campione:\n {sample}")
Il parametro replace=False indica che stiamo effettuando il campionamento senza reinserimento.
Questo vuol dire che, una volta estratto un individuo dalla popolazione,
tale individuo non può essere selezionato nuovamente nello stesso campione.
Calcoliamo ora la media campionaria $\bar{x}$ e confrontiamola con la media della popolazione $\mu$:
print(f"Media della popolazione: {population_mean:.2f}")
print(f"Media del campione: {sample_mean:.2f}")
# Media della popolazione: 174.96
# Media del campione: 175.84
In generale, la media del campione non sarà esattamente uguale alla media della popolazione:
$$\bar{x} \neq \mu$$Questo è perfettamente normale. Il campione contiene infatti soltanto una parte delle osservazioni della popolazione e la sua composizione dipende dal caso. Per questo motivo, il campione non rappresenta perfettamente la popolazione di origine, e le statistiche calcolate su di esso possono differire dai corrispondenti parametri della popolazione.
La Variabilità Campionaria
La differenza tra la statistica calcolata sul campione e il parametro della popolazione deriva dalla variabilità campionaria. Se estraessimo un altro campione di $30$ individui, probabilmente otterremmo una media del campione leggermente diversa. Possiamo verificare questo comportamento ripetendo il campionamento dalla stessa popolazione più volte. Supponiamo di estrarre cinque campioni indipendenti, ciascuno composto sempre da $30$ osservazioni:
# Estraiamo cinque campioni indipendenti,
# ciascuno composto sempre da 30 osservazioni
for i in range(5):
# campione
sample = rng.choice(population, size=30, replace=False)
# calcolo della media campionaria
sample_mean = np.mean(sample)
print(f"Campione {i + 1}: media = {sample_mean:.2f}")
Potremmo ottenere qualcosa di simile:
Campione 1: media = 176.99
Campione 2: media = 175.65
Campione 3: media = 175.30
Campione 4: media = 174.85
Campione 5: media = 175.05
Ogni campione proviene dalla stessa popolazione, ma produce una media leggermente differente. Questo semplice esperimento mostra un concetto fondamentale:
Una statistica campionaria è una quantità che varia da campione a campione.
La media campionaria $\bar{X}$, quindi, può essere considerata essa stessa una variabile casuale, perché il suo valore cambia a seconda del campione estratto.
Nota: con $\bar{X}$ indichiamo la variabile casuale, mentre con $\bar{x}$ indichiamo uno specifico valore assunto dalla media campionaria.
Per capire meglio il fenomeno, possiamo aumentare notevolmente il numero degli esperimenti. Supponiamo di estrarre $10{,}000$ campioni, ciascuno di dimensione $n = 30$, e di calcolare ogni volta la media campionaria.
# Aumentiamo notevolmente il numero degli esperimenti
n_samples = 10_000
sample_size = 30
sample_means = []
for _ in range(n_samples):
# Campione
sample = rng.choice(population, size=sample_size, replace=False)
# Calcolo e salvataggio della media campionaria per il campione corrente
sample_mean = np.mean(sample)
sample_means.append(sample_mean)
# Per usare le API di Numpy, trasformiamo un array Python in un array Numpy
sample_means = np.array(sample_means)
La variabile sample_means contiene adesso le $10{,}000$ medie campionarie ottenute dai diversi campioni.
Calcoliamo ora la media di tutte queste medie campionarie:
# Calcolo della media delle medie campionarie e confronto con la media della popolazione
mean_of_sample_means = np.mean(sample_means)
print(f"Media della popolazione: {population_mean:.2f}")
print(f"Media delle medie campionarie: {mean_of_sample_means:.2f}")
# Media della popolazione: 174.96
# Media delle medie campionarie: 174.95
Scopriremo che i due valori sono molto vicini. In termini teorici, il valore atteso della media campionaria coincide con la media della popolazione:
$$ E[\bar{X}] = \mu $$Dobbiamo però capire che una simulazione Python non può dimostrare matematicamente tale uguaglianza. Può però mostrarlo empiricamente: se ripetiamo il campionamento moltissime volte e calcoliamo la media delle medie campionarie, questa tende ad avvicinarsi alla media della popolazione. In altre parole, la teoria dimostra che il valore atteso di $\bar{X}$ è $\mu$; la simulazione verifica numericamente che questo comportamento emerge anche in pratica.
La Dimensione del Campione
Un aspetto particolarmente importante emerge modificando la dimensione $n$ del campione. Proviamo, ad esempio, a confrontare campioni di dimensione $n=5$ con campioni di dimensione $n=100$ con una semplice funzione:
# Crea un campione di medie campionarie
def simulate_sample_means(population, sample_size, n_samples, rng):
sample_means = []
for _ in range(n_samples):
# Campione
sample = rng.choice(population, size=sample_size, replace=False)
# Calcolo e salvataggio della media campionaria
sample_means.append(np.mean(sample))
return np.array(sample_means)
Eseguendo le due simulazioni, possiamo confrontare la loro variabilità:
# Campione di 5 medie campionarie
means_n5 = simulate_sample_means(population, sample_size=5, n_samples=10_000, rng=rng)
# Campione di 100 medie campionarie
means_n100 = simulate_sample_means(population, sample_size=100, n_samples=10_000, rng=rng)
# Calcoliamo la deviazione standard di entrambi i campioni
print(f"Deviazione standard con n=5: {np.std(means_n5):.2f}")
print(f"Deviazione standard con n=100: {np.std(means_n100):.2f}")
# Deviazione standard con n=5: 4.54
# Deviazione standard con n=100: 1.00
La distribuzione delle medie ottenute con $n=100$ sarà molto meno dispersa. Il motivo è che campioni più grandi tendono a produrre stime della media più stabili.
Perché Utilizzare una Simulazione?
Naturalmente, nella realtà non conosciamo normalmente tutta la popolazione: se la conoscessimo, spesso non avremmo bisogno di campionarla per stimarne le caratteristiche.
La simulazione serve quindi come esperimento didattico. Ci permette di conoscere artificialmente sia la popolazione, sia i campioni, e di osservare direttamente fenomeni che, nei problemi reali, possiamo descrivere soltanto attraverso la teoria statistica.
Attraverso poche righe di Python possiamo vedere concretamente che:
- campioni differenti producono statistiche differenti;
- la media campionaria varia da campione a campione;
- le medie campionarie tendono a concentrarsi intorno alla media della popolazione;
- aumentando la dimensione del campione, la variabilità della media campionaria diminuisce.
Uso il carattere virgola come separatore delle migliaia, per mantenere la stessa formattazione utilizzata nell’output dei programmi Python. ↩︎
La Distribuzione Normale è una distribuzione di probabilità continua, simmetrica rispetto alla media e caratterizzata dalla tipica forma a campana. È definita da due parametri, la media $\mu$ e la deviazione standard $\sigma$, che ne determinano rispettivamente la posizione e la dispersione. ↩︎