0Pricing
Pandas & NumPy Academy · Lezione

T-test per confrontare le medie

Esegua t-test a un campione, t-test indipendenti a due campioni e t-test per campioni appaiati con scipy.stats, interpretando gli intervalli di confidenza.

T-test per confrontare le medie è 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'è un t-test?

Un t-test è un test d'ipotesi che determina se una differenza tra le medie di due gruppi è statisticamente significativa o probabilmente dovuta al caso. Confronta la differenza osservata con la variabilità dei dati e produce una statistica t e un p-value. Se p ≤ 0.05 (la soglia convenzionale), si rifiuta l'ipotesi nulla secondo cui le medie sono uguali. Esistono tre tipi comuni: a un campione, a due campioni indipendenti e t-test per campioni appaiati, ciascuno destinato a diversi disegni sperimentali.

T-test a un campione

Il t-test a un campione verifica se la media di un campione differisce significativamente dalla media della popolazione nota o ipotizzata. Per esempio: «Il nostro tempo medio di consegna è significativamente diverso dallo standard di settore di 3 giorni?». Usi scipy.stats.ttest_1samp(data, popmean). L'ipotesi nulla è H₀: mean = popmean. Un p-value piccolo porta a rifiutare H₀ e a concludere che la media campionaria differisce dal valore obiettivo.

import numpy as np
from scipy import stats

np.random.seed(42)
# Delivery times in days (true mean is ~3.5, not 3)
delivery_times = np.random.normal(loc=3.5, scale=0.8, size=50)

# Test: is our mean significantly different from 3 days?
stat, p = stats.ttest_1samp(delivery_times, popmean=3.0)
print(f'Sample mean: {delivery_times.mean():.3f}')
print(f't-statistic: {stat:.3f}')
print(f'p-value: {p:.4f}')
print('Differs from 3?', 'Yes' if p < 0.05 else 'No')

T-test a due campioni indipendenti

Il t-test indipendente a due campioni confronta le medie di due gruppi indipendenti. Ad esempio: «La variante del test A/B produce ricavi medi più elevati rispetto al controllo?». Usi scipy.stats.ttest_ind(group_a, group_b). Il parametro equal_var è importante: imposti equal_var=False (t-test di Welch) quando i due gruppi possono avere varianze diverse; questa è l'impostazione predefinita più sicura per la maggior parte dei dati reali.

import numpy as np
from scipy import stats

np.random.seed(0)
# A/B test revenue per session
control = np.random.normal(loc=25.0, scale=8.0, size=80)
variant = np.random.normal(loc=28.0, scale=9.0, size=80)

# Welch's t-test (does not assume equal variances)
stat, p = stats.ttest_ind(control, variant, equal_var=False)
print(f'Control mean: {control.mean():.2f}')
print(f'Variant mean: {variant.mean():.2f}')
print(f't-statistic: {stat:.3f}')
print(f'p-value: {p:.4f}')
print('Significant difference?', 'Yes' if p < 0.05 else 'No')

Test a una coda e a due code

Per impostazione predefinita, i t-test sono a due code: verificano se le medie differiscono in una direzione qualsiasi (maggiore O minore). Se ha un'ipotesi direzionale («la variante aumenta i ricavi»), usi un test a una coda con il parametro alternative: 'greater' o 'less'. Un test a una coda ha maggiore potenza nella direzione specifica, ma non fornisce alcuna evidenza riguardo all'altra direzione. Decida la direzione prima di esaminare i dati, per evitare l'HARKing (Hypothesising After Results are Known, cioè formulare ipotesi dopo aver conosciuto i risultati).

import numpy as np
from scipy import stats

np.random.seed(1)
control = np.random.normal(25, 8, 100)
variant = np.random.normal(27, 8, 100)

# Two-tailed (default): does mean differ at all?
stat, p_two = stats.ttest_ind(control, variant, equal_var=False,
                              alternative='two-sided')
# One-tailed: is variant > control?
stat, p_one = stats.ttest_ind(control, variant, equal_var=False,
                              alternative='less')

print(f'Two-tailed p: {p_two:.4f}')
print(f'One-tailed p (variant greater): {p_one:.4f}')

T-test per campioni appaiati

Il t-test per campioni appaiati si usa quando ogni osservazione del gruppo A ha un abbinamento naturale nel gruppo B, ad esempio misurazioni prima e dopo un trattamento sugli stessi soggetti, oppure negli stessi negozi in due mesi diversi. L'appaiamento controlla la variabilità tra i soggetti, rendendo il test più potente di un t-test indipendente sugli stessi dati. Usi scipy.stats.ttest_rel(before, after). L'ordine degli elementi deve corrispondere: before[i] e after[i] devono provenire dallo stesso soggetto.

import numpy as np
from scipy import stats

np.random.seed(5)
# Blood pressure before and after medication (paired)
before = np.random.normal(130, 10, 30)
after = before - np.random.normal(5, 3, 30)  # Treatment reduces BP by ~5

stat, p = stats.ttest_rel(before, after)
print(f'Mean before: {before.mean():.2f}')
print(f'Mean after: {after.mean():.2f}')
print(f't-statistic: {stat:.3f}')
print(f'p-value: {p:.4f}')
print('Treatment effective?', 'Yes' if p < 0.05 else 'No')

Interpretazione della statistica t

La statistica t misura a quante deviazioni standard della media si trova la differenza osservata rispetto a zero. Una statistica t pari a 2 significa che la differenza osservata è 2 errori standard al di sopra di quanto ci si aspetterebbe sotto l'ipotesi nulla. Per un test a due code con α=0.05 e un campione numeroso, il valore critico è approssimativamente ±1.96: quindi |t| > 1.96 implica p < 0.05. Il p-value converte la statistica t in una probabilità, rendendone più semplice l'interpretazione senza consultare le tabelle della distribuzione t.

import numpy as np
from scipy import stats

# Demonstrate relationship between t-statistic and p-value
for t_val in [1.0, 1.96, 2.5, 3.0, 4.0]:
    # degrees of freedom = large sample -> t ~ normal
    p = 2 * stats.t.sf(abs(t_val), df=100)  # two-tailed
    print(f't = {t_val:4.2f} -> p = {p:.4f} '
          f'({'significant' if p < 0.05 else 'not significant'})')

Intervalli di confidenza per la differenza tra le medie

Un p-value da solo non indica la grandezza dell'effetto. Calcoli sempre un intervallo di confidenza per la differenza tra le medie. Un IC al 95% che include lo zero è compatibile con p > 0.05 (non significativo); uno che esclude lo zero indica che la differenza è significativa. L'IC indica anche se la dimensione dell'effetto è rilevante dal punto di vista pratico: una differenza statisticamente significativa di $0.01 nei ricavi può essere irrilevante, mentre una differenza di $50 con p=0.06 può comunque essere importante per l'azienda.

import numpy as np
from scipy import stats

np.random.seed(0)
control = np.random.normal(25, 8, 80)
variant = np.random.normal(28, 8, 80)

diff = variant.mean() - control.mean()
se = np.sqrt(control.var()/len(control) + variant.var()/len(variant))
df = len(control) + len(variant) - 2
t_crit = stats.t.ppf(0.975, df=df)
ci_low = diff - t_crit * se
ci_high = diff + t_crit * se

print(f'Mean difference: {diff:.2f}')
print(f'95% CI: [{ci_low:.2f}, {ci_high:.2f}]')
print('CI excludes zero:', ci_low > 0 or ci_high < 0)

Dimensione dell'effetto: d di Cohen

La significatività statistica dipende dalla dimensione del campione: con campioni sufficientemente numerosi, anche differenze trascurabili diventano significative. La d di Cohen misura la significatività pratica (dimensione dell'effetto): è la differenza tra le medie divisa per la deviazione standard combinata. Indicazioni generali: d < 0.2 è trascurabile, 0.2–0.5 è piccola, 0.5–0.8 è media, > 0.8 è grande. Riporti sempre la dimensione dell'effetto insieme ai p-value: un p-value significativo con d = 0.05 indica che la differenza è reale, ma probabilmente non rilevante nella pratica.

import numpy as np
from scipy import stats

np.random.seed(0)
g1 = np.random.normal(25, 8, 200)
g2 = np.random.normal(28, 8, 200)

stat, p = stats.ttest_ind(g1, g2)

# Cohen's d
pooled_std = np.sqrt((g1.var() + g2.var()) / 2)
cohens_d = (g2.mean() - g1.mean()) / pooled_std

print(f'p-value: {p:.4f}')
print(f'Cohen\'s d: {cohens_d:.3f}')

if cohens_d < 0.2: print('Effect: negligible')
elif cohens_d < 0.5: print('Effect: small')
elif cohens_d < 0.8: print('Effect: medium')
else: print('Effect: large')

Assunzioni del t-test

Il t-test indipendente presuppone: (1) Indipendenza: le osservazioni all'interno di ciascun gruppo sono indipendenti tra loro; (2) Normalità: i dati di ciascun gruppo sono approssimativamente normali (robusto per n > 30 grazie al CLT); (3) per il t-test di Student (equal_var=True), varianze uguali. Il t-test di Welch (equal_var=False) non richiede l'assunzione 3 ed è preferibile. Quando la normalità è gravemente violata con campioni piccoli (< 30), usi invece il test U di Mann-Whitney.

import numpy as np
from scipy import stats

np.random.seed(0)
g1 = np.random.normal(10, 2, 30)
g2 = np.random.normal(11, 5, 30)  # Very different variance!

# Levene test for equal variances
stat_l, p_l = stats.levene(g1, g2)
print(f'Levene test p: {p_l:.4f}')
if p_l < 0.05:
    print('Unequal variances -> use Welch\'s (equal_var=False)')
    stat, p = stats.ttest_ind(g1, g2, equal_var=False)
else:
    print('Equal variances OK -> Student\'s t-test')
    stat, p = stats.ttest_ind(g1, g2, equal_var=True)
print(f'Result: t={stat:.3f}, p={p:.4f}')

T-test su Series di Pandas

In pratica, i dati da sottoporre al test si trovano in una colonna di un DataFrame Pandas. Può passare direttamente una Series di Pandas alle funzioni di scipy.stats: funzionano senza problemi sia con le Series sia con gli array NumPy. Un flusso di lavoro comune consiste nel filtrare il DataFrame per ottenere la Series di ciascun gruppo, eseguire il t-test e memorizzare i risultati in un DataFrame riepilogativo. Questo schema può essere usato per testare molte coppie di colonne o gruppi in un ciclo.

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

np.random.seed(0)
df = pd.DataFrame({
    'group': ['A']*60 + ['B']*60,
    'revenue': np.concatenate([
        np.random.normal(100, 20, 60),
        np.random.normal(110, 22, 60)
    ])
})

grp_a = df.loc[df['group'] == 'A', 'revenue']
grp_b = df.loc[df['group'] == 'B', 'revenue']

stat, p = stats.ttest_ind(grp_a, grp_b, equal_var=False)
print(f'A mean: {grp_a.mean():.2f}, B mean: {grp_b.mean():.2f}')
print(f'p-value: {p:.4f}')

Problema dei confronti multipli

Eseguire simultaneamente molti t-test aumenta la probabilità di un falso positivo. Se esegue 20 t-test con α=0.05, si aspetta 1 falso positivo dovuto al caso. Questo è il problema dei confronti multipli. Applichi la correzione di Bonferroni: divida α per il numero di test (p_threshold = 0.05 / n_tests). Per ottenere una maggiore potenza con un approccio meno conservativo, usi la correzione Benjamini-Hochberg False Discovery Rate (FDR) disponibile in statsmodels. Nei test A/B con molte metriche, corregga sempre per i confronti multipli.

import numpy as np
from scipy import stats

np.random.seed(0)
n_tests = 10
results = []
for i in range(n_tests):
    g1 = np.random.normal(0, 1, 50)
    g2 = np.random.normal(0.1, 1, 50)  # Tiny true effect
    _, p = stats.ttest_ind(g1, g2)
    results.append(p)

print('p-values:', [round(p, 3) for p in results])
bonferroni_thresh = 0.05 / n_tests
print(f'Bonferroni threshold: {bonferroni_thresh}')
print('Significant after Bonferroni:', sum(p < bonferroni_thresh for p in results))

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: ttest_1samp verifica una media campionaria rispetto a un valore di riferimento, ttest_ind (il test di Welch con equal_var=False) confronta due gruppi indipendenti e ttest_rel gestisce misurazioni appaiate. Riporti sempre la d di Cohen insieme al p-value per distinguere la significatività statistica da quella pratica. Nella prossima lezione verificheremo le relazioni tra variabili categoriali con il test del chi-quadrato.

Domande Frequenti

La lezione «T-test per confrontare le medie» è gratuita?

Sì — il testo completo di «T-test per confrontare le medie» è 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 «T-test per confrontare le medie»?

Esegua t-test a un campione, t-test indipendenti a due campioni e t-test per campioni appaiati con scipy.stats, interpretando gli intervalli di confidenza. 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 «T-test per confrontare le medie»?

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