Pandas & NumPy Academy · Lezione

Statistiche descrittive e test di normalità

Calcoli asimmetria e curtosi con scipy.stats, esegua un test di Shapiro-Wilk per la normalità e interpreti il p-value.

Lezione 1 di 413 passaggi

Statistiche descrittive e test di normalità è una lezione Pandas & NumPy Academy gratuita su CoddyKit. Questa è la lezione 1 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.

Statistica descrittiva e inferenziale

La statistica descrittiva riassume ciò che è presente nei dati: media, mediana, deviazione standard e asimmetria. La statistica inferenziale formula affermazioni su una popolazione basandosi su un campione: test d'ipotesi, intervalli di confidenza e p-value. Il modulo scipy.stats di SciPy collega questi due ambiti: fornisce sia misure descrittive che vanno oltre describe() di Pandas, sia l'intera raccolta dei test d'ipotesi classici. Combinare Pandas per la manipolazione dei dati e SciPy per i test statistici è il flusso di lavoro standard della data science in Python.

Statistiche descrittive estese

describe() di Pandas fornisce conteggio, media, deviazione standard, valori minimo/massimo e quartili. scipy.stats aggiunge asimmetria (l'assenza di simmetria della distribuzione) e curtosi (la pesantezza delle code). Un'asimmetria pari a 0 indica simmetria; un'asimmetria positiva indica una coda destra più lunga (molti valori piccoli e pochi valori molto grandi). Una curtosi pari a 3 (o pari a 0 nella forma in eccesso) è normale; valori più elevati indicano code più pesanti, con più valori anomali estremi di quanti ne produrrebbe una distribuzione normale.

import pandas as pd
from scipy import stats
import numpy as np

np.random.seed(0)
data = np.concatenate([
    np.random.exponential(scale=2, size=500),  # right-skewed
    np.random.normal(loc=5, scale=1, size=500)
])

print('Mean:', round(data.mean(), 3))
print('Std:', round(data.std(), 3))
print('Skewness:', round(stats.skew(data), 3))
print('Kurtosis (excess):', round(stats.kurtosis(data), 3))

Comprendere l'asimmetria

L'asimmetria è importante per scegliere i test statistici e le trasformazioni. Una distribuzione asimmetrica a destra (asimmetria positiva) ha una media maggiore della mediana ed è tipica dei dati sui redditi, dei tempi di reazione e degli importi delle vendite. Una distribuzione asimmetrica a sinistra (asimmetria negativa) ha una media minore della mediana. Regola pratica: |skewness| < 0.5 indica una distribuzione approssimativamente simmetrica; 0.5–1.0 indica un'asimmetria moderata; > 1.0 indica un'asimmetria elevata, che può trarre beneficio da una trasformazione logaritmica o dalla radice quadrata prima di applicare test che presuppongono la normalità.

import numpy as np
from scipy import stats

# Right-skewed: income-like data
right_skewed = np.random.lognormal(mean=1, sigma=1, size=1000)
print('Right skew:', round(stats.skew(right_skewed), 3))

# After log transform
print('After log transform:', round(stats.skew(np.log(right_skewed)), 3))

# Symmetric normal
normal_data = np.random.normal(0, 1, 1000)
print('Normal skew:', round(stats.skew(normal_data), 3))

Perché il test di normalità è importante

Molti test statistici — t-test, ANOVA e correlazione di Pearson — presuppongono che i dati, o i residui, seguano una distribuzione normale (gaussiana). Se questa ipotesi è violata con campioni piccoli, i p-value del test possono essere inaccurati. Il test di normalità verifica se l'ipotesi è soddisfatta. Per campioni grandi (n > 100), i test di normalità diventano molto sensibili e quasi sempre rifiutano la normalità per deviazioni trascurabili; in tal caso, si affidi al Teorema del Limite Centrale (le medie campionarie sono approssimativamente normali) invece di testare i dati grezzi.

Il test di Shapiro-Wilk

Il test di Shapiro-Wilk (scipy.stats.shapiro(data)) è il test di normalità più potente per campioni fino a circa 5.000 elementi. Restituisce una statistica W e un p-value. Se p > 0.05, non si rifiuta l'ipotesi di normalità: i dati sono compatibili con una distribuzione normale. Se p ≤ 0.05, si rifiuta l'ipotesi di normalità. Ricordi: non rifiutare l'ipotesi di normalità non dimostra che i dati siano normali; significa soltanto che non ci sono prove sufficienti per affermare il contrario.

import numpy as np
from scipy import stats

np.random.seed(42)

# Normal data
normal = np.random.normal(0, 1, 100)
stat, p = stats.shapiro(normal)
print(f'Normal data: W={stat:.4f}, p={p:.4f}')
print('Conclusion:', 'Looks normal' if p > 0.05 else 'Not normal')

# Skewed data
skewed = np.random.exponential(1, 100)
stat2, p2 = stats.shapiro(skewed)
print(f'Skewed data: W={stat2:.4f}, p={p2:.4f}')
print('Conclusion:', 'Looks normal' if p2 > 0.05 else 'Not normal')

Il test di Kolmogorov-Smirnov

Il test di Kolmogorov-Smirnov (K-S) (scipy.stats.kstest(data, 'norm', args)) verifica se un campione segue una distribuzione specificata. A differenza di Shapiro-Wilk, funziona con qualsiasi distribuzione, non solo con quella normale, e con campioni grandi. Calcola la differenza massima tra la funzione di distribuzione cumulativa empirica dei dati e la funzione di distribuzione cumulativa teorica. Una variante, il test K-S a due campioni (stats.ks_2samp(a, b)), verifica se due campioni provengono dalla stessa distribuzione ed è utile per confrontare distribuzioni prima e dopo.

import numpy as np
from scipy import stats

np.random.seed(0)
data = np.random.normal(loc=5, scale=2, size=200)

# One-sample K-S test against N(5, 2) distribution
stat, p = stats.kstest(data, 'norm', args=(5, 2))
print(f'K-S test: stat={stat:.4f}, p={p:.4f}')
print('Follows N(5,2)?', 'Yes' if p > 0.05 else 'No')

# Two-sample K-S test
data2 = np.random.normal(loc=5.5, scale=2, size=200)
stat2, p2 = stats.ks_2samp(data, data2)
print(f'Two-sample K-S: stat={stat2:.4f}, p={p2:.4f}')

Verifica visiva della normalità: Q-Q plot

Il Q-Q plot (quantile-quantile) è una verifica visiva della normalità: rappresenta i quantili dei dati rispetto ai quantili di una distribuzione normale. Se i dati sono normali, i punti si dispongono lungo una linea diagonale retta. Le deviazioni dalla linea indicano un allontanamento dalla normalità: le curve a S indicano curtosi, mentre le curvature indicano asimmetria. I test statistici indicano se le deviazioni sono significative; i Q-Q plot mostrano la natura e la posizione delle deviazioni. Usi scipy.stats.probplot() per generare i dati del Q-Q plot.

import numpy as np
from scipy import stats
import matplotlib.pyplot as plt

np.random.seed(1)
data = np.random.exponential(2, 200)

# Q-Q plot
(osm, osr), (slope, intercept, r) = stats.probplot(data, dist='norm')
print(f'R-squared of Q-Q fit: {r**2:.4f}')  # Close to 1 = normal
# Visual inspection: if plotting, points on the line = normal
# plt.figure()
# stats.probplot(data, dist='norm', plot=plt)
# plt.show()

Il Teorema del Limite Centrale nella pratica

Il Teorema del Limite Centrale (CLT) afferma che la distribuzione delle medie campionarie tende alla normalità all'aumentare della dimensione del campione, indipendentemente dalla distribuzione sottostante. Per n ≥ 30, la media campionaria è approssimativamente normale anche se le singole osservazioni sono asimmetriche. Per questo i t-test sono robusti con campioni grandi: si verifica se la media è diversa e la media è approssimativamente normale anche quando i dati grezzi non lo sono. Per campioni piccoli provenienti da popolazioni non normali, usi invece test non parametrici.

import numpy as np
from scipy import stats

np.random.seed(0)
# Population: heavily right-skewed (exponential)
population = np.random.exponential(scale=1, size=10000)
print('Population skewness:', round(stats.skew(population), 3))

# Sample means are approximately normal (CLT)
sample_means = [np.random.choice(population, 50).mean()
                for _ in range(1000)]
print('Sample means skewness:', round(stats.skew(sample_means), 3))
_, p = stats.shapiro(sample_means)
print('Shapiro p-value for means:', round(p, 4))

Normalità per gruppo

Nei test di confronto tra gruppi (t-test, ANOVA), la normalità è richiesta all'interno di ciascun gruppo, non nei dati complessivi. Quando verifica se le vendite differiscono per regione, testi la normalità delle vendite separatamente all'interno di ogni regione. Una distribuzione non normale nel complesso può comunque rispettare la normalità all'interno dei sottogruppi. Applichi stats.shapiro() a ciascun gruppo usando groupby() di Pandas e iterando sui gruppi, per verificare sistematicamente l'ipotesi.

import pandas as pd
import numpy as np
from scipy import stats

np.random.seed(0)
df = pd.DataFrame({
    'region': ['N']*50 + ['S']*50 + ['E']*50,
    'sales': np.concatenate([
        np.random.normal(100, 20, 50),
        np.random.normal(110, 25, 50),
        np.random.normal(95, 15, 50)
    ])
})

for region, group in df.groupby('region'):
    stat, p = stats.shapiro(group['sales'])
    print(f'Region {region}: W={stat:.4f}, p={p:.4f} -> '
          f'{"Normal" if p>0.05 else "Not normal"}')

Alternative non parametriche

Quando la normalità è violata, usi test non parametrici che non fanno ipotesi sulla distribuzione. Il test U di Mann-Whitney (stats.mannwhitneyu) sostituisce il t-test per campioni indipendenti; il test dei ranghi con segno di Wilcoxon (stats.wilcoxon) sostituisce il t-test per campioni appaiati; il test di Kruskal-Wallis (stats.kruskal) sostituisce l'ANOVA a una via. Questi test usano i ranghi invece dei valori grezzi e sono robusti rispetto ai valori anomali, ma hanno una potenza statistica inferiore rispetto alle controparti parametriche quando la normalità è effettivamente soddisfatta.

import numpy as np
from scipy import stats

np.random.seed(0)
# Non-normal data
group_a = np.random.exponential(2, 40)
group_b = np.random.exponential(2.5, 40)

# Parametric would be wrong here; use non-parametric
stat, p = stats.mannwhitneyu(group_a, group_b, alternative='two-sided')
print(f'Mann-Whitney U test: U={stat:.1f}, p={p:.4f}')
print('Groups differ?' , 'Yes' if p < 0.05 else 'No')

Riepilogare le statistiche per più colonne

Nell'EDA, spesso è necessario verificare contemporaneamente la normalità e l'asimmetria di tutte le colonne numeriche. Combini select_dtypes di Pandas con un ciclo sulle colonne che chiama stats.shapiro() e stats.skew(). Crei un DataFrame riepilogativo con colonne per asimmetria, curtosi e p-value di Shapiro, così da ottenere una panoramica delle colonne non normali che richiedono una trasformazione prima della modellazione. Questo fa parte di una checklist sistematica della qualità dei dati.

import pandas as pd
import numpy as np
from scipy import stats

np.random.seed(0)
df = pd.DataFrame({
    'age': np.random.normal(35, 10, 200),
    'income': np.random.lognormal(10, 1, 200),
    'score': np.random.uniform(0, 100, 200)
})

rows = []
for col in df.select_dtypes(include='number').columns:
    s = stats.skew(df[col])
    _, p = stats.shapiro(df[col][:200])
    rows.append({'column': col, 'skewness': round(s, 3),
                 'shapiro_p': round(p, 4), 'normal': p > 0.05})

print(pd.DataFrame(rows).to_string(index=False))

Verifica rapida

Verifichi la Sua comprensione dei concetti di analisi dei dati trattati in questa lezione.

Riepilogo della lezione

In questa lezione ha imparato che scipy.stats.skew() e kurtosis() descrivono la forma di una distribuzione andando oltre le informazioni fornite da describe(), che scipy.stats.shapiro() verifica la normalità, dove p > 0.05 indica l'assenza di prove contro la normalità, e che il Teorema del Limite Centrale rende i t-test robusti per campioni grandi anche in presenza di dati non normali. Ora applicheremo i test d'ipotesi con i t-test per confrontare le medie dei gruppi.

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 «Statistiche descrittive e test di normalità» è gratuita?

Sì — il testo completo di «Statistiche descrittive e test di normalità» è 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 «Statistiche descrittive e test di normalità»?

Calcoli asimmetria e curtosi con scipy.stats, esegua un test di Shapiro-Wilk per la normalità e interpreti il p-value. 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 1 di 4.

Quanto tempo richiede la lezione «Statistiche descrittive e test di normalità»?

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. Statistiche descrittive e test di normalità
  2. T-test per confrontare le medie
  3. Test chi-quadrato di indipendenza
  4. ANOVA e test post-hoc
← Torna a Pandas & NumPy Academy