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 questo blog: nuove aperture, title riscritti, domande frequenti. Fra un mese aprirò Search Console, e la tentazione sarà la solita: confrontare i clic di prima con quelli di dopo.
Se saranno saliti, sarà merito mio? E se saranno scesi, avrò sbagliato tutto? Il confronto prima/dopo non può rispondere a nessuna delle due domande: nel frattempo cambiano la stagione, la concorrenza, l’algoritmo. Basta un core update nella settimana sbagliata e il miglior lavoro del mondo sembra un disastro.

Nel marketing la risposta classica a questo problema è l’A/B test: due versioni, utenti divisi a caso, si confrontano i risultati. Con la SEO però c’è un ostacolo insormontabile, perché Google vede una sola versione della pagina. Non possiamo dividere gli utenti: possiamo dividere le pagine. È l’idea dello split test SEO, e lo strumento statistico che lo rende affidabile ha un nome che sembra uno scioglilingua: la differenza delle differenze.

In sintesi: uno split test SEO misura l’effetto di una modifica (title, template, dati strutturati) dividendo a caso un gruppo di pagine simili in pagine test, che ricevono la modifica, e pagine di controllo, lasciate intatte. Il confronto prima/dopo da solo inganna, perché stagionalità e core update colpiscono tutte le pagine; la differenza delle differenze (difference-in-differences, DiD) sottrae alla variazione delle pagine test quella delle pagine di controllo, e ciò che resta è l’effetto della modifica. Con un t-test sulle variazioni pagina per pagina si stabilisce se l’effetto è distinguibile dal caso. Servono pagine numerose e omogenee: per accorgersi di un +8% ne servono, di norma, alcune centinaia.

Di cosa parleremo:


Perché l’A/B test classico non funziona per la SEO

In un A/B test sulla conversione di una landing page, ogni visitatore vede la versione A o la versione B, assegnata a caso. Siccome il sorteggio rende i due gruppi di utenti simili in tutto il resto, una differenza nei risultati si può attribuire alla versione. Il caso fa da garante.

Con il ranking il meccanismo si inceppa subito. L’utente che conta, nella SEO, è uno solo: Googlebot. Se gli mostriamo due versioni della stessa pagina a seconda della visita, nel migliore dei casi non capisce quale indicizzare, nel peggiore si chiama cloaking. E il risultato che vogliamo misurare (posizioni, impressioni, clic organici) dipende da come Google valuta la pagina, non da come la vive il singolo visitatore.

La via d’uscita è spostare il sorteggio un livello più su. Al posto degli utenti si sorteggiano le pagine: su un gruppo la modifica, sull’altro niente. Funziona quando il sito ha molte pagine costruite con lo stesso template, che rispondono allo stesso tipo di ricerca: le schede prodotto di un e-commerce, le schede di un sito di annunci, le pagine di categoria, le pagine città di un portale. Un sito con dieci pagine tutte diverse, va detto subito, non può fare uno split test: non ha abbastanza unità da sorteggiare.

Perché il confronto prima/dopo inganna

Se non abbiamo un gruppo di controllo, l’unico confronto possibile è con il passato. Il guaio è che il passato differisce dal presente in mille modi che non c’entrano con la nostra modifica:

  • la stagionalità: a dicembre si cercano regali, ad agosto molto meno di tutto il resto;
  • gli aggiornamenti dell’algoritmo, che spostano il traffico di interi settori nel giro di pochi giorni;
  • la concorrenza, che nel frattempo pubblica, migliora, sparisce;
  • la regressione verso la media: se abbiamo scelto di intervenire proprio sulle pagine andate peggio, una parte del miglioramento sarebbe arrivata comunque.

Ognuno di questi fattori può essere più grande dell’effetto che cerchiamo. Un title migliore vale magari un 5-10% di clic in più; un core update può toglierne il 20% in una settimana. Confrontando solo il prima con il dopo, misuriamo la somma di tutto ciò che è cambiato, e la nostra modifica ne è solo un pezzo, spesso il più piccolo.

Il gruppo di controllo risolve il problema alla radice: le pagine di controllo vivono la stessa stagione, lo stesso core update, la stessa concorrenza delle pagine test. L’unica cosa che le distingue è la modifica.

Come si costruisce uno split test SEO?

Le regole sono poche, ma vanno rispettate tutte.

Pagine omogenee. Test e controllo devono essere pagine dello stesso tipo, con lo stesso template e lo stesso intento di ricerca. Mescolare schede prodotto e articoli del blog vuol dire confrontare mele con pere.

Assegnazione casuale. Non scegliamo noi quali pagine modificare: le sorteggiamo. Se scegliessimo “le più promettenti”, introdurremmo esattamente la differenza di partenza che il test deve evitare. Un accorgimento utile è la randomizzazione a coppie: si ordinano le pagine per traffico, si formano coppie di pagine vicine e dentro ogni coppia si tira a sorte quale va nel test. I due gruppi nascono così bilanciati anche sul traffico, che è il fattore più ingombrante.

Un periodo “prima” abbastanza lungo. Ci serve per sapere come si comportano le pagine quando nessuno le tocca, e per verificare che test e controllo si muovano insieme (ci torneremo). Sei-otto settimane sono un buon punto di partenza.

Decidere prima quando guardare. Durata del test e metrica si fissano in anticipo. Guardare i dati ogni mattina e fermarsi al primo segnale favorevole è il peeking problem, e gonfia i falsi positivi esattamente come in un A/B test.

Un esempio: 200 schede prodotto e un core update

Come sempre, un esempio è utile a chiarire il tutto. Lo costruiamo per simulazione, così conosciamo la verità e possiamo vedere quali metodi la ritrovano.

Immaginiamo un e-commerce con 200 schede prodotto dello stesso template. Decidiamo di riscrivere i title di metà di esse, sorteggiate a coppie. Nel nostro mondo simulato la modifica funziona: porta l’8% di clic in più. Ma una settimana dopo la messa online arriva un core update che toglie il 15% dei clic a tutte le pagine del sito, test e controllo. E ogni pagina, come accade sempre, oscilla anche per conto suo, salendo o scendendo di qualche posizione.

Costruisco le pagine in R, con il sorteggio a coppie:

set.seed(2020)
n_pagine <- 200
base <- rlnorm(n_pagine, meanlog = log(30), sdlog = 1)   # clic medi al giorno

# randomizzazione a coppie: ordino per traffico e sorteggio dentro ogni coppia
ordine <- order(base)
gruppo <- character(n_pagine)
for (k in seq(1, n_pagine, by = 2)) {
  gruppo[ordine[k:(k + 1)]] <- sample(c("test", "controllo"))
}
pagine <- data.frame(pagina = 1:n_pagine, base = base, gruppo = gruppo,
                     deriva = rlnorm(n_pagine, -0.25^2 / 2, 0.25))

tapply(pagine$base, pagine$gruppo, median)
# controllo      test
#    32.36      31.96

Il sorteggio a coppie ha fatto il suo lavoro: la pagina “tipica” dei due gruppi fa circa 32 clic al giorno in entrambi.

Genero poi 14 settimane di clic giornalieri: 8 prima della modifica e 6 dopo. Il giorno 57 arrivano i nuovi title (solo sulle pagine test), il giorno 64 il core update (su tutte); nel weekend il traffico cala del 30%.

simula_clic <- function(pagine, effetto = 1.08, modifica = 57, core_update = 64) {
  d <- expand.grid(pagina = pagine$pagina, giorno = 1:98)
  d$gruppo <- pagine$gruppo[d$pagina]
  lambda <- pagine$base[d$pagina] *
    ifelse(d$giorno %% 7 %in% c(6, 0), 0.7, 1) *                    # weekend
    ifelse(d$giorno >= core_update, 0.85, 1) *                      # core update
    ifelse(d$giorno >= modifica, pagine$deriva[d$pagina], 1) *      # deriva
    ifelse(d$gruppo == "test" & d$giorno >= modifica, effetto, 1) * # la modifica
    rlnorm(nrow(d), 0, 0.15)                                        # rumore
  d$clic <- rpois(nrow(d), lambda)
  d$periodo <- ifelse(d$giorno >= modifica, "dopo", "prima")
  d
}
clic <- simula_clic(pagine)

(n.b. la deriva è l’oscillazione propria di ogni pagina dopo la modifica: concorrenti nuovi, link guadagnati o persi, piccoli spostamenti di posizione. Vale in media 1, ma la singola pagina può guadagnare o perdere anche il 30-40%.)

Ora mettiamoci nei panni di chi ha fatto la modifica e non ha un gruppo di controllo. Per ogni pagina calcolo i clic medi al giorno prima e dopo, e la loro variazione:

variazioni <- function(d) {
  m <- aggregate(clic ~ pagina + gruppo + periodo, data = d, FUN = mean)
  w <- reshape(m, idvar = c("pagina", "gruppo"), timevar = "periodo",
               direction = "wide")
  w$delta <- log(w$clic.dopo / w$clic.prima)
  w
}
w <- variazioni(clic)

# variazione media delle pagine test, in percentuale
exp(mean(w$delta[w$gruppo == "test"])) - 1
# [1] -0.101

Le pagine con i title nuovi hanno perso, in media, il 10% dei clic. Senza un controllo, la conclusione sarebbe immediata: la modifica ha fatto danni, torniamo indietro. E sarebbe sbagliata, perché sappiamo (l’abbiamo deciso noi) che la modifica funziona.

(n.b. lavoriamo sul logaritmo del rapporto dopo/prima per due ragioni. Le pagine hanno volumi diversissimi, da un clic al giorno a qualche centinaio, e un effetto come “+8%” è proporzionale, non un numero fisso di clic: sulla scala logaritmica le variazioni percentuali diventano differenze, che si possono sommare e mediare. E la media dei logaritmi, riportata indietro con exp(), è la media geometrica: non si fa trascinare da una pagina che passa da 2 a 9 clic.)

Cos’è la differenza delle differenze?

Guardiamo ora anche le pagine di controllo:

tapply(w$delta, w$gruppo, function(x) exp(mean(x)) - 1)
# controllo      test
#    -0.166    -0.101

Le pagine di controllo, che nessuno ha toccato, hanno perso il 16,6%. È il core update, più le oscillazioni di periodo: è ciò che sarebbe successo anche alle pagine test, se non le avessimo modificate. Gli economisti lo chiamano controfattuale, la storia che non è andata in scena.

L’idea della differenza delle differenze è tutta qui. Calcoliamo prima la variazione di ciascun gruppo (prima differenza), poi la differenza fra le due variazioni (seconda differenza). A parole: l’effetto della modifica è quanto le pagine test sono andate meglio, o peggio, rispetto alle pagine di controllo. In formula, con la variazione media sulla scala logaritmica indicata con Δ:

\(DiD = \bar{\Delta}_{test} – \bar{\Delta}_{controllo} = (\log T_{dopo} – \log T_{prima}) – (\log C_{dopo} – \log C_{prima})\)

dove T e C sono i clic delle pagine test e di controllo. Tutto ciò che ha colpito entrambi i gruppi allo stesso modo (stagionalità, core update, il weekend) compare in tutte e due le parentesi e si cancella nella sottrazione. Resta solo ciò che distingue i gruppi: la modifica.

I due gruppi giorno per giorno, ogni pagina rapportata alla propria media del periodo «prima» (media mobile a 7 giorni). Prima della modifica le due linee camminano insieme; il core update le fa scendere entrambe, ma dopo le pagine test restano stabilmente sopra quelle di controllo. L&apos;effetto non è il calo rispetto a 100: è lo scarto fra le due linee.
I due gruppi giorno per giorno, ogni pagina rapportata alla propria media del periodo «prima» (media mobile a 7 giorni). Prima della modifica le due linee camminano insieme; il core update le fa scendere entrambe, ma dopo le pagine test restano stabilmente sopra quelle di controllo. L’effetto non è il calo rispetto a 100: è lo scarto fra le due linee.

Il grafico racconta la storia meglio di qualunque tabella. Nella prima settimana dopo la modifica le pagine test salgono davvero, e chi guardasse solo quei giorni griderebbe al successo; poi arriva il core update e tutto precipita. Ma il controllo precipita di più. Lo scarto fra le due linee, che si mantiene fino alla fine, è il nostro effetto.

Resta da capire se quello scarto è abbastanza grande da non essere un caso. Le pagine sono poche e rumorose: magari il sorteggio ha messo nel gruppo test, per pura sfortuna del controllo, qualche pagina che sarebbe salita comunque. È una domanda da test statistico, e siccome abbiamo una variazione per pagina e due gruppi da confrontare, lo strumento è il familiare t-test per due campioni. Lo calcolo in R:

t.test(delta ~ gruppo, data = w)

# t = -2.1086, df = 197.34, p-value = 0.03624
# 95 percent confidence interval:
#  -0.144123939 -0.004821942
# mean in group controllo      mean in group test
#              -0.1809326              -0.1064596

La differenza fra le due medie è 0,0745 sulla scala logaritmica, cioè exp(0,0745) − 1 = +7,7% di clic: molto vicino all’8% vero. Il p-value di 0,036 ci dice che una differenza così grande, se la modifica fosse stata inutile, uscirebbe per caso meno di 4 volte su 100.

L’intervallo di confidenza è però l’informazione più onesta. R lo riporta per la differenza controllo − test; girato nel verso che ci interessa e riportato in percentuale va da +0,5% a +15,5%. In termini più chiari e diretti: la modifica ha quasi certamente funzionato, ma i dati sono compatibili sia con un effetto trascurabile sia con un effetto doppio di quello stimato.

Ogni punto è una pagina: la sua variazione di clic dopo la modifica. Le due nuvole si sovrappongono quasi del tutto, e decine di pagine test vanno peggio di pagine di controllo. Le barre verticali sono le medie geometriche dei due gruppi: l&apos;effetto della modifica vive solo nello scarto fra le due barre.
Ogni punto è una pagina: la sua variazione di clic dopo la modifica. Le due nuvole si sovrappongono quasi del tutto, e decine di pagine test vanno peggio di pagine di controllo. Le barre verticali sono le medie geometriche dei due gruppi: l’effetto della modifica vive solo nello scarto fra le due barre.

Questa seconda figura spiega perché nessuno dovrebbe giudicare una modifica guardando una pagina sola, o anche dieci. Le oscillazioni di una singola pagina sono molto più grandi dell’effetto che cerchiamo: decine di pagine con i title nuovi hanno perso più di pagine di controllo intatte. Solo la media su molte pagine, confrontata con la media del controllo, fa emergere il segnale.

(n.b. lo stesso risultato si ottiene con una regressione, lm(delta ~ gruppo, data = w): il coefficiente di gruppotest vale 0,0745, con lo stesso p-value. È la forma che conviene quando vogliamo aggiungere altre variabili, per esempio il tipo di pagina, e il motivo per cui nei manuali la DiD compare quasi sempre come un modello di regressione.)

Come verifichiamo che il controllo sia un buon controllo?

Tutto il ragionamento si regge su un’ipotesi: che, senza la modifica, test e controllo si sarebbero mossi allo stesso modo. Si chiama ipotesi delle tendenze parallele (parallel trends), ed è il punto in cui uno split test sta in piedi o crolla.

Non possiamo verificarla nel periodo “dopo”, perché lì il controfattuale non lo vediamo. Ma possiamo controllarla nel periodo “prima”, dove nessuno ha toccato niente. Due strumenti, entrambi di estrema utilità.

Il primo è il grafico che abbiamo già visto: prima della modifica le linee devono sovrapporsi. Se nel periodo “prima” le pagine test crescevano già più del controllo, il sorteggio è andato storto, o le pagine non sono davvero omogenee, e qualunque differenza successiva è sospetta.

Il secondo è un test placebo: si finge che la modifica sia avvenuta a metà del periodo “prima” e si ripete l’analisi. Siccome in quel momento non è successo niente, la DiD deve risultare nulla. Lo verifico in R, spostando la finta modifica al giorno 29:

pre <- subset(clic, giorno < 57)
pre$periodo <- ifelse(pre$giorno >= 29, "dopo", "prima")
t.test(delta ~ gruppo, data = variazioni(pre))

# t = 0.071079, df = 197.57, p-value = 0.9434

Differenza praticamente zero (−0,09%), p-value 0,94: il controllo si comportava come il test quando nessuno li distingueva. È esattamente quello che vogliamo vedere. Un test placebo “significativo”, al contrario, sarebbe un campanello d’allarme da non ignorare.

Un avvertimento: la differenza delle differenze elimina tutto ciò che colpisce test e controllo allo stesso modo, ma niente di ciò che li colpisce in modo diverso.
Se il core update penalizza proprio il tipo di title che abbiamo introdotto, o se nel frattempo un concorrente attacca solo alcune delle nostre categorie, la DiD attribuirà alla modifica anche quell’effetto. Il sorteggio rende improbabili queste coincidenze, non impossibili: per questo il controllo delle tendenze parallele e il test placebo non sono un optional.

Quante pagine servono, e per quanto tempo?

Nel nostro esempio è andata bene: p-value 0,036, effetto ritrovato. Ma ci è andata bene quanto? Lo scopriamo con la power analysis: la probabilità che il test si accorga di un effetto che c’è davvero.

Per calcolarla servono tre ingredienti: l’effetto che vogliamo scoprire (+8%, cioè log(1,08) = 0,077 sulla scala logaritmica), il numero di pagine e la variabilità delle variazioni pagina per pagina dentro ciascun gruppo. Quest’ultima la stimiamo dai dati del test: vale circa 0,25. Calcolo in R:

s <- sqrt(mean(tapply(w$delta, w$gruppo, var)))   # dev. standard dentro i gruppi
s
# [1] 0.2497

power.t.test(n = 100, delta = log(1.08), sd = s)$power
# [1] 0.583

power.t.test(power = 0.8, delta = log(1.08), sd = s)$n
# [1] 166.3

Con 100 pagine per gruppo, la potenza è del 58%: poco più di una moneta lanciata. Se ripetessimo lo stesso identico esperimento molte volte, in quattro casi su dieci concluderemmo che la modifica non serve, pur sapendo che funziona. Per arrivare all’80%, la soglia abituale, servono circa 166 pagine per gruppo, oltre 330 in tutto.

Potenza del test al crescere del numero di pagine, per tre possibili effetti. Un +15% si scopre già con un centinaio di pagine; un +8% ne chiede più di 300 per arrivare all&apos;80%; un +5% oltre 800. Il nostro esempio, con 200 pagine, sta a metà della curva dell&apos;8%.
Potenza del test al crescere del numero di pagine, per tre possibili effetti. Un +15% si scopre già con un centinaio di pagine; un +8% ne chiede più di 300 per arrivare all’80%; un +5% oltre 800. Il nostro esempio, con 200 pagine, sta a metà della curva dell’8%.

La figura dà la misura del problema. Gli effetti tipici delle modifiche SEO sono piccoli, e gli effetti piccoli richiedono molte pagine. Un +15% si vede anche su un centinaio di schede; un +5%, che su un grande e-commerce vale comunque parecchio denaro, ne richiede più di 800.

Prima di partire, dunque, conviene fare il conto all’incontrario: quante pagine abbiamo? Con quel numero, quale effetto minimo possiamo sperare di vedere? La variabilità si stima dalle stesse pagine, confrontando due periodi passati lunghi quanto il test. Se l’effetto minimo rilevabile è più grande di quello che ragionevolmente ci aspettiamo, il test è sottodimensionato. In quel caso o si allarga a più pagine, o si accetta che dirà soltanto “non lo so”.

Quanto al tempo, due considerazioni. Google deve prima scansionare e rielaborare le pagine modificate, e su siti grandi può volerci più di una settimana: i primi giorni dopo la modifica vanno spesso esclusi dal confronto. Poi, un periodo “dopo” più lungo riduce il rumore giornaliero, ma non quello pagina per pagina, che è il grosso. Allungare il test aiuta meno che allargarlo: quattro-sei settimane sono di solito un buon compromesso.

Le insidie dello split test

Il metodo è solido, ma nella pratica quotidiana le occasioni per farsi ingannare non mancano.

La contaminazione. Le pagine test e controllo non vivono isolate: si linkano fra loro, condividono menu e pagine di categoria. Se la modifica cambia i link interni, per esempio aggiungendo un blocco di prodotti correlati, l’effetto si travasa anche sul controllo, e la DiD lo sottostima.

Le pagine con pochissimi clic. Una pagina che passa da 2 a 0 clic al giorno ha una variazione infinita sulla scala logaritmica. Conviene escludere in partenza le pagine sotto una soglia minima di traffico, oppure lavorare sulle impressioni, che sono molto più numerose.

La metrica scelta a posteriori. Clic, impressioni, CTR, posizione media, conversioni: se le guardiamo tutte, qualcuna risulterà “significativa” per caso. La metrica principale si decide prima, e le altre restano informazioni di contorno. È il problema dei confronti multipli, e merita un discorso a sé.

I gruppi troppo diversi. Se il sito ha pagine con volumi molto diversi, conviene randomizzare a coppie o a blocchi (per categoria, per fascia di traffico), come abbiamo fatto. Un sorteggio semplice su 40 pagine può produrre, per pura sfortuna, un gruppo test con tutte le pagine stagionali.

E quando non si può sorteggiare?

Fin qui abbiamo dato per scontato di poter scegliere quali pagine modificare. Ma alcune delle decisioni più importanti, e più rischiose, non si possono dividere in due: una migrazione di dominio o di piattaforma, un nuovo template per tutto il sito, il passaggio a HTTPS. In questi casi il gruppo di controllo interno non esiste, perché tutte le pagine cambiano insieme. E il confronto prima/dopo resta inaffidabile come prima.

L’idea della differenza delle differenze, però, sopravvive. Se non possiamo osservare il controfattuale dentro il sito, possiamo ricostruirlo da fuori: da serie che vivono le stesse stagioni e gli stessi aggiornamenti dell’algoritmo, ma che la migrazione non tocca. Un sito gemello dello stesso gruppo, una sezione del sito rimasta fuori dalla migrazione, l’indice di Google Trends per i temi principali. Nel periodo prima stimiamo come il nostro traffico si muove insieme a queste serie; nel periodo dopo usiamo la stessa relazione per prevedere quanto traffico avremmo avuto senza la migrazione. La differenza fra osservato e previsto è l’effetto.

Facciamo un esempio. Simulo 180 giorni di clic di un sito che migra il giorno 121, perdendo il 12% del traffico; dieci giorni dopo un core update toglie il 25% a tutto il settore. Accanto ci sono due serie di controllo: un sito gemello non migrato e Google Trends.

set.seed(3)
giorni <- 1:180; migrazione <- 121                        # 4 mesi prima, 2 dopo
settore <- exp(cumsum(rnorm(180, 0, 0.02))) *             # andamento del settore
           ifelse(giorni %% 7 %in% c(6, 0), 0.75, 1) *     # weekend
           (1 - 0.25 * (giorni > 130))                     # core update di settore
rumore <- function(sd) exp(as.numeric(arima.sim(list(ar = 0.6), 180, sd = sd)))
sito    <- 800 * settore * rumore(0.05) * ifelse(giorni >= migrazione, 0.88, 1)
gemello <- 300 * settore * rumore(0.03)                    # sito gemello, non migrato
trends  <- 60  * settore * rumore(0.05)                    # Google Trends del tema
prima <- giorni < migrazione

mean(sito[!prima]) / mean(sito[prima]) - 1
# [1] -0.273

Il prima/dopo dice −27%: più del doppio dell’effetto vero, perché mette sul conto della migrazione anche il core update. Costruisco allora il controfattuale con una regressione multipla sul periodo prima, in scala logaritmica, e lo proietto sul periodo dopo:

m <- lm(log(sito) ~ log(gemello) + log(trends), subset = prima)
summary(m)$r.squared
# [1] 0.792

previsto <- predict(m, newdata = data.frame(gemello, trends))
exp(mean(log(sito[!prima]) - previsto[!prima])) - 1
# [1] -0.125

sum(exp(previsto[!prima])) - sum(sito[!prima])   # clic persi in 60 giorni
# [1] 4532.7

Le due serie di controllo spiegano il 79% delle oscillazioni giornaliere del sito prima della migrazione. Il confronto con il controfattuale stima un effetto di −12,5%, a un passo dal −12% vero: circa 4.500 clic persi in due mesi, su 36.400 attesi.

Clic giornalieri del sito (media mobile a 7 giorni) e controfattuale previsto dalle serie di controllo. Prima della migrazione le due linee si inseguono; dopo, il core update fa crollare entrambe, ma i clic osservati restano sotto il previsto. L&apos;area colorata sono i clic persi per la migrazione, non per l&apos;algoritmo.
Clic giornalieri del sito (media mobile a 7 giorni) e controfattuale previsto dalle serie di controllo. Prima della migrazione le due linee si inseguono; dopo, il core update fa crollare entrambe, ma i clic osservati restano sotto il previsto. L’area colorata sono i clic persi per la migrazione, non per l’algoritmo.

Quanto fidarsi di quel −12,5%? I giorni di una serie storica non sono indipendenti fra loro (un giorno buono tende a essere seguito da un altro giorno buono), e un intervallo calcolato come se lo fossero sarebbe troppo ottimista. Anche qui ci viene in aiuto il placebo, questa volta nel tempo: fingiamo che la migrazione sia avvenuta in vari momenti del periodo prima, stimiamo il modello sui giorni precedenti e misuriamo il finto “effetto” sui 30 giorni successivi.

placebo <- sapply(seq(40, 90, by = 5), function(k) {
  mk <- lm(log(sito) ~ log(gemello) + log(trends), subset = giorni < k)
  pk <- predict(mk, newdata = data.frame(gemello, trends))
  exp(mean(log(sito[k:(k + 29)]) - pk[k:(k + 29)])) - 1
})
range(placebo)
# [1] -0.018  0.028

Quando non è successo niente, il metodo “trova” effetti fra −1,8% e +2,8%: è la sua soglia di rumore. Il −12,5% della migrazione sta nettamente fuori da quella fascia, e possiamo considerarlo reale.

Il metodo che abbiamo appena costruito a mano è il cuore di CausalImpact, il pacchetto R sviluppato da Google per questo problema. Al posto della regressione semplice usa un modello bayesiano per serie storiche, che tiene conto di trend, stagionalità e dipendenza fra i giorni, e restituisce direttamente l’effetto con il suo intervallo di credibilità. La logica è la stessa: un controfattuale costruito da serie non toccate dall’intervento.

Un avvertimento: il controfattuale è credibile solo quanto le serie di controllo.
Devono muoversi con il sito prima dell’intervento (lo si verifica con l’R² e con il placebo), e soprattutto non devono essere toccate dall’intervento stesso. Se la migrazione cambia anche il sito gemello, o se la usiamo come controllo per una campagna che ha spinto anche le ricerche su Google Trends, il controfattuale si sposta insieme al sito e l’effetto sparisce dai conti.

Domande frequenti

Cos’è uno split test SEO?

È un esperimento che misura l’effetto di una modifica sul traffico organico dividendo a caso un insieme di pagine simili in due gruppi: le pagine test ricevono la modifica, quelle di controllo restano invariate. Confrontando come cambiano i due gruppi si separa l’effetto della modifica da stagionalità, aggiornamenti dell’algoritmo e concorrenza.

Che differenza c’è tra split test SEO e A/B test?

L’A/B test divide gli utenti: ognuno vede una delle due versioni della stessa pagina. Lo split test SEO divide le pagine, perché Google deve vedere una sola versione di ciascuna. Per questo serve un sito con molte pagine dello stesso tipo.

Quanto deve durare uno split test SEO?

Di solito sei-otto settimane di dati prima della modifica e quattro-sei settimane dopo, escludendo i primi giorni in cui Google sta ancora scansionando le pagine modificate. Allungare il periodo aiuta meno che aumentare il numero di pagine.

Si può fare uno split test con poche pagine?

Con poche decine di pagine si scoprono solo effetti grandi, del 15-20% o più. Per effetti tipici del 5-10% servono di norma alcune centinaia di pagine: conviene calcolare la potenza del test prima di partire.

Come si misura l’impatto di una migrazione SEO?

Confrontando il traffico dopo la migrazione con un controfattuale: una previsione di quanto traffico ci sarebbe stato senza migrazione, costruita da serie che la migrazione non tocca (un sito gemello, una sezione non migrata, Google Trends). Il pacchetto R CausalImpact automatizza questo approccio.

Cosa si può testare con uno split test SEO?

Qualunque modifica applicabile a un gruppo di pagine dello stesso template: title e meta description, struttura degli heading, dati strutturati, blocchi di contenuto, link interni, velocità di caricamento. Le modifiche che toccano tutto il sito insieme, come una migrazione, non si prestano: manca un gruppo di controllo interno, e serve un controfattuale costruito da fuori.

Prova tu

Il codice dell’esempio è completo: basta copiarlo in R e cambiare un parametro alla volta.

  • Si porta effetto a 1 (la modifica non fa nulla) e si ripete l’analisi con qualche set.seed() diverso. Quante volte il t-test dichiara, per sbaglio, un effetto significativo? (Dovrebbe succedere circa 5 volte su 100.)
  • Si riporta effetto a 1,08 e si raddoppiano le pagine, n_pagine <- 400: come si stringe l’intervallo di confidenza?
  • Si “rompe” il sorteggio, mettendo nel gruppo test le 100 pagine con più traffico invece di sorteggiarle, e si dà alle pagine grandi una deriva più favorevole. Cosa dice il test placebo?

Per l’ultimo esercizio, un indizio: è il modo più rapido per capire perché il sorteggio non è un dettaglio burocratico.


C’è una domanda che abbiamo sfiorato senza affrontarla. In uno split test guardiamo di solito una metrica principale, ma la tentazione di guardarne dieci è fortissima: clic, impressioni, CTR, posizione, e poi ogni categoria di pagine per conto suo. Ogni sguardo in più è un’occasione in più per trovare un effetto “significativo” che non esiste. Quanto in fretta si accumulano questi falsi allarmi, e come si correggono, è il tema dei confronti multipli: il prossimo passo del nostro viaggio tra le trappole dei dati.


Per approfondire

Sulla sperimentazione online il riferimento, in inglese, è Trustworthy Online Controlled Experiments di Ron Kohavi, Diane Tang e Ya Xu: dalla scelta delle metriche alle insidie dei gruppi di controllo, è scritto da chi ha guidato gli esperimenti di Microsoft, Google e LinkedIn. Per la differenza delle differenze come strumento econometrico, con le sue ipotesi e le sue estensioni, consiglio Introduzione all’econometria di Stock e Watson, che le dedica una trattazione chiara e piena di esempi.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *