Categories: statistica

Confronti multipli: perché su 200 query qualcuna «migliora» sempre per caso

Nel 2009 un gruppo di neuroscienziati mise un salmone in uno scanner per la risonanza magnetica funzionale, gli mostrò una serie di fotografie di persone in situazioni sociali e gli “chiese” quali emozioni provassero. L’analisi trovò una piccola area del cervello con un’attività statisticamente significativa. Il dettaglio è che il salmone era morto.
Lo studio era uno scherzo serissimo (vinse un Ig Nobel nel 2012), e il suo bersaglio non era la risonanza magnetica: era un errore di metodo diffusissimo. Con abbastanza test, qualcosa di “significativo” salta fuori comunque. Lo scanner aveva confrontato decine di migliaia di piccoli volumi del cervello, uno per uno, ciascuno con la soglia del 5%.

Nel nostro lavoro il salmone morto ha un nome familiare: Search Console. Confrontiamo il CTR di questo mese con quello del mese scorso su 200 query, troviamo una dozzina di variazioni “significative” e scriviamo nel report quali query sono migliorate. Ma quante di quelle dodici sono cambiate davvero? È il problema dei confronti multipli, e abbiamo già visto spuntarne il profilo parlando di split test SEO e di peeking.

In sintesi: quando si eseguono molti test statistici insieme, la soglia del 5% non vale più per l’insieme: con 10 test indipendenti la probabilità di almeno un falso positivo sale al 40%, con 100 al 99%. I confronti multipli si correggono in due modi. La correzione di Bonferroni (o la sua versione migliorata, il metodo di Holm) controlla la probabilità di commettere anche un solo falso positivo, dividendo la soglia per il numero di test: è prudente, ma perde molti effetti veri. Il metodo di Benjamini-Hochberg controlla invece la quota di falsi positivi fra le scoperte (false discovery rate): è meno severo e più adatto quando si scandagliano molte pagine o query. In R si usa p.adjust().

Di cosa parleremo:


Perché più test fanno più falsi allarmi?

Ricordiamo cosa promette la soglia del 5% in un test delle ipotesi: se non c’è nessun effetto, il test ci darà un falso allarme in 5 casi su 100. È una promessa che vale per un test. Il guaio comincia quando di test ne facciamo molti, e ci basta che uno qualsiasi risulti significativo per alzare la mano.

Facciamo un esempio al volo. Lanciando un dado una volta, la probabilità di fare 6 è un sesto; lanciandolo dieci volte, la probabilità di fare almeno un 6 sale all’84%. Con i test funziona allo stesso modo. Se un test da solo ha il 95% di probabilità di non dare un falso allarme, due test indipendenti ne hanno il 95% del 95%, e così via. La probabilità di almeno un falso positivo su m test indipendenti, che gli statistici chiamano family-wise error rate (FWER), è dunque:

\(FWER = 1 – (1 – \alpha)^m\)

dove α è la soglia del singolo test (0,05) e m il numero di test. Con 10 test vale 1 − 0,95¹⁰ = 40%; con 14 supera il 50%; con 100 arriva al 99,4%.

La probabilità di almeno un falso positivo al crescere del numero di test, ciascuno con la soglia del 5%. Già con 14 test un falso allarme è più probabile del contrario; con 100 è praticamente certo.

C’è un altro modo di vederla, ancora più diretto: su m test senza alcun effetto reale, il numero atteso di falsi positivi è m × α. Su 200 query che non sono cambiate affatto, dobbiamo aspettarci una decina di variazioni “significative” (200 × 0,05 = 10). Non sono un’anomalia: sono esattamente ciò che la soglia del 5% promette.

Un esempio: 200 query in Search Console

Costruiamo l’esempio per simulazione, così conosciamo la verità. Prendiamo 200 query, con qualche migliaio di impressioni al mese ciascuna e un CTR fra l’1% e il 6%. Per 180 di esse non cambia niente fra un mese e l’altro; per le prime 20 il CTR sale davvero, del 50%. Per ogni query confrontiamo i due mesi con un test sulle proporzioni, come faremmo in un report.

Genero i dati in R:

set.seed(7)
n_query <- 200
impressioni <- round(300 + rlnorm(n_query, log(5000), 0.8))   # impressioni al mese
ctr <- runif(n_query, 0.01, 0.06)                              # CTR fra l'1% e il 6%
vera <- seq_len(n_query) <= 20                                 # le 20 che cambiano davvero
clic_prima <- rbinom(n_query, impressioni, ctr)
clic_dopo  <- rbinom(n_query, impressioni, ifelse(vera, ctr * 1.5, ctr))

# un test per query: il CTR del mese scorso contro quello di questo mese
p <- mapply(function(a, b, n) prop.test(c(a, b), c(n, n))$p.value,
            clic_prima, clic_dopo, impressioni)

Ora facciamo quello che si fa di solito: segniamo come “cambiate” le query con p-value sotto 0,05, e confrontiamo con la verità.

table(scoperta = p < 0.05, vera)

#         vera
# scoperta FALSE TRUE
#    FALSE   171    2
#    TRUE      9   18

Il test ha segnalato 27 query. Diciotto sono cambiate davvero, ma nove no: sono falsi allarmi, esattamente il 5% delle 180 query immobili. In termini più chiari e diretti: un terzo delle “scoperte” del nostro report è rumore, e niente nei numeri ci dice quale terzo. Se a ciascuna di quelle nove query dedicassimo un’analisi, un intervento, una riga di commento al cliente, staremmo lavorando su un fantasma.

La correzione di Bonferroni (e quella di Holm)

La soluzione più antica, e la più intuitiva, è rendere ogni singolo test più severo. Se vogliamo che la probabilità di almeno un falso positivo sull’intera famiglia di test resti al 5%, basta dividere la soglia per il numero di test. È la correzione di Bonferroni:

\(\alpha_{Bonferroni} = \frac{\alpha}{m} = \frac{0{,}05}{200} = 0{,}00025\)

Una query è “cambiata” solo se il suo p-value scende sotto 0,00025. In R non serve fare la divisione a mano: p.adjust() restituisce i p-value corretti, da confrontare con la soglia abituale. Lo verifico in R:

table(scoperta = p.adjust(p, method = "bonferroni") < 0.05, vera)

#         vera
# scoperta FALSE TRUE
#    FALSE   180    6
#    TRUE      0   14

Nessun falso allarme: le 14 query segnalate sono tutte vere. Il prezzo, però, si vede nell’angolo in alto a destra. Sei query cambiate davvero sono sparite dal radar, perché un aumento del 50% su poche centinaia di clic non basta a scendere sotto una soglia così bassa. Bonferroni compra la certezza di non sbagliare con la cecità su una parte degli effetti reali.

C’è un perfezionamento che non costa nulla, il metodo di Holm. Si ordinano i p-value dal più piccolo al più grande; il primo si confronta con α/m, il secondo con α/(m − 1), il terzo con α/(m − 2), e ci si ferma al primo che non supera la sua soglia. Garantisce lo stesso controllo di Bonferroni ed è sempre almeno altrettanto potente, spesso di più. Nel nostro esempio dà lo stesso risultato (14 scoperte, nessuna falsa); con p.adjust(p, method = "holm") non c’è motivo di preferirgli Bonferroni.

Errore sulla famiglia o quota di scoperte false?

Prima di andare oltre, conviene fermarsi su una domanda che sembra filosofica ed è invece pratica: che cosa vogliamo controllare?

Bonferroni e Holm controllano la probabilità di commettere anche un solo falso positivo. È la richiesta giusta quando ogni errore costa caro: decidiamo di rifare il template di tutte le schede prodotto sulla base di un test, e un falso positivo vuol dire settimane di lavoro buttate.

Ma in molte analisi SEO la situazione è diversa. Scandagliamo 200 query o 500 pagine per capire quali meritano un approfondimento. Qui un falso positivo non è una catastrofe: è un’occhiata in più a una pagina che non ne aveva bisogno. Quello che non vogliamo è che le scoperte siano per lo più falsi allarmi. La grandezza da controllare diventa allora la quota di falsi positivi fra le scoperte, che gli statistici chiamano false discovery rate (FDR). Un FDR del 5% vuol dire: delle query che segnaliamo, in media il 5% sarà rumore.

Sono due promesse diverse, e la seconda è molto meno esigente della prima: per questo, a parità di dati, lascia passare più effetti veri.

Il metodo di Benjamini-Hochberg

Il metodo che controlla il FDR, proposto da Yoav Benjamini e Yosef Hochberg nel 1995, è sorprendentemente semplice. A parole: ordiniamo i p-value dal più piccolo al più grande, e invece di una soglia fissa usiamo una soglia che cresce con la posizione in classifica. Il p-value al primo posto deve stare sotto 1/m × 0,05, quello al secondo sotto 2/m × 0,05, e così via:

\(p_{(i)} \le \frac{i}{m} \cdot q\)

dove p₍ᵢ₎ è il p-value in posizione i, m il numero di test e q il FDR che accettiamo (qui 0,05). Si cerca la posizione più bassa in classifica (il p-value più grande) che rispetta la disuguaglianza: quella query e tutte quelle che la precedono sono le scoperte.

Vediamo i p-value del nostro esempio dalla posizione 14 alla 20, con la soglia di ciascuno:

Rangop-valueSoglia BH (rango/200 × 0,05)CTR cambiato davvero?
140,0000180,00350sì
150,000700,00375sì
160,001240,00400sì
170,003560,00425no
180,005310,00450sì
190,006830,00475no
200,007200,00500sì

L’ultima posizione che sta sotto la sua soglia è la 17: le prime 17 query sono le scoperte. Bonferroni, con la sua soglia fissa di 0,00025, si sarebbe fermato alla 14. Lo verifico in R:

table(scoperta = p.adjust(p, method = "BH") < 0.05, vera)

#         vera
# scoperta FALSE TRUE
#    FALSE   179    4
#    TRUE      1   16

Diciassette scoperte, sedici vere e una falsa: un falso allarme su diciassette, il 5,9%, in linea con il 5% promesso. Rispetto a Bonferroni abbiamo recuperato due query cambiate davvero, al prezzo di un solo errore. E rispetto al report senza correzioni siamo passati da nove falsi allarmi su 27 a uno su 17.

I 40 p-value più piccoli delle 200 query, in ordine, colorati secondo la verità. La soglia piatta del 5% cattura anche molte query che non sono cambiate. La soglia di Bonferroni è così bassa da lasciarne fuori diverse vere. La retta di Benjamini-Hochberg sale con il rango e taglia dopo la diciassettesima.

Il grafico mostra bene la logica. In cima alla classifica ci sono gli effetti grandi, con p-value minuscoli: li trova qualunque metodo. Il problema è la zona di mezzo, dove effetti veri ma modesti si mescolano al rumore. Bonferroni la scarta in blocco; la soglia piatta la accoglie in blocco; Benjamini-Hochberg la attraversa con una soglia che si allarga quanto basta.

(n.b. il metodo garantisce un FDR medio del 5%: in un singolo report la quota effettiva può essere più alta o più bassa. Ripetendo la simulazione 300 volte, Benjamini-Hochberg produce in media 0,6 scoperte false per report, una quota del 3,7%: sotto il 5% promesso.)

Quale correzione scegliere?

Non esiste una correzione giusta in assoluto: esiste quella adatta alla domanda. In sintesi:

SituazioneCorrezionePerché
Un solo test, deciso prima di guardare i datinessunanon c’è molteplicità
Poche decisioni costose (un template, una migrazione, la variante vincente di un A/B/n)Holmanche un solo errore costa caro
Molte query, pagine o segmenti da cui scegliere cosa approfondireBenjamini-Hochbergconta la quota di scoperte false, non l’errore singolo

Il caso più comune nella pratica quotidiana, però, è un altro: non avevamo deciso niente prima. Abbiamo aperto Search Console, filtrato, segmentato, confrontato, e ci siamo fermati sui numeri che ci sembravano interessanti. In questo scenario nessuna correzione è possibile, perché non sappiamo nemmeno quanti test abbiamo fatto davvero.

Un avvertimento: una correzione per confronti multipli vale solo se conta tutti i test che abbiamo fatto, non solo quelli che mostriamo.
Se abbiamo guardato 30 segmenti e ne riportiamo 5, la correzione va fatta su 30. Se abbiamo provato tre modi diversi di calcolare la metrica e teniamo quello che “funziona”, i test sono tre volte tanti. Il modo più semplice per non perdersi è decidere prima, e mettere per iscritto, quali confronti faremo.

Dove si nascondono i confronti multipli nella SEO

Il report mese su mese delle query è il caso più evidente, ma la molteplicità ha molti travestimenti.

I segmenti. “Il CTR non è cambiato, ma da mobile in Lombardia sì”: a forza di affettare i dati per dispositivo, paese, categoria e tipo di pagina, qualche fetta significativa salta fuori sempre. È la stessa trappola del paradosso di Simpson, vista dal lato opposto.

Le metriche. In uno split test SEO guardare clic, impressioni, CTR, posizione e conversioni vuol dire fare cinque test. Per questo la metrica principale si sceglie in anticipo.

Le varianti. Un A/B test con cinque varianti contro il controllo sono cinque confronti; se poi confrontiamo anche le varianti fra loro diventano quindici. Per i confronti fra tutte le coppie di medie l’ANOVA ha un suo strumento dedicato, il test di Tukey.

Il tempo. Guardare un test ogni giorno riservandosi di fermarlo è una molteplicità nascosta nel calendario: è il peeking problem.

Le correlazioni. Una matrice di correlazione fra 20 metriche contiene 190 coppie: una decina risulterà “significativa” anche se le metriche fossero tutte indipendenti.

(n.b. nella realtà le cose vanno anche peggio che nella nostra simulazione. Il CTR di una query non oscilla solo per il caso binomiale dei clic: cambia con la posizione, con le funzioni della SERP, con il mix di dispositivi. prop.test() ignora queste fonti di variabilità e sottostima l’incertezza, quindi sui dati veri i falsi allarmi sono più del 5%. Le correzioni per confronti multipli non rimediano a un modello sbagliato: lo presuppongono giusto.)

Domande frequenti

Cosa sono i confronti multipli in statistica?

Sono le situazioni in cui si eseguono molti test statistici sugli stessi dati o sulla stessa domanda: confrontare molte query, molti segmenti, molte metriche o molte varianti. Ogni test ha la sua probabilità di falso positivo, e insieme le probabilità si accumulano: con abbastanza test, qualche risultato “significativo” arriva per puro caso.

Come si applica la correzione di Bonferroni?

Si divide la soglia di significatività per il numero di test: con 20 test e una soglia del 5%, ogni test deve avere un p-value sotto 0,05 / 20 = 0,0025. In modo equivalente si moltiplica ogni p-value per il numero di test e lo si confronta con 0,05. In R lo fa p.adjust(p, method = "bonferroni").

Che differenza c’è tra FWER e FDR?

Il FWER (family-wise error rate) è la probabilità di commettere almeno un falso positivo nell’insieme dei test; il FDR (false discovery rate) è la quota media di falsi positivi fra i risultati dichiarati significativi. Controllare il FWER è più severo e adatto a poche decisioni costose; controllare il FDR è adatto all’esplorazione di molte ipotesi.

Quando usare Benjamini-Hochberg invece di Bonferroni?

Quando i test sono molti e lo scopo è selezionare cosa approfondire, accettando che una piccola quota delle scoperte sia falsa: analisi di centinaia di query o pagine, screening di segmenti, ricerca di anomalie. Bonferroni, in questi casi, è così severo da perdere buona parte degli effetti veri.

La correzione serve anche con pochi test?

Con due o tre test il rischio complessivo cresce di poco (dal 5% a circa il 10-14%), ma cresce. Se i confronti sono stati decisi in anticipo e sono pochi, il metodo di Holm costa pochissimo in potenza e mette al riparo. Se invece è stato deciso in anticipo un solo confronto principale, la correzione non serve.

Prova tu

Il codice dell’esempio è completo, e si presta a qualche esperimento.

  • Si mette a 0 il numero di query che cambiano davvero (vera <- rep(FALSE, n_query)) e si contano le scoperte con e senza correzione. Senza correzione dovrebbero essere una decina; con Bonferroni e con Benjamini-Hochberg quasi sempre zero. È il caso del salmone morto.
  • Si porta l’aumento del CTR da 1,5 a 1,2: un effetto più piccolo, più realistico per un title riscritto. Quante delle 20 query vere ritrova ciascun metodo?
  • Si ripete la simulazione con 2.000 query invece di 200, sempre con 20 vere. Cosa succede alla soglia di Bonferroni, e quante query vere sopravvivono?

L’ultimo esercizio, in particolare, mostra perché i grandi siti hanno bisogno del FDR: più test facciamo, più Bonferroni ci rende ciechi.


Abbiamo parlato di p-value per tutto l’articolo: da correggere, da ordinare, da confrontare con soglie diverse. Ma che cosa dice, esattamente, un p-value di 0,03? Che c’è il 97% di probabilità che l’effetto sia reale? Che l’ipotesi nulla ha il 3% di probabilità di essere vera? Nessuna delle due, ed è forse il numero più frainteso di tutta la statistica applicata. Sarà il tema del prossimo passo: il p-value e i suoi malintesi.


Per approfondire

Per capire perché tanti risultati «significativi» non reggono alla prova dei fatti, e quanto conti il numero di domande che abbiamo posto ai dati, consiglio L’arte della statistica di David Spiegelhalter: un libro che spiega l’incertezza senza formule inutili e senza scorciatoie. Per i confronti multipli negli esperimenti online, dalle metriche alle varianti, il riferimento in inglese resta Trustworthy Online Controlled Experiments di Ron Kohavi, Diane Tang e Ya Xu.

Paolo Gironi

Recent Posts

Il p-value e i suoi malintesi: cosa significa davvero p < 0,05

«Il nuovo titolo ha un p-value di 0,03: c'è il 97% di probabilità che funzioni.»…

4 giorni ago

Split test SEO: come capire se una modifica ha funzionato davvero

In queste settimane ho rifatto da cima a fondo alcune delle pagine più lette di…

1 settimana ago

Clustering delle keyword: raggruppare migliaia di query con K-means e clustering gerarchico

Capita spesso che si esporti l'elenco delle keyword da Search Console o da un tool,…

4 mesi ago

CTR atteso vs reale: trovare le pagine che rendono meno della loro posizione

Chi passa le giornate dentro Search Console conosce bene una situazione di questo tipo: una…

4 mesi ago

Naive Bayes: classificare l’intento delle query con il teorema di Bayes

Nell'articolo sul multi-armed bandit abbiamo usato Bayes per decidere fra varianti: spostare il traffico verso…

4 mesi ago

Multi-armed bandit: ottimizzare le varianti mentre il test è ancora in corso

Nell'articolo sull'A/B test bayesiano abbiamo confrontato due varianti a campione fisso: si raccolgono i dati…

4 mesi ago