import numpy as np
import scipy.stats as stats
import matplotlib.pyplot as plt
import seaborn as sns
import arviz as az
120 La fragilità del p-valore
Prerequisiti
Concetti e competenze chiave
Preparazione del Notebook
int = sum(map(ord, "s_m_errors"))
seed: = np.random.default_rng(seed=seed)
rng: np.random.Generator ="colorblind")
sns.set_theme(palette"arviz-darkgrid")
az.style.use(%config InlineBackend.figure_format = "retina"
120.1 Introduzione
Il codice seguente è ispirato da un post sul blog di Andrew Gelman.
120.2 Simulazione
La seguente simulazione ha l’obiettivo di mostrare quanto i p-valori possano essere “fragili” e variare notevolmente da campione a campione, anche quando i dati provengono da una distribuzione con parametri molto simili. Questo serve a dimostrare che il p-valore, spesso usato per determinare la significatività statistica di un effetto, può essere influenzato pesantemente dalla variabilità campionaria, soprattutto in campioni di piccole dimensioni o con effetti deboli. Gelman esprime questo concetto dicendo che
the difference between “significant” and “not significant” is not itself statistically significant.
120.2.1 Logica della Simulazione
- Obiettivo:
- Dimostrare la variabilità dei p-valori calcolati per diversi campioni estratti da una popolazione con una media molto vicina a zero.
- Mostrare come, nonostante l’effetto vero sia piccolo, i p-valori possano essere significativamente diversi tra loro, a seconda della variabilità e delle dimensioni del campione.
- Setup della Simulazione:
- Generiamo \(J = 10\) campioni indipendenti, ognuno con un numero ridotto di osservazioni (\(n = 10\)), per massimizzare la variabilità dei risultati.
- Ogni campione è generato da una distribuzione normale con una media vera di \(\mu = 0.05\) e una deviazione standard di \(\sigma = 0.1\). Questi parametri sono scelti per rendere la media dei campioni vicina a zero e, al tempo stesso, abbastanza variabile.
- Calcolo della media campionaria:
- Per ciascun campione, calcoliamo la media (\(\hat{\mu}\)) e la deviazione standard (\(\hat{\sigma}\)).
- La media del campione (\(\hat{\mu}\)) è utilizzata come stima del parametro.
- Calcolo del p-valore:
- Applichiamo un t-test per ciascun campione per verificare l’ipotesi nulla (\(H_0\)) che la media del campione sia zero.
- Il p-valore viene calcolato utilizzando la formula classica del t-test: [ t = ] dove:
- \(\hat{\mu}\) è la media del campione,
- \(\hat{\sigma}\) è la deviazione standard del campione,
- \(n\) è il numero di osservazioni per campione.
- Successivamente, il p-valore è calcolato come: [ = 2 (1 - (|t|)) ] dove \(\text{CDF}\) è la funzione cumulativa della distribuzione t con \(n-1\) gradi di libertà.
120.2.2 Descrizione della Sintassi
Il codice Python è strutturato come segue:
Importazione delle librerie:
- Usiamo
numpy
per generare i campioni casuali e calcolare le medie e le deviazioni standard. scipy.stats
fornisce la distribuzione t per calcolare il p-valore.
- Usiamo
Generazione dei campioni:
= [np.random.normal(true_mean, se, n) for _ in range(J)] samples
- Creiamo una lista di campioni (10 campioni in totale), ognuno con 10 osservazioni, utilizzando la distribuzione normale con media 0.05 e deviazione standard 0.1.
Calcolo delle medie e dei p-valori:
- Iteriamo su ciascun campione per calcolare la media (\(\hat{\mu}\)) e la deviazione standard (\(\hat{\sigma}\)).
- Calcoliamo il valore statistico \(t\) e il corrispondente p-valore utilizzando la distribuzione t.
Stampa dei risultati:
- I p-valori vengono arrotondati e stampati per osservare quanto siano variabili.
# Imposta il seme per la riproducibilità
1234)
np.random.seed(
# Parametri della simulazione
= 10 # Numero di campioni
J = 10 # Numero di osservazioni per campione
n = 0.05 # Media vera vicina a zero
true_mean = 0.1 # Deviazione standard
se
# Genera i campioni casuali
= [np.random.normal(true_mean, se, n) for _ in range(J)]
samples
# Calcola la media di ciascun campione e i p-valori usando il t-test
= []
p_values for sample in samples:
= np.mean(sample) # Media campionaria
sample_mean = np.std(sample, ddof=1) # Deviazione standard campionaria
sample_std = sample_mean / (sample_std / np.sqrt(n)) # Calcolo della statistica t
t_statistic = 2 * (1 - stats.t.cdf(np.abs(t_statistic), df=n - 1)) # Calcolo del p-valore
p_value
p_values.append(p_value)
# Stampa i p-valori arrotondati a 3 cifre decimali
print(np.round(p_values, 3))
[0.336 0.118 0.094 0.003 0.311 0.153 0.282 0.05 0.181 0.245]
Immagina che questo fosse un esperimento reale. Alcuni campioni mostrano risultati che potrebbero essere compatibili con puro rumore, alcuni sembrano fornire prove deboli contro l’ipotesi nulla, e altri mostrano risultati altamente significativi dal punto di vista statistico. Sarebbe naturale cercare di categorizzare questi risultati in qualche modo. Certo, la differenza tra “significativo” e “non significativo” non è di per sé statisticamente significativa, ma un p-valore di 0.336 in un caso e di 0.003 in un altro… sicuramente deve essere rilevante, giusto? No.
Questo è un caso estremo, in quanto non c’è una variazione sottostante reale; infatti, se si adatta un modello multilivello, si potrebbe vedere la mancanza di evidenza di una variazione effettiva sottostante.
I punti principali sono:
Il p-valore è una dichiarazione relativa all’ipotesi nulla di assenza di effetto. Non ha molto significato rispetto a un effetto reale, anche se piccolo.
Il p-valore è estremamente variabile. È una trasformazione non lineare e strana dello z-score (che invece ha un’interpretazione più chiara) e può comportarsi in modi non intuitivi.
E inoltre:
- Si può imparare molto da una simulazione. Anche un esperimento semplice come questo può essere estremamente istruttivo!
Si noti che anche le inferenze bayesiane sono altamente variabili. Qualsiasi sintesi dei dati presenterà variabilità! Il problema non è tanto con i p-valori, quanto con il loro utilizzo scorretto (come nel punto 1) e quando vengono presi come una dichiarazione forte sulla realtà (come nel punto 2), invece di essere visti come un riassunto rumoroso di un esperimento specifico. Se si fraintendono e si sovrainterpretano le inferenze bayesiane—ad esempio, adattando un modello con prior non informativi, prendendo la probabilità posteriore che il parametro sia maggiore di zero e poi decidendo sulla base di una soglia arbitraria—ci si ritrova in una situazione altrettanto problematica.
120.2.3 Conclusioni
La simulazione mostra che, nonostante le medie dei campioni siano generate con una distribuzione simile, i p-valori possono variare drasticamente. Questo effetto è amplificato dalla scelta di campioni piccoli e di una media vera molto vicina all’ipotesi nulla (zero). Dimostra quanto il p-valore possa essere influenzato da piccole variazioni nei dati e perché non sia sempre un indicatore affidabile per valutare l’efficacia o la presenza di un effetto.
120.3 Informazioni sull’Ambiente di Sviluppo
%load_ext watermark
%watermark -n -u -v -iv -w
Last updated: Wed Oct 09 2024
Python implementation: CPython
Python version : 3.12.4
IPython version : 8.26.0
matplotlib: 3.9.1
seaborn : 0.13.2
numpy : 1.26.4
arviz : 0.18.0
scipy : 1.14.0
Watermark: 2.4.3