120  La fragilità del p-valore

Prerequisiti

Concetti e competenze chiave

Preparazione del Notebook

import numpy as np
import scipy.stats as stats
import matplotlib.pyplot as plt
import seaborn as sns
import arviz as az
seed: int = sum(map(ord, "s_m_errors"))
rng: np.random.Generator = np.random.default_rng(seed=seed)
sns.set_theme(palette="colorblind")
az.style.use("arviz-darkgrid")
%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

  1. 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.
  2. 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.
  3. 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.
  4. 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:

  1. 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.
  2. Generazione dei campioni:

    samples = [np.random.normal(true_mean, se, n) for _ in range(J)]
    • 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.
  3. 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.
  4. Stampa dei risultati:

    • I p-valori vengono arrotondati e stampati per osservare quanto siano variabili.
# Imposta il seme per la riproducibilità
np.random.seed(1234)

# Parametri della simulazione
J = 10  # Numero di campioni
n = 10  # Numero di osservazioni per campione
true_mean = 0.05  # Media vera vicina a zero
se = 0.1  # Deviazione standard

# Genera i campioni casuali
samples = [np.random.normal(true_mean, se, n) for _ in range(J)]

# Calcola la media di ciascun campione e i p-valori usando il t-test
p_values = []
for sample in samples:
    sample_mean = np.mean(sample)  # Media campionaria
    sample_std = np.std(sample, ddof=1)  # Deviazione standard campionaria
    t_statistic = sample_mean / (sample_std / np.sqrt(n))  # Calcolo della statistica t
    p_value = 2 * (1 - stats.t.cdf(np.abs(t_statistic), df=n - 1))  # Calcolo del p-valore
    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:

  1. 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.

  2. 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:

  1. 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