<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>paologironi blog</title>
	<atom:link href="https://www.gironi.it/blog/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.gironi.it/blog</link>
	<description>Appunti sparsi di (retro) informatica, analisi dei dati, statistica, seo, e cose che cambiano</description>
	<lastBuildDate>Mon, 13 Jul 2026 07:05:10 +0000</lastBuildDate>
	<language>it-IT</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	
	<item>
		<title>Clustering delle keyword: raggruppare migliaia di query con K-means e clustering gerarchico</title>
		<link>https://www.gironi.it/blog/clustering-keyword/</link>
					<comments>https://www.gironi.it/blog/clustering-keyword/#respond</comments>
		
		<dc:creator><![CDATA[Paolo Gironi]]></dc:creator>
		<pubDate>Tue, 30 Jun 2026 06:43:17 +0000</pubDate>
				<category><![CDATA[statistica]]></category>
		<guid isPermaLink="false">https://www.gironi.it/blog/?p=3946</guid>

					<description><![CDATA[Capita spesso che si esporti l&#8217;elenco delle keyword da Search Console o da un tool, e ci si ritrovi davanti a migliaia di righe. Tremila, diecimila query. Leggerle una per una è impensabile, e raggrupparle a mano &#8220;a sentimento&#8221; è un lavoro lento, soggettivo e impossibile da rifare.Eppure quel raggruppamento serve: vogliamo capire quali grandi &#8230; <a href="https://www.gironi.it/blog/clustering-keyword/" class="more-link">Leggi tutto<span class="screen-reader-text"> "Clustering delle keyword: raggruppare migliaia di query con K-means e clustering gerarchico"</span></a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Capita spesso che si esporti l&#8217;elenco delle keyword da Search Console o da un tool, e ci si ritrovi davanti a migliaia di righe. Tremila, diecimila query. <br>Leggerle una per una è impensabile, e raggrupparle a mano &#8220;a sentimento&#8221; è un lavoro lento, soggettivo e impossibile da rifare.<br>Eppure quel raggruppamento serve: vogliamo capire quali grandi famiglie di ricerche esistono nel nostro mercato, per decidere dove creare contenuti, quali pagine costruire, su cosa puntare.<br>La domanda è: possiamo lasciare che siano i dati a rivelarci i gruppi, invece di imporli noi? Trasformare quella montagna di query in pochi insiemi omogenei è il lavoro del <em><strong>clustering delle keyword</strong></em>.</p>



<p class="wp-block-paragraph">Abbiamo già affrontato un problema vicino, classificare l&#8217;<a href="https://www.gironi.it/blog/naive-bayes-intento-ricerca/">intento di una query con il Naive Bayes</a> — ma lì avevamo un ingrediente che oggi ci manca: un insieme di esempi <em>già etichettati</em> da cui imparare. Qui le etichette non ce le ha date nessuno. <br>È il territorio del <em>clustering</em>, uno degli strumenti più usati del <a href="https://www.gironi.it/blog/machine-learning-una-guida-per-i-principianti/">machine learning</a>, e in questo articolo lo costruiamo in R con i suoi due algoritmi classici: il <em><strong>K-means</strong></em> e il <strong>clustering gerarchico</strong>.</p>



<span id="more-3946"></span>



<p class="wp-block-paragraph"><strong>Di cosa parleremo</strong>:</p>



<ul class="wp-block-list">
<li><a href="#senza-etichette">Raggruppare senza etichette: l&#8217;idea del clustering</a></li>



<li><a href="#k-means">K-means: i centroidi e il problema di scegliere k</a></li>



<li><a href="#leggere-cluster">Leggere i cluster: chi sono questi gruppi?</a></li>



<li><a href="#gerarchico">Clustering gerarchico: il dendrogramma</a></li>



<li><a href="#trappole">Quale metodo, e le trappole</a></li>



<li><a href="#prova-tu">Prova tu</a></li>



<li><a href="#per-approfondire">Per approfondire</a></li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="senza-etichette" class="wp-block-heading">Raggruppare senza etichette: l&#8217;idea del clustering</h2>



<p class="wp-block-paragraph">La differenza con il Naive Bayes è di principio, non di dettaglio. Lì infatti facevamo <em>apprendimento supervisionato</em>: avevamo query già marcate come informazionali, navigazionali o transazionali, e insegnavamo all&#8217;algoritmo a riconoscere le nuove. Qui facciamo <em>apprendimento non supervisionato</em>: nessuno ci ha detto quali e quanti gruppi esistano. <strong>Il clustering non verifica un&#8217;etichetta che già conosciamo: cerca una struttura che non sapevamo ci fosse.</strong></p>



<p class="wp-block-paragraph">Per raggruppare servono due cose. La prima è descrivere ogni keyword con dei numeri: nel nostro esempio useremo il volume di ricerca, il costo per clic (<em>cpc</em>), la posizione media e il numero di parole.<br>La seconda è una nozione di <em>distanza</em>: due keyword sono &#8220;vicine&#8221; se i loro numeri si somigliano. La <strong>distanza più comune è quella euclidea</strong>, la stessa che useremmo su una mappa, solo calcolata in uno spazio a quattro dimensioni (una per ogni metrica).</p>



<p class="wp-block-paragraph">C&#8217;è però una trappola da disinnescare subito. Il volume si misura in decine di migliaia, il cpc in centesimi di euro: lasciate così, le distanze sarebbero dominate dal volume, e il cpc non conterebbe quasi nulla.<br><strong>Prima di calcolare qualsiasi distanza dobbiamo mettere tutte le variabili sulla stessa scala</strong>, standardizzandole — in R con la funzione <code>scale()</code>, che a ogni colonna sottrae la media e la divide per la deviazione standard. Solo a quel punto un euro di differenza nel cpc e diecimila ricerche di differenza nel volume &#8220;pesano&#8221; allo stesso modo.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="k-means" class="wp-block-heading">K-means: i centroidi e il problema di scegliere k</h2>



<p class="wp-block-paragraph">L&#8217;idea del K-means è quasi ingenua nella sua semplicità. Decidiamo in quanti gruppi (k) vogliamo dividere i dati; l&#8217;algoritmo piazza k punti-rappresentanti, i <em>centroidi</em>, e poi ripete due passi finché le cose si stabilizzano: assegna ogni keyword al centroide più vicino, poi sposta ogni centroide nel centro delle keyword che gli sono state assegnate. Ad ogni giro i gruppi diventano un po&#8217; più coesi, finché non si muovono più.</p>



<p class="wp-block-paragraph">Ciò che l&#8217;algoritmo cerca di minimizzare, a parole, è la dispersione interna dei gruppi: la somma delle distanze (al quadrato) di ogni punto dal centroide del proprio cluster. In formula:</p>



\( \text{WCSS} = \sum_{k=1}^{K} \sum_{x \in C_k} \| x-\mu_k \|^2 \\ \)



<p class="wp-block-paragraph">dove \( C_k \) è il k-esimo cluster, \( \mu_k \) il suo centroide e la doppia sommatoria scorre tutti i punti di tutti i gruppi. Più la WCSS (<em>within-cluster sum of squares</em>) è bassa, più i gruppi sono compatti.</p>



<p class="wp-block-paragraph">Resta il punto dolente: k lo dobbiamo decidere noi, prima di iniziare. Un aiuto viene dal <em>metodo del gomito</em>: proviamo diversi valori di k e guardiamo come cala la WCSS. All&#8217;inizio aggiungere un cluster aiuta molto, poi i miglioramenti si fanno marginali; il &#8220;gomito&#8221; della curva — il punto dove la discesa si appiattisce — suggerisce un k ragionevole. Costruisco la tabella di keyword e calcolo la WCSS da 1 a 6 gruppi in R:</p>



<pre class="wp-block-code"><code>kw &lt;- data.frame(
  keyword = c("scarpe running","scarpe running uomo","nike pegasus","nike pegasus 40",
              "migliori scarpe trail 2026","come scegliere scarpe running",
              "scarpe running offerta","comprare scarpe trail online",
              "differenza scarpe trail e strada","scarpe running pronazione",
              "asics gel nimbus","saucony endorphin","recensione scarpe trail",
              "scarpe running scontate","negozio scarpe running milano"),
  volume   = c(40000,18000,12000,8000,2400,1900,3200,880,1300,2100,9000,4000,1500,2600,720),
  cpc      = c(0.45,0.55,0.30,0.35,0.40,0.10,0.95,1.10,0.08,0.30,0.28,0.33,0.15,0.90,0.85),
  posizione= c(3.1,4.2,2.0,5.5,8.1,11.2,6.0,9.4,14.0,7.3,2.5,6.8,12.1,5.9,4.7),
  n_parole = c(2,3,2,3,5,5,3,4,6,3,3,2,3,3,4)
)

# standardizzo le quattro metriche (scale diverse -&gt; stesso peso)
X &lt;- scale(kw&#091;, c("volume","cpc","posizione","n_parole")])

# metodo del gomito: WCSS per k da 1 a 6
set.seed(1)
wss &lt;- sapply(1:6, function(k) kmeans(X, centers = k, nstart = 10)$tot.withinss)
round(wss, 1)
# &#091;1] 56.0 34.4 20.7 12.4  8.7  6.9</code></pre>



<div class="wp-block-group has-background" style="background-color:#f5f7f9;margin-top:2.5rem;margin-bottom:2.5rem;padding-top:1.5rem;padding-right:1.5rem;padding-bottom:1rem;padding-left:1.5rem"><div class="wp-block-group__inner-container is-layout-constrained wp-container-core-group-is-layout-eed7543b wp-block-group-is-layout-constrained">
<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="975" height="600" src="https://www.gironi.it/blog/wp-content/uploads/2026/07/clustering-elbow.png" alt="Il metodo del gomito: la dispersione interna (WCSS) crolla fino a tre gruppi e poi si appiattisce. Il gomito della curva suggerisce k = 3." class="wp-image-4024" srcset="https://www.gironi.it/blog/wp-content/uploads/2026/07/clustering-elbow.png 975w, https://www.gironi.it/blog/wp-content/uploads/2026/07/clustering-elbow-300x185.png 300w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /><figcaption class="wp-element-caption">Il metodo del gomito: la dispersione interna (WCSS) crolla fino a tre gruppi e poi si appiattisce. Il gomito della curva suggerisce k = 3.</figcaption></figure>
</div></div>



<p class="wp-block-paragraph">Il calo è ripido fino a tre gruppi (56 → 34 → 21) e poi rallenta nettamente (21 → 12 → 9 → 7). Il gomito non è mai una linea netta — è una lettura, non un teorema — ma qui indica con ragionevolezza <strong>k = 3</strong>. Lancio allora il K-means con tre centroidi:</p>



<pre class="wp-block-code"><code>set.seed(1)
km &lt;- kmeans(X, centers = 3, nstart = 25)
kw$cluster &lt;- km$cluster
table(km$cluster)
# 1 2 3
# 4 7 4</code></pre>



<p class="wp-block-paragraph">n.b. l&#8217;argomento <code>nstart = 25</code> fa ripartire l&#8217;algoritmo 25 volte da centroidi iniziali diversi, tenendo la soluzione migliore: il K-means può infatti incagliarsi in un minimo locale a seconda di dove parte, e ripartire più volte è la difesa standard. Il <code>set.seed(1)</code> serve solo a rendere l&#8217;esempio riproducibile (compresa la numerazione dei cluster, che di per sé è arbitraria).</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="leggere-cluster" class="wp-block-heading">Leggere i cluster: chi sono questi gruppi?</h2>



<p class="wp-block-paragraph">Avere tre gruppi non serve a niente finché non capiamo <em>cosa</em> rappresentano. Il modo più diretto è guardare le metriche medie di ciascun cluster.<br>Le calcolo sulla scala originale (non quella standardizzata, che è illeggibile):</p>



<pre class="wp-block-code"><code>aggregate(kw&#091;, c("volume","cpc","posizione","n_parole")],
          by = list(cluster = kw$cluster), FUN = mean)
#   cluster volume  cpc posizione n_parole
# 1       1   1775 0.18     11.35     4.75
# 2       2  13300 0.37      4.49     2.57
# 3       3   1850 0.95      6.50     3.50</code></pre>



<div class="wp-block-group has-background" style="background-color:#f5f7f9;margin-top:2.5rem;margin-bottom:2.5rem;padding-top:1.5rem;padding-right:1.5rem;padding-bottom:1rem;padding-left:1.5rem"><div class="wp-block-group__inner-container is-layout-constrained wp-container-core-group-is-layout-eed7543b wp-block-group-is-layout-constrained">
<figure class="wp-block-image size-large"><img decoding="async" width="975" height="600" src="https://www.gironi.it/blog/wp-content/uploads/2026/07/clustering-profilo.png" alt="L'impronta dei tre cluster sulle quattro metriche standardizzate: le teste svettano sul volume, il gruppo commerciale sul cpc, l'informazionale su posizione e numero di parole. È la tabella delle medie, letta a colpo d'occhio." class="wp-image-4025" srcset="https://www.gironi.it/blog/wp-content/uploads/2026/07/clustering-profilo.png 975w, https://www.gironi.it/blog/wp-content/uploads/2026/07/clustering-profilo-300x185.png 300w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /><figcaption class="wp-element-caption">L&#8217;impronta dei tre cluster sulle quattro metriche standardizzate: le teste svettano sul volume, il gruppo commerciale sul cpc, l&#8217;informazionale su posizione e numero di parole. È la tabella delle medie, letta a colpo d&#8217;occhio.</figcaption></figure>
</div></div>



<p class="wp-block-paragraph">Adesso i gruppi parlano.<br>Il <strong>cluster 2</strong> raccoglie le query a volume altissimo (in media 13.300 ricerche), corte (due-tre parole), ben posizionate e con cpc modesto: sono le <em>teste</em> generiche e di marca — &#8220;scarpe running&#8221;, &#8220;nike pegasus&#8221;, &#8220;asics gel nimbus&#8221;. Il <strong>cluster 1</strong> ha volumi bassi, query lunghe (quasi cinque parole), posizioni arretrate e cpc minimo: è il long-tail <em>informazionale</em> — &#8220;come scegliere scarpe running&#8221;, &#8220;differenza scarpe trail e strada&#8221;, &#8220;recensione scarpe trail&#8221;. Il <strong>cluster 3</strong> si distingue per un cpc altissimo (0,95 €) e contiene le query a chiaro taglio <em>commerciale</em> — &#8220;scarpe running offerta&#8221;, &#8220;comprare scarpe trail online&#8221;, &#8220;scarpe running scontate&#8221;, &#8220;negozio scarpe running milano&#8221;.</p>



<p class="wp-block-paragraph">Vale la pena fermarsi un istante su cosa è appena successo. <strong>Senza dare all&#8217;algoritmo alcuna etichetta, i tre gruppi che emergono ricalcano da vicino una distinzione per intento — ricerca informativa, teste generiche e di marca, query commerciali — vicina a quella che con il Naive Bayes avevamo invece dovuto insegnargli a colpi di esempi.</strong><br>L&#8217;allineamento, attenzione, non è magia: emerge perché le metriche che abbiamo scelto (cpc, lunghezza, posizione) <em>tracciano indirettamente</em> l&#8217;intento, non perché il clustering lo conosca — di significato delle query, qui, non ne ha vista neppure una parola. La lettura resta comunque immediatamente operativa: il cluster informativo chiede articoli e guide, quello commerciale pagine prodotto e di offerta, quello ad alto volume pagine pilastro robuste.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="gerarchico" class="wp-block-heading">Clustering gerarchico: il dendrogramma</h2>



<p class="wp-block-paragraph">Il K-means ci ha costretti a scegliere k in anticipo. Il clustering <em>gerarchico</em> ribalta l&#8217;approccio: non decide nulla a priori, e costruisce invece un albero completo di raggruppamenti. Parte da ogni keyword come gruppo a sé, poi fonde via via le due più vicine, poi le due più vicine fra quelle rimaste, e così via fino a un unico grande gruppo. Il risultato è un <em>dendrogramma</em>: un albero che mostra a quale &#8220;altezza&#8221; (cioè a quale distanza) ogni fusione avviene.</p>



<p class="wp-block-paragraph">Lo costruisco in R calcolando prima la matrice delle distanze, poi l&#8217;albero:</p>



<pre class="wp-block-code"><code>d  &lt;- dist(X)                       # distanze euclidee fra le keyword standardizzate
hc &lt;- hclust(d, method = "ward.D2") # albero gerarchico (criterio di Ward)
plot(hc, labels = kw$keyword)       # il dendrogramma</code></pre>



<div class="wp-block-group has-background" style="background-color:#f5f7f9;margin-top:2.5rem;margin-bottom:2.5rem;padding-top:1.5rem;padding-right:1.5rem;padding-bottom:1rem;padding-left:1.5rem"><div class="wp-block-group__inner-container is-layout-constrained wp-container-core-group-is-layout-eed7543b wp-block-group-is-layout-constrained">
<figure class="wp-block-image size-large"><img decoding="async" width="1050" height="780" src="https://www.gironi.it/blog/wp-content/uploads/2026/07/clustering-dendrogramma.png" alt="Il dendrogramma del clustering gerarchico (criterio di Ward): l'albero delle fusioni fra le keyword. Tagliandolo a k = 3 si ritrovano le tre famiglie — teste, commerciale, informazionale — con confini un po' diversi da quelli del K-means." class="wp-image-4026" srcset="https://www.gironi.it/blog/wp-content/uploads/2026/07/clustering-dendrogramma.png 1050w, https://www.gironi.it/blog/wp-content/uploads/2026/07/clustering-dendrogramma-300x223.png 300w, https://www.gironi.it/blog/wp-content/uploads/2026/07/clustering-dendrogramma-1024x761.png 1024w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /><figcaption class="wp-element-caption">Il dendrogramma del clustering gerarchico (criterio di Ward): l&#8217;albero delle fusioni fra le keyword. Tagliandolo a k = 3 si ritrovano le tre famiglie — teste, commerciale, informazionale — con confini un po&#8217; diversi da quelli del K-means.</figcaption></figure>
</div></div>



<p class="wp-block-paragraph">Il bello del dendrogramma è che la decisione su <em>quanti</em> gruppi tenere la prendiamo <em>dopo</em> averlo visto, semplicemente &#8220;tagliandolo&#8221; a un&#8217;altezza scelta: un taglio basso lascia molti gruppetti, un taglio alto pochi gruppi grandi. Taglio a tre gruppi e confronto con il K-means:</p>



<pre class="wp-block-code"><code>kw$cluster_hc &lt;- cutree(hc, k = 3)
table(kw$cluster_hc)
# 1 2 3
# 8 3 4</code></pre>



<p class="wp-block-paragraph">La partizione non è identica a quella del K-means (qui i gruppi sono da 8, 3 e 4 keyword), ed è normale: i due metodi ottimizzano criteri diversi e su pochi dati le differenze si notano.<br>Ma la sostanza dei raggruppamenti — il blocco transazionale ad alto cpc, le teste ad alto volume, la coda informazionale — resta riconoscibile in entrambi. Quando due metodi diversi convergono sulla stessa storia, possiamo fidarci un po&#8217; di più di quella storia.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="trappole" class="wp-block-heading">Quale metodo, e le trappole</h2>



<p class="wp-block-paragraph">La scelta fra i due, nella pratica, segue poche regole semplici. Il <strong>K-means</strong> è veloce ed efficiente anche su decine di migliaia di keyword, ma vuole che gli si dica k e tende a costruire gruppi di forma &#8220;sferica&#8221; e di dimensioni simili. Il <strong>gerarchico</strong> non chiede k in anticipo e regala il dendrogramma — prezioso per <em>vedere</em> come i gruppi si annidano l&#8217;uno nell&#8217;altro — ma diventa pesante quando le keyword sono troppe. La pratica più comune: esplorare in gerarchico su un campione per farsi un&#8217;idea del numero di gruppi, poi applicare il K-means all&#8217;intero set con il k così individuato.</p>



<p class="has-light-gray-background-color has-background wp-block-paragraph">Un avvertimento, il più importante di tutti: <strong>il clustering trova sempre dei gruppi, anche quando non ce ne sono.</strong> Diamogli del rumore puro e ce lo dividerà diligentemente in k cluster ordinati. Il numero di gruppi, le metriche scelte per descrivere le keyword, la standardizzazione: sono tutte decisioni <em>nostre</em>, e ognuna cambia il risultato. Un raggruppamento non è una verità scoperta nei dati, è un&#8217;ipotesi di lavoro che ha senso solo se regge alla prova del buon senso di business. Se un cluster non riusciamo a spiegarlo a parole, probabilmente non esiste davvero.</p>



<p class="wp-block-paragraph">C&#8217;è poi una questione di dimensioni. Qui abbiamo usato quattro metriche, ma nella realtà operativa le variabili descrittive di una keyword possono essere molte di più, e con tante dimensioni le distanze perdono di significato (tutto tende a sembrare ugualmente lontano).<br>È esattamente il problema che l&#8217;<a href="https://www.gironi.it/blog/una-introduzione-allanalisi-delle-componenti-principali-pca/">analisi delle componenti principali</a> sa alleggerire, comprimendo molte metriche in poche componenti prima di passare il testimone al clustering.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="prova-tu" class="wp-block-heading">Prova tu</h2>



<p class="wp-block-paragraph">Il modo migliore per capire il clustering è vederlo cambiare risposta al variare delle scelte. Riprendendo il codice qui sopra:</p>



<ol class="wp-block-list">
<li>Salta la standardizzazione: lancia il K-means direttamente su <code>kw[, c("volume","cpc","posizione","n_parole")]</code> senza <code>scale()</code>. I gruppi crollano tutti sul volume? È la dimostrazione pratica di perché si standardizza.</li>



<li>Cambia k: prova con quattro o cinque gruppi e rileggi le medie. Il cluster informazionale si spezza in sotto-temi sensati o stai solo tagliando il rumore?</li>



<li>Cambia il criterio di fusione del gerarchico: <code>method = "complete"</code> o <code>"average"</code> al posto di <code>"ward.D2"</code>. Il dendrogramma cambia forma? E i gruppi tagliati a k=3?</li>
</ol>



<p class="wp-block-paragraph">Suggerimento: tieni sempre d&#8217;occhio le medie per cluster con <code>aggregate()</code>. È lì, e non nel codice, che si decide se un raggruppamento è utile o è solo una partizione elegante di niente.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p class="wp-block-paragraph">Abbiamo raggruppato le keyword in base a come si <em>comportano</em> — volume, costo, posizione.<br>Ma resta fuori la cosa che per un SEO conta di più: il loro <em>significato</em>. Due query possono avere metriche diverse e voler dire la stessa cosa, oppure metriche simili e intenti opposti. Raggruppare per senso, e non solo per numeri, significa trasformare il testo stesso delle query in qualcosa di misurabile: è il mestiere del <em>text mining</em>, dove le parole diventano vettori e la similarità si calcola sul linguaggio. Ed è da lì che ripartiremo.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 id="per-approfondire" class="wp-block-heading">Per approfondire</h3>



<p class="wp-block-paragraph">Se vuoi approfondire il clustering — K-means, metodi gerarchici, la scelta del numero di gruppi e le insidie dell&#8217;interpretazione — <em><a href="https://www.amazon.it/dp/1461471370?tag=consulenzeinf-21" rel="nofollow sponsored noopener" target="_blank">An Introduction to Statistical Learning</a></em> di James, Witten, Hastie e Tibshirani dedica all&#8217;apprendimento non supervisionato un capitolo limpido, con i laboratori in R che ricalcano esattamente i passi che abbiamo visto qui. È il riferimento che consiglio a chi vuole passare dall&#8217;esempio giocattolo al clustering su dati veri.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.gironi.it/blog/clustering-keyword/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>CTR atteso vs reale: trovare le pagine che rendono meno della loro posizione</title>
		<link>https://www.gironi.it/blog/ctr-atteso-vs-reale/</link>
					<comments>https://www.gironi.it/blog/ctr-atteso-vs-reale/#respond</comments>
		
		<dc:creator><![CDATA[Paolo Gironi]]></dc:creator>
		<pubDate>Mon, 29 Jun 2026 08:22:33 +0000</pubDate>
				<category><![CDATA[statistica]]></category>
		<guid isPermaLink="false">https://www.gironi.it/blog/?p=3941</guid>

					<description><![CDATA[Chi passa le giornate dentro Search Console conosce bene una situazione di questo tipo: una pagina è stabilmente in terza posizione, eppure i clic sono pochi, un CTR che parrebbe da fondo pagina.La domanda che ci poniamo, di solito, è quella sbagliata: non «quanti clic prende?», ma quella più scomoda — «quanti clic dovrebbe prendere, &#8230; <a href="https://www.gironi.it/blog/ctr-atteso-vs-reale/" class="more-link">Leggi tutto<span class="screen-reader-text"> "CTR atteso vs reale: trovare le pagine che rendono meno della loro posizione"</span></a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Chi passa le giornate dentro Search Console conosce bene una situazione di questo tipo: una pagina è stabilmente in terza posizione, eppure i clic sono pochi, un CTR che parrebbe da fondo pagina.<br>La domanda che ci poniamo, di solito, è quella sbagliata: non «quanti clic prende?», ma quella più scomoda — «quanti clic <em>dovrebbe</em> prendere, stando dov&#8217;è?». <br>Finché non abbiamo un termine di paragone, un CTR del 3% non dice niente: per la posizione 8 sarebbe ottimo, per la 2 un piccolo disastro.<br>Quello che ci manca, per giudicare, è un CTR atteso: il valore con cui confrontare quello reale.</p>



<p class="wp-block-paragraph">Abbiamo avuto modo di vedere, parlando di <a href="https://www.gironi.it/blog/correlazione/">correlazione</a>, che posizione e CTR si muovono insieme lungo una curva ripida; e che il passo successivo — usare una variabile per prevederne un&#8217;altra — è il mestiere della <a href="https://www.gironi.it/blog/regressione-lineare-semplice/">regressione lineare</a>.<br>Qui i due fili si annodano: trasformiamo quella curva in un <strong>CTR atteso</strong> e misuriamo, pagina per pagina, di quanto ciascuna se ne discosta. È il modo per smettere di leggere i CTR come numeri assoluti e iniziare a leggerli per quello che sono davvero: scostamenti da una norma.</p>



<span id="more-3941"></span>



<p class="wp-block-paragraph"><strong>Di cosa parleremo</strong>:</p>



<ul class="wp-block-list">
<li><a href="#perche-baseline">Perché un CTR, da solo, non significa niente</a></li>



<li><a href="#modellare-la-curva">Modellare la curva del CTR: tre strade</a></li>



<li><a href="#esempio">Un esempio con dati di Search Console</a></li>



<li><a href="#loess-nls">Le altre due strade: loess e nls al lavoro</a></li>



<li><a href="#residui">I residui: chi rende meno di quanto dovrebbe</a></li>



<li><a href="#interpretare">Leggere gli scostamenti senza farsi ingannare</a></li>



<li><a href="#prova-tu">Prova tu</a></li>



<li><a href="#per-approfondire">Per approfondire</a></li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="perche-baseline" class="wp-block-heading">Perché un CTR, da solo, non significa niente</h2>



<p class="wp-block-paragraph">Esistono tabelle di settore che dicono quanto &#8220;dovrebbe&#8221; valere il CTR di ogni posizione: la prima intorno al 25-30%, la seconda sotto la metà, e così via a scendere. Sono utili come orizzonte generale, ma per giudicare le <em>nostre</em> pagine portano fuori strada: il CTR dipende dal tipo di query (un marchio cliccatissimo o una ricerca informativa fredda), dal settore, da quanto la SERP è affollata di annunci e <em>rich snippet</em>.<br>Il CTR medio di &#8220;posizione 3&#8221; su un benchmark americano di e-commerce non ha quasi nulla da dire al nostro blog tecnico in italiano.</p>



<p class="wp-block-paragraph">La via d&#8217;uscita è smettere di confrontarci con una tabella esterna e costruirci la curva di riferimento <em>sui nostri stessi dati</em>.<br>Prendiamo tutte le pagine, le loro posizioni medie e i loro CTR, e tracciamo la curva che descrive l&#8217;andamento tipico del CTR al variare della posizione <em>per come funziona il nostro sito</em>. Quella curva diventa il metro: il CTR atteso di una pagina è il valore che la curva le assegna, data la sua posizione.<br>Lo scostamento fra il CTR reale e quel valore atteso è l&#8217;informazione che cercavamo.</p>



<p class="wp-block-paragraph"><strong>Un CTR ha senso solo accanto alla posizione che l&#8217;ha prodotto: è lo scostamento dalla curva, non il numero assoluto, a dirci se una pagina sta lavorando bene o sta lasciando clic sul tavolo.</strong></p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="modellare-la-curva" class="wp-block-heading">Modellare la curva del CTR: tre strade</h2>



<p class="wp-block-paragraph">Costruire la curva significa stimare una funzione che, data la posizione, restituisca il CTR atteso. La forma di quella curva la conosciamo già a occhio: parte alta, crolla a precipizio nelle prime posizioni e poi si appiattisce verso lo zero. Una retta non la descrive; serve qualcosa che curvi.</p>



<p class="wp-block-paragraph">C&#8217;è però un dettaglio che cambia tutto il modo di ragionare, ed è il tipo di scostamento che ci interessa.<br>A noi non importa che una pagina prenda &#8220;due punti di CTR in meno&#8221; dell&#8217;atteso: in cima alla SERP due punti sono briciole, in fondo sono un raddoppio. Ci interessa lo scostamento <em>moltiplicativo</em> — &#8220;rende la metà di quanto dovrebbe&#8221;, &#8220;rende il doppio&#8221;.<br>E lo scostamento moltiplicativo si maneggia bene su scala logaritmica, dove un rapporto diventa una differenza.</p>



<p class="wp-block-paragraph">La forma più naturale per una curva di questo tipo è la <strong>legge di potenza</strong>, cioè l&#8217;idea che il CTR sia proporzionale alla posizione elevata a un esponente negativo:</p>



\( \text{CTR} = a \cdot posizione^{b} \\ \)



<p class="wp-block-paragraph">dove \( a \) fissa il livello generale e \( b \) (negativo) governa la rapidità della discesa. Il bello arriva prendendo il logaritmo di entrambi i lati, che trasforma quella curva in una retta:</p>



\( \log(\text{CTR}) = \log(a) + b \cdot \log(posizione) \\ \)



<p class="wp-block-paragraph">In altri termini: il logaritmo del CTR è una funzione <em>lineare</em> del logaritmo della posizione. E stimare una retta è esattamente ciò che sappiamo fare con la regressione. Da qui, tre strade per costruire la curva.</p>



<p class="wp-block-paragraph">La prima, e quella che consiglio come cavallo di battaglia, è una <strong>regressione lineare sui logaritmi</strong> — <code>lm(log(ctr) ~ log(posizione))</code>. È in R base, è interpretabile (la pendenza \( b \) è l&#8217;elasticità del CTR alla posizione: di quanto cala in percentuale il CTR a ogni punto percentuale di posizione in più), e i suoi residui sono già su scala logaritmica, quindi <em>moltiplicativi</em>, proprio come ci serve.<br>Si estrapola anche a posizioni poco osservate, e la si può <strong>pesare per le impression</strong> (<code>weights = impression</code>), così le pagine con due clic in croce non sbilanciano la curva quanto quelle con decine di migliaia di visualizzazioni. È una scelta pragmatica più che il peso teoricamente ottimo (per una proporzione la varianza dipende anche dal CTR stesso), ma nella pratica funziona benissimo.</p>



<p class="wp-block-paragraph">La seconda è la <strong>regressione non lineare</strong> con <code>nls</code>, che stima direttamente \( a \) e \( b \) sulla scala naturale del CTR senza passare per i logaritmi. È un raffinamento elegante, ma va innescato con valori di partenza sensati (che si pescano proprio dalla regressione log-log) e su dati sporchi può non convergere. La tengo per quando serve un parametro pulito da mettere in un report, non come punto di partenza.</p>



<p class="wp-block-paragraph">La terza è il <strong>lisciamento locale</strong> con <code>loess</code>, che non impone nessuna forma alla curva e la lascia &#8220;disegnare ai dati&#8221;. È perfetto per <em>vedere</em> l&#8217;andamento a colpo d&#8217;occhio, ma traballa sulle code (poche pagine in prima posizione) e soprattutto non estrapola: fuori dall&#8217;intervallo osservato non sa che dire. È uno strumento esplorativo, non il modello su cui costruire i giudizi.</p>



<p class="wp-block-paragraph">Dunque: partiamo dalla regressione log-log pesata per le impression come modello di lavoro, la confrontiamo a occhio con un <code>loess</code> per controllare che non stiamo forzando una forma sbagliata, e passiamo a <code>nls</code> solo se ci serve l&#8217;esponente esplicito. Vediamola al lavoro.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="esempio" class="wp-block-heading">Un esempio con dati di Search Console</h2>



<p class="wp-block-paragraph">Partiamo da un estratto come quello che chiunque può scaricare da Search Console: una riga per pagina, con le impression, i clic e la posizione media. Nella realtà il CTR è il rapporto fra clic e impression; qui, trattandosi di dati d&#8217;esempio, facciamo il percorso inverso — fissiamo un CTR plausibile e ricostruiamo i clic.<br>Costruisco la tabella in R con dodici pagine di esempio (con dentro, di proposito, un paio di casi anomali):</p>



<pre class="wp-block-code"><code>gsc &lt;- data.frame(
  pagina     = c("/guida-seo-tecnica","/checklist-audit-seo",
                 "/guida-keyword-research","/calcolatore-roi-campagne",
                 "/tutorial-google-analytics","/glossario-statistica",
                 "/recensione-tool-seo","/guida-link-building",
                 "/analisi-dei-competitor","/modello-attribuzione",
                 "/report-posizionamento","/ottimizzazione-meta-tag"),
  impression = c(  9800, 5400, 12500, 2100, 7600, 1500,
                   8300, 4200,  6100,  900, 3300, 1800),
  posizione  = c(  1.3,  2.1,   3.4,  4.0,  4.6,  5.2,
                   2.8,  6.1,   7.0,  8.3,  9.1, 10.2)
)
# CTR osservato (in genere lo si calcola come click / impression)
gsc$ctr   &lt;- c(0.232, 0.150, 0.034, 0.071, 0.066, 0.060,
               0.171, 0.048, 0.041, 0.012, 0.031, 0.028)
gsc$click &lt;- round(gsc$impression * gsc$ctr)</code></pre>



<p class="wp-block-paragraph">Stimo ora la curva del CTR atteso con la regressione sui logaritmi, pesando ogni pagina per le sue impression:</p>



<pre class="wp-block-code"><code>fit &lt;- lm(log(ctr) ~ log(posizione), data = gsc, weights = impression)
round(coef(fit), 3)
# (Intercept)  log(posizione)
#      -1.240          -1.088</code></pre>



<p class="wp-block-paragraph">La pendenza è <strong>−1,088</strong>: un valore vicino a −1 racconta una curva quasi inversamente proporzionale, in cui raddoppiare la posizione (passare, diciamo, dalla 3 alla 6) taglia il CTR all&#8217;incirca della metà.<br>È la stessa caduta ripida che avevamo intravisto misurando la correlazione, ora però scritta in una formula che possiamo <em>interrogare</em>: dato un numero di posizione, ci restituisce il CTR tipico che quella posizione comporta sul nostro sito.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="loess-nls" class="wp-block-heading">Le altre due strade: loess e nls al lavoro</h2>



<p class="wp-block-paragraph">Abbiamo scelto la regressione log-log come modello di lavoro, ma le tre strade le avevamo promesse tutte. Vale la pena vederle davvero all&#8217;opera sugli stessi dodici dati, per capire cosa aggiungono le altre due — e soprattutto dove inciampano.</p>



<p class="wp-block-paragraph">La <strong>regressione non lineare</strong> stima \( a \) e \( b \) direttamente sulla scala del CTR, senza passare per i logaritmi. Va innescata con valori di partenza sensati, che peschiamo proprio dalla log-log appena stimata: l&#8217;intercetta riportata in scala naturale con l&#8217;<code>exp</code> è la nostra \( a \), la pendenza è la nostra \( b \). La faccio partire in R:</p>



<pre class="wp-block-code"><code>start &lt;- list(a = exp(coef(fit)&#091;1]), b = coef(fit)&#091;2])  # innesco dalla log-log
fit_nls &lt;- nls(ctr ~ a * posizione^b, data = gsc,
               weights = impression, start = start)
round(coef(fit_nls), 3)
#      a       b
#  0.315  -1.073</code></pre>



<p class="wp-block-paragraph">Converge, e restituisce un esponente <strong>−1,073</strong>, praticamente lo stesso della log-log, ma con un regalo in più: ora \( a \) e \( b \) sono numeri leggibili sulla scala vera del CTR. Un \( a \) di 0,315 dice, in soldoni, che in cima alla SERP il CTR tipico è attorno al 31% — esattamente il genere di parametro pulito da mettere in un report.<br>Il prezzo lo paghiamo in fragilità: senza quei valori di partenza, o su dati più rumorosi, <code>nls</code> può non convergere affatto e restituirci soltanto un errore.</p>



<p class="wp-block-paragraph">Il <strong>lisciamento locale</strong> con <code>loess</code> fa l&#8217;opposto: non impone nessuna forma, lascia disegnare la curva ai dati. Lo stimo e gli chiedo il CTR atteso a qualche posizione, inclusa la prima:</p>



<pre class="wp-block-code"><code>fit_lo &lt;- loess(ctr ~ posizione, data = gsc, span = 0.9)
round(predict(fit_lo, data.frame(posizione = c(1, 2, 3, 5))), 3)
# &#091;1]    NA 0.173 0.111 0.029</code></pre>



<p class="wp-block-paragraph">Ed ecco il limite in un solo output: alla posizione 1, <code>loess</code> restituisce <strong><code>NA</code></strong>. Il minimo che i nostri dati osservano è 1,3, e fuori da quell&#8217;intervallo <code>loess</code> si rifiuta di pronunciarsi — <em>non estrapola</em>.<br>Per una curva di CTR è un difetto serio: la prima posizione, quella che ci interessa di più, è spesso la meno popolata, ed è proprio lì che il metodo locale ci lascia a bocca asciutta.</p>



<p class="wp-block-paragraph">Messe sullo stesso grafico, le tre strade raccontano tutto in un colpo d&#8217;occhio:</p>



<div class="wp-block-group has-background" style="background-color:#f5f7f9;margin-top:2.5rem;margin-bottom:2.5rem;padding-top:1.5rem;padding-right:1.5rem;padding-bottom:1rem;padding-left:1.5rem"><div class="wp-block-group__inner-container is-layout-constrained wp-container-core-group-is-layout-eed7543b wp-block-group-is-layout-constrained">
<figure class="wp-block-image size-large"><img decoding="async" width="1020" height="690" src="https://www.gironi.it/blog/wp-content/uploads/2026/07/ctr-tre-metodi.png" alt="Le tre stime della curva del CTR atteso sui dodici dati d'esempio. Al centro, dove le pagine abbondano, quasi coincidono; sulle code divergono. Il loess (viola) si ferma alla posizione 1,3 — non estrapola — mentre lm (blu) e nls (arancio) proseguono anche sotto la prima posizione. La curva nls sta un filo sopra la log-log perché ne stima il livello direttamente sulla scala del CTR." class="wp-image-4074" srcset="https://www.gironi.it/blog/wp-content/uploads/2026/07/ctr-tre-metodi.png 1020w, https://www.gironi.it/blog/wp-content/uploads/2026/07/ctr-tre-metodi-300x203.png 300w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /><figcaption class="wp-element-caption">Le tre stime della curva del CTR atteso sui dodici dati d&#8217;esempio. Al centro, dove le pagine abbondano, quasi coincidono; sulle code divergono. Il loess (viola) si ferma alla posizione 1,3 — non estrapola — mentre lm (blu) e nls (arancio) proseguono anche sotto la prima posizione. La curva nls sta un filo sopra la log-log perché ne stima il livello direttamente sulla scala del CTR.</figcaption></figure>
</div></div>



<p class="wp-block-paragraph">Al centro, dove le pagine abbondano, le tre curve quasi coincidono: qualunque metodo va bene quando i dati parlano chiaro. È sulle code che si separano — <code>loess</code> ondeggia dietro le poche pagine che trova e si ferma di netto al bordo dei dati, mentre <code>lm</code> e <code>nls</code> proseguono lisce anche dove le osservazioni scarseggiano.</p>



<p class="wp-block-paragraph"><strong>Dunque</strong>: la log-log pesata resta il cavallo di battaglia — interpretabile, estrapolabile, con i residui già in scala moltiplicativa. <code>nls</code> la raffina quando serve un esponente pulito da mettere nero su bianco; <code>loess</code> è l&#8217;occhio critico che, prima di fidarci, ci dice se stiamo forzando la forma sbagliata. Tre strumenti diversi per un solo mestiere: trasformare la posizione in un CTR atteso.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="residui" class="wp-block-heading">I residui: chi rende meno di quanto dovrebbe</h2>



<p class="wp-block-paragraph">Avere la curva significa poter calcolare, per ogni pagina, il suo CTR atteso e confrontarlo con quello reale. Il confronto, lo abbiamo detto, va fatto in termini di rapporto e non di differenza: <code>rapporto = ctr_reale / ctr_atteso</code>. Un valore intorno a 1 dice che la pagina rende quanto previsto; molto sotto 1 che lascia clic sul tavolo; molto sopra 1 che cattura più della sua quota.<br>Calcolo il CTR atteso, il rapporto e segnalo i casi che si discostano davvero — ma solo se hanno abbastanza impression da rendere il loro CTR affidabile:</p>



<pre class="wp-block-code"><code>gsc$ctr_atteso &lt;- exp(predict(fit))           # torno dalla scala log a quella naturale
gsc$rapporto   &lt;- gsc$ctr / gsc$ctr_atteso

gsc$flag &lt;- ifelse(gsc$rapporto &lt; 0.6 &amp; gsc$impression &gt;= 1000, "SOTTO",
             ifelse(gsc$rapporto &gt; 1.4 &amp; gsc$impression &gt;= 1000, "SOPRA", "ok"))

gsc&#091;order(gsc$rapporto),
    c("pagina","posizione","impression","ctr","ctr_atteso","rapporto","flag")]</code></pre>



<p class="wp-block-paragraph">n.b. il <code>predict</code> ci dà il logaritmo del CTR atteso, perché è su quella scala che abbiamo stimato il modello: l&#8217;<code>exp</code> lo riporta in CTR vero e proprio. A rigore l&#8217;<code>exp</code> ci restituisce la <em>mediana</em> del CTR atteso, non la media aritmetica (sotto errori log-normali la media vera è un filo più alta), ma per i rapporti relativi che ci interessano la distinzione è ininfluente.<br>L&#8217;output, ordinato dal rapporto più basso al più alto:</p>



<figure class="wp-block-table"><table><thead><tr><th>pagina</th><th>posizione</th><th>impression</th><th>ctr</th><th>ctr_atteso</th><th>rapporto</th><th>flag</th></tr></thead><tbody><tr><td>/modello-attribuzione</td><td>8,3</td><td>900</td><td>0,012</td><td>0,029</td><td>0,41</td><td>ok</td></tr><tr><td>/guida-keyword-research</td><td>3,4</td><td>12500</td><td>0,034</td><td>0,076</td><td>0,44</td><td><strong>SOTTO</strong></td></tr><tr><td>/guida-seo-tecnica</td><td>1,3</td><td>9800</td><td>0,232</td><td>0,218</td><td>1,07</td><td>ok</td></tr><tr><td>/calcolatore-roi-campagne</td><td>4,0</td><td>2100</td><td>0,071</td><td>0,064</td><td>1,11</td><td>ok</td></tr><tr><td>/checklist-audit-seo</td><td>2,1</td><td>5400</td><td>0,150</td><td>0,129</td><td>1,16</td><td>ok</td></tr><tr><td>/analisi-dei-competitor</td><td>7,0</td><td>6100</td><td>0,041</td><td>0,035</td><td>1,18</td><td>ok</td></tr><tr><td>/report-posizionamento</td><td>9,1</td><td>3300</td><td>0,031</td><td>0,026</td><td>1,18</td><td>ok</td></tr><tr><td>/guida-link-building</td><td>6,1</td><td>4200</td><td>0,048</td><td>0,040</td><td>1,19</td><td>ok</td></tr><tr><td>/tutorial-google-analytics</td><td>4,6</td><td>7600</td><td>0,066</td><td>0,055</td><td>1,20</td><td>ok</td></tr><tr><td>/ottimizzazione-meta-tag</td><td>10,2</td><td>1800</td><td>0,028</td><td>0,023</td><td>1,21</td><td>ok</td></tr><tr><td>/glossario-statistica</td><td>5,2</td><td>1500</td><td>0,060</td><td>0,048</td><td>1,25</td><td>ok</td></tr><tr><td>/recensione-tool-seo</td><td>2,8</td><td>8300</td><td>0,171</td><td>0,094</td><td>1,81</td><td><strong>SOPRA</strong></td></tr></tbody></table></figure>



<p class="wp-block-paragraph">Il caso che salta all&#8217;occhio è <strong>/guida-keyword-research</strong>: sta in terza posizione, dove la curva si aspetterebbe un CTR del 7,6%, e invece raccoglie un misero 3,4% — meno della metà del dovuto, su dodicimila e cinquecento impression che rendono il dato solidissimo.<br>È un&#8217;ipotesi forte e immediatamente azionabile: con ogni probabilità il <em>title</em> e la <em>meta description</em> non stanno facendo il loro lavoro, e una riscrittura potrebbe sbloccare clic che la posizione avrebbe già meritato.</p>



<p class="wp-block-paragraph">All&#8217;estremo opposto c&#8217;è <strong>/recensione-tool-seo</strong>, che in seconda-terza posizione rende quasi il doppio dell&#8217;atteso. Non è un problema, è una lezione: qualcosa in quello <em>snippet</em> funziona benissimo — un titolo magnetico, una <em>rich card</em>, una corrispondenza perfetta con l&#8217;intento — e vale la pena capire cosa, per provare a replicarlo altrove. <strong>I residui non servono solo a trovare i malati: le sovra-performance sono i casi di studio da cui imparare cosa, sul nostro sito, fa cliccare.</strong></p>



<div class="wp-block-group has-background" style="background-color:#f5f7f9;margin-top:2.5rem;margin-bottom:2.5rem;padding-top:1.5rem;padding-right:1.5rem;padding-bottom:1rem;padding-left:1.5rem"><div class="wp-block-group__inner-container is-layout-constrained wp-container-core-group-is-layout-eed7543b wp-block-group-is-layout-constrained">
<figure class="wp-block-image size-large"><img decoding="async" width="1110" height="690" src="https://www.gironi.it/blog/wp-content/uploads/2026/07/ctr-curva-attesa.png" alt="Le dodici pagine d'esempio: posizione media in SERP contro CTR, con l'area di ogni punto proporzionale alle impression. La curva blu è il CTR atteso stimato con la log-log pesata; il segmento verticale sotto ogni punto è il suo residuo, cioè la distanza dalla curva. Spiccano le due pagine dell'esempio: /guida-keyword-research a 0,44× l'atteso (rosso, sotto-performa) e /recensione-tool-seo a 1,81× (verde, sovra-performa)." class="wp-image-4075" srcset="https://www.gironi.it/blog/wp-content/uploads/2026/07/ctr-curva-attesa.png 1110w, https://www.gironi.it/blog/wp-content/uploads/2026/07/ctr-curva-attesa-300x186.png 300w, https://www.gironi.it/blog/wp-content/uploads/2026/07/ctr-curva-attesa-1024x637.png 1024w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /><figcaption class="wp-element-caption">Le dodici pagine d&#8217;esempio: posizione media in SERP contro CTR, con l&#8217;area di ogni punto proporzionale alle impression. La curva blu è il CTR atteso stimato con la log-log pesata; il segmento verticale sotto ogni punto è il suo residuo, cioè la distanza dalla curva. Spiccano le due pagine dell&#8217;esempio: /guida-keyword-research a 0,44× l&#8217;atteso (rosso, sotto-performa) e /recensione-tool-seo a 1,81× (verde, sovra-performa).</figcaption></figure>
</div></div>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="interpretare" class="wp-block-heading">Leggere gli scostamenti senza farsi ingannare</h2>



<p class="wp-block-paragraph">C&#8217;è un dettaglio nella tabella che è il cuore di tutto il discorso, e che è facile mancare. <strong>/modello-attribuzione</strong> ha un rapporto di 0,41 — più basso ancora di /guida-keyword-research — eppure non l&#8217;abbiamo segnalato. Il motivo è nella colonna delle impression: novecento. Un CTR calcolato su così pochi dati è quasi solo rumore, e il mese prossimo rientrerà verso la sua media a prescindere da qualunque cosa facciamo.<br>Segnalarlo come &#8220;pagina da ottimizzare&#8221; ci farebbe inseguire un fantasma. Due pagine con lo stesso scostamento, due verdetti opposti, e a fare la differenza è solo la quantità di dati che li sostiene.</p>



<p class="has-light-gray-background-color has-background wp-block-paragraph">Un avvertimento: il CTR atteso è un <strong>valore tipico condizionato</strong> — la mediana che la curva associa a una posizione — non una legge di natura. Una pagina può &#8220;sotto-performare&#8221; per ragioni che con il <em>title</em> non c&#8217;entrano nulla: una query di marca che gonfia il CTR dei concorrenti, un <em>featured snippet</em> o un blocco di annunci che si mangia i clic prima del primo risultato organico, un intento puramente informativo che si soddisfa già leggendo lo <em>snippet</em>. E un CTR costruito su poche impression non misura quasi niente: regredirà verso la sua media da solo, come abbiamo visto parlando di <a href="https://www.gironi.it/blog/regressione-verso-la-media/">regressione verso la media</a>. <strong>Un residuo negativo è un&#8217;ipotesi da verificare — &#8220;forse qui il title rende poco&#8221; — non un verdetto da eseguire.</strong></p>



<p class="wp-block-paragraph">Vale anche la pena ricordare che la curva la abbiamo stimata sui nostri dati, e quei dati la influenzano: una manciata di pagine molto anomale può inclinarla quel tanto che basta a spostare i giudizi sulle altre.<br>Lo si vede già nella nostra tabella: dieci pagine su dodici hanno un rapporto sopra l&#8217;1, ma non è che il sito &#8220;sovra-performi&#8221; quasi ovunque. È che l&#8217;unico grosso scostamento negativo, <code>/guida-keyword-research</code>, pesa moltissimo (dodicimila e cinquecento impression) e tira la curva verso il basso, alzando di riflesso il rapporto di tutte le altre. Il &#8220;centro&#8221; della nuvola, insomma, non è esattamente 1, e conviene leggere i rapporti in termini relativi — chi sta molto sotto e chi molto sopra il grosso del gruppo — più che rispetto alla soglia secca dell&#8217;uno.<br>Per questo il peso delle impression è prezioso, e per questo conviene rifare la curva ogni volta che il quadro cambia, invece di trattarla come una costante incisa nella pietra.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="prova-tu" class="wp-block-heading">Prova tu</h2>



<p class="wp-block-paragraph">Il modo migliore per fissare il meccanismo è metterci le mani. Riprendendo il codice qui sopra, ci sono tre direzioni interessanti da esplorare:</p>



<ol class="wp-block-list">
<li>Aggrega per <strong>query</strong> invece che per pagina: la stessa pagina può comparire su decine di ricerche con posizioni e CTR diversi, e spesso è lì — sulla singola query — che si nasconde l&#8217;occasione persa. La curva e i residui si costruiscono allo stesso identico modo.</li>



<li>Togli il peso delle impression — <code>lm(log(ctr) ~ log(posizione))</code> senza <code>weights</code> — e confronta esponente e verdetti con la versione pesata: quanto sposta le conclusioni l&#8217;aver dato più voce alle pagine con più dati? È il modo per <em>sentire</em> quanto conta la pesatura, invece di prenderla sulla fiducia.</li>



<li>Cambia la soglia di impression sotto cui non ti fidi del CTR: passando da 1000 a 3000, quali pagine escono dal radar? Il numero giusto non esiste in astratto, dipende da quanto traffico muove il tuo sito.</li>
</ol>



<p class="wp-block-paragraph">Suggerimento: la struttura non cambia mai — si stima la curva, si predice l&#8217;atteso, si guarda il rapporto. È giocando con la soglia e con il livello di aggregazione che si capisce quanto di ciò che chiamiamo &#8220;pagina sotto-performante&#8221; sia segnale e quanto, semplicemente, rumore.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p class="wp-block-paragraph">Individuare una pagina che rende meno del previsto <em>per la sua posizione</em> è cugino stretto di un altro problema che ogni analista conosce: individuare un giorno che rende meno del previsto <em>nel tempo</em>, un crollo o un picco nel traffico che non torna con l&#8217;andamento abituale. È lo stesso ragionamento sui residui — valore osservato contro valore atteso — spostato dallo spazio delle posizioni all&#8217;asse del tempo, dove l&#8217;atteso lo fornisce l&#8217;andamento storico della <a href="https://www.gironi.it/blog/analisi-delle-serie-storiche0-e-previsioni-di-serie-temporali-in-r-con-il-metodo-holt-winters/">serie</a>. È esattamente il mestiere dell&#8217;<a href="https://www.gironi.it/blog/anomaly-detection/">anomaly detection</a>: distinguere il segnale dal rumore quando i numeri si muovono nel tempo — il passo naturale da cui proseguire.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 id="per-approfondire" class="wp-block-heading">Per approfondire</h3>



<p class="wp-block-paragraph">Se vuoi approfondire la regressione, le trasformazioni logaritmiche e la lettura dei residui — l&#8217;ossatura esatta del modello che abbiamo costruito qui — <em><a href="https://www.amazon.it/dp/8891906190?tag=consulenzeinf-21" rel="nofollow sponsored noopener" target="_blank">Introduzione all&#8217;econometria</a></em> di Stock e Watson è il riferimento che consiglio: spiega con cura sia il &#8220;perché&#8221; dei logaritmi sia il &#8220;come&#8221; si interpretano i coefficienti, partendo sempre da problemi applicati. A chi volesse spingersi oltre la legge di potenza, verso i metodi locali come il <code>loess</code>, <em><a href="https://www.amazon.it/dp/1461471370?tag=consulenzeinf-21" rel="nofollow sponsored noopener" target="_blank">An Introduction to Statistical Learning</a></em> di James, Witten, Hastie e Tibshirani offre i capitoli giusti, con i laboratori in R sotto mano.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.gironi.it/blog/ctr-atteso-vs-reale/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Naive Bayes: classificare l&#8217;intento delle query con il teorema di Bayes</title>
		<link>https://www.gironi.it/blog/naive-bayes-intento-ricerca/</link>
					<comments>https://www.gironi.it/blog/naive-bayes-intento-ricerca/#respond</comments>
		
		<dc:creator><![CDATA[Paolo Gironi]]></dc:creator>
		<pubDate>Sun, 28 Jun 2026 08:27:16 +0000</pubDate>
				<category><![CDATA[statistica]]></category>
		<guid isPermaLink="false">https://www.gironi.it/blog/?p=3901</guid>

					<description><![CDATA[Nell&#8217;articolo sul multi-armed bandit abbiamo usato Bayes per decidere fra varianti: spostare il traffico verso quella che converte di più mentre il test è ancora in corso. Adesso facciamo un passo di lato, restando però nello stesso impianto di ragionamento: invece di scegliere fra opzioni, vogliamo classificare, cioè attaccare a ogni nuova osservazione l&#8217;etichetta più &#8230; <a href="https://www.gironi.it/blog/naive-bayes-intento-ricerca/" class="more-link">Leggi tutto<span class="screen-reader-text"> "Naive Bayes: classificare l&#8217;intento delle query con il teorema di Bayes"</span></a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Nell&#8217;articolo sul <a href="https://www.gironi.it/blog/multi-armed-bandit-thompson/">multi-armed bandit</a> abbiamo usato Bayes per <em>decidere fra varianti</em>: spostare il traffico verso quella che converte di più mentre il test è ancora in corso. Adesso facciamo un passo di lato, restando però nello stesso impianto di ragionamento: invece di scegliere fra opzioni, vogliamo <em>classificare</em>, cioè attaccare a ogni nuova osservazione l&#8217;etichetta più probabile date le sue caratteristiche.<br> Il caso concreto è uno che chiunque faccia SEO conosce bene: <strong>l&#8217;intento dietro una query di ricerca</strong>. Chi cerca &#8220;come fare un dolce&#8221; vuole imparare qualcosa; chi cerca &#8220;comprare scarpe online&#8221; è pronto a tirare fuori la carta di credito. Sono due mondi diversi, e servono contenuti diversi: una guida, un tutorial, un glossario per il primo; una scheda prodotto, un listino, una <em>call to action</em> ben visibile per il secondo. Sbagliare l&#8217;intento significa rispondere alla domanda giusta nel modo sbagliato.</p>



<p class="wp-block-paragraph">Il problema è che le query sono tante e sempre nuove, e classificarle a mano non scala. Ci serve un metodo che impari da un po&#8217; di esempi etichettati e poi se la cavi da solo sulle query mai viste. L&#8217;algoritmo che fa questo con un&#8217;eleganza quasi disarmante è il <em>Naive Bayes</em>, e — come il nome lascia intuire — parte ancora una volta dal <a href="https://www.gironi.it/blog/statistica-bayesiana/">teorema di Bayes</a> che ci accompagna da tutto questo percorso.</p>



<span id="more-3901"></span>



<p class="wp-block-paragraph"><strong>Di cosa parleremo</strong>:</p>



<ul class="wp-block-list"><li><a href="#dal-teorema">Dal teorema al classificatore</a></li><li><a href="#addestramento">Addestrare su query etichettate</a></li><li><a href="#classificare">Classificare le query nuove</a></li><li><a href="#limiti">I limiti del &#8220;naive&#8221;</a></li><li><a href="#prova-tu">Prova tu</a></li><li><a href="#per-approfondire">Per approfondire</a></li></ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="dal-teorema">Dal teorema al classificatore</h2>



<p class="wp-block-paragraph">Riprendiamo il filo. Il teorema di Bayes ci dice come aggiornare la probabilità di un&#8217;ipotesi alla luce dei dati osservati. Qui l&#8217;ipotesi è &#8220;questa query appartiene alla classe <em>informazionale</em>&#8221; (oppure <em>transazionale</em>), e i dati sono le parole che compongono la query. Vogliamo cioè la probabilità di una classe <em>dato</em> il testo: in simboli, <em>P(classe | parole)</em>.</p>



<p class="wp-block-paragraph">Calcolarla direttamente sarebbe un incubo, perché le combinazioni di parole possibili sono sterminate. Bayes ci permette di girare il problema: invece di chiederci quanto è probabile la classe viste le parole, ci chiediamo quanto sono probabili quelle parole vista la classe — una domanda a cui i dati di addestramento sanno rispondere. La regola, prima a parole e poi in formula, è che la probabilità di una classe data una query è proporzionale alla probabilità a priori della classe moltiplicata per la probabilità di osservare quelle parole se la classe fosse quella:</p>



\( P(\text{classe} \mid \text{parole}) \propto P(\text{classe}) \cdot \prod_i P(\text{parola}_i \mid \text{classe}) \\ \)



<p class="wp-block-paragraph">Sciogliamo i simboli. <em>P(classe)</em> è il <strong>prior</strong>: quanto è frequente quella classe in partenza, prima di guardare il testo (se metà delle query d&#8217;esempio sono informazionali, il prior è 0,5). <em>P(parola | classe)</em> è quanto spesso quella parola compare nelle query di quella classe. Il simbolo ∏ è semplicemente il prodotto: moltiplichiamo i contributi di tutte le parole della query. Il segno ∝ (&#8220;proporzionale a&#8221;) ci ricorda che stiamo trascurando un denominatore costante, identico per tutte le classi: visto che alla fine ci interessa solo <em>quale</em> classe vince, possiamo ignorarlo senza danni.</p>



<p class="wp-block-paragraph">Ed eccolo, il punto in cui si annida il &#8220;naive&#8221;. <strong>Moltiplicare le probabilità delle singole parole come se fossero indipendenti l&#8217;una dall&#8217;altra equivale ad assumere che, nota la classe, la presenza di una parola non dica nulla sulla presenza delle altre.</strong> È un&#8217;assunzione palesemente falsa nel linguaggio reale — &#8220;carta&#8221; e &#8220;credito&#8221; non compaiono certo a caso una indipendentemente dall&#8217;altra — ed è proprio questa ingenuità (in inglese <em>naive</em>) a dare il nome all&#8217;algoritmo. La cosa sorprendente, che vedremo, è che pur partendo da un&#8217;ipotesi così grossolana il metodo funziona benissimo nella pratica.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="addestramento">Addestrare su query etichettate</h2>



<p class="wp-block-paragraph">Addestrare un Naive Bayes vuol dire una cosa sola: contare. Per ogni classe contiamo quante volte compare ciascuna parola nelle query di esempio, e da quei conteggi ricaviamo le <em>P(parola | classe)</em>. Niente ottimizzazione, niente iterazioni: si scorrono i dati una volta e si riempie una tabella di frequenze.</p>



<p class="wp-block-paragraph">Partiamo da un piccolo insieme di query etichettate a mano, cinque informazionali e cinque transazionali. Addestro il classificatore in R così:</p>



<pre class="wp-block-code"><code>train &lt;- list(
  info  = c("come fare un dolce", "cos e la statistica", "guida seo principianti",
            "tutorial r gratis", "come funziona il bayesiano"),
  trans = c("comprare scarpe online", "prezzo iphone offerta", "miglior hosting economico",
            "acquista corso seo", "sconto abbonamento palestra")
)
tok &lt;- function(s) unlist(strsplit(tolower(s), "\\s+"))
vocab &lt;- unique(unlist(lapply(unlist(train), tok)))
counts &lt;- lapply(train, function(docs) {
  w &lt;- table(factor(unlist(lapply(docs, tok)), levels = vocab)); w + 1
})
tots &lt;- sapply(counts, sum); V &lt;- length(vocab)
prior &lt;- sapply(train, length) / length(unlist(train))   # 0.5 / 0.5</code></pre>



<p class="wp-block-paragraph">Vediamo cosa succede riga per riga. La funzione <code>tok</code> spezza ogni query in parole minuscole (una tokenizzazione spartana ma sufficiente). <code>vocab</code> è il vocabolario, l&#8217;elenco di tutte le parole distinte viste in addestramento: qui sono 31. Per ogni classe, <code>table(factor(...))</code> conta le occorrenze di ciascuna parola del vocabolario; <code>tots</code> è il totale dei conteggi per classe, e <code>prior</code> qui vale 0,5 e 0,5 perché le due classi hanno lo stesso numero di esempi.</p>



<p class="wp-block-paragraph">C&#8217;è un dettaglio in quel <code>w + 1</code> che merita una sosta, perché è il trucco che tiene in piedi tutto l&#8217;edificio. <strong>Se una parola non compare mai nelle query di una classe, il suo conteggio è zero, e con esso si azzererebbe l&#8217;intero prodotto delle probabilità: una sola parola sconosciuta basterebbe a mandare a zero la probabilità della classe, cancellando il contributo di tutte le altre.</strong> È il classico caso in cui &#8220;moltiplicare per zero&#8221; rovina la festa. La soluzione si chiama <strong>smoothing di Laplace</strong>: aggiungiamo 1 al conteggio di ogni parola, in ogni classe, prima di calcolare le proporzioni. Nessuna parola ha più probabilità esattamente nulla, solo molto piccola.</p>



<p class="wp-block-paragraph">Il prezzo dello smoothing è che ai totali per classe va aggiunto il numero di parole del vocabolario (le 31 unità aggiunte una per parola): per questo <code>tots</code> non vale 18 e 15 — i token grezzi delle due classi — ma 49 e 46. Con questi numeri, per esempio, la parola &#8220;comprare&#8221; (presente una volta fra le transazionali, mai fra le informazionali) ha <em>P(comprare | trans) = 2/46 ≈ 0,043</em> contro <em>P(comprare | info) = 1/49 ≈ 0,020</em>: più del doppio, ed è proprio questo squilibrio che spinge una query verso l&#8217;intento giusto. Una parola neutra come &#8220;seo&#8221;, che compare una volta per parte, resta invece quasi bilanciata fra le due classi e non sposta l&#8217;ago.</p>



<div class="wp-block-group has-background" style="background-color:#f5f7f9;margin-top:2.5rem;margin-bottom:2.5rem;padding-top:1.5rem;padding-right:1.5rem;padding-bottom:1rem;padding-left:1.5rem"><div class="wp-block-group__inner-container is-layout-constrained wp-container-core-group-is-layout-eed7543b wp-block-group-is-layout-constrained">

<figure class="wp-block-image size-large"><img decoding="async" width="990" height="630" src="https://www.gironi.it/blog/wp-content/uploads/2026/07/naive-bayes-impronta.png" alt="L'impronta lessicale delle due classi: P(parola | classe) per alcuni termini del vocabolario, dopo lo smoothing di Laplace. "comprare" e "sconto" pesano più del doppio verso il transazionale, "come" verso l'informazionale, "seo" resta quasi in equilibrio: è questo squilibrio parola per parola a spingere una query verso l'intento giusto." class="wp-image-4032" srcset="https://www.gironi.it/blog/wp-content/uploads/2026/07/naive-bayes-impronta.png 990w, https://www.gironi.it/blog/wp-content/uploads/2026/07/naive-bayes-impronta-300x191.png 300w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /><figcaption class="wp-element-caption">L&#8217;impronta lessicale delle due classi: P(parola | classe) per alcuni termini del vocabolario, dopo lo smoothing di Laplace. &#8220;comprare&#8221; e &#8220;sconto&#8221; pesano più del doppio verso il transazionale, &#8220;come&#8221; verso l&#8217;informazionale, &#8220;seo&#8221; resta quasi in equilibrio: è questo squilibrio parola per parola a spingere una query verso l&#8217;intento giusto.</figcaption></figure>

</div></div>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="classificare">Classificare le query nuove</h2>



<p class="wp-block-paragraph">Con la tabella delle frequenze pronta, classificare una query nuova è davvero un giochetto da ragazzi: si tokenizza, si sommano i logaritmi delle probabilità di ciascuna parola (sommare logaritmi anziché moltiplicare probabilità evita che il prodotto di tanti numeri piccolissimi vada in <em>underflow</em> numerico, ma il risultato è lo stesso), si aggiunge il logaritmo del prior e si sceglie la classe con il punteggio più alto. Calcolo in R:</p>



<pre class="wp-block-code"><code>classify &lt;- function(query) {
  w &lt;- tok(query)
  logp &lt;- log(prior)
  for (cl in names(train)) {
    p &lt;- counts[[cl]][w]; p[is.na(p)] &lt;- 1            # parola fuori vocab: peso neutro
    logp[cl] &lt;- logp[cl] + sum(log(as.numeric(p) / tots[cl]))
  }
  names(which.max(logp))
}
cat("'comprare corso seo sconto' -&gt;", classify("comprare corso seo sconto"), "\n")
cat("'come imparare la statistica'  -&gt;", classify("come imparare la statistica"), "\n")</code></pre>



<p class="wp-block-paragraph">L&#8217;output è netto:</p>



<pre class="wp-block-code"><code>'comprare corso seo sconto' -&gt; trans
'come imparare la statistica'  -&gt; info</code></pre>



<p class="wp-block-paragraph">Come si vede, il classificatore assegna la prima query all&#8217;intento transazionale e la seconda all&#8217;informazionale, esattamente come avrebbe fatto un essere umano. Vale la pena notare come ci arriva. Nella prima query &#8220;comprare&#8221; e &#8220;sconto&#8221; tirano con decisione verso <em>trans</em>, &#8220;seo&#8221; resta neutra, e nemmeno &#8220;corso&#8221; — che in addestramento compariva nel transazionale &#8220;acquista corso seo&#8221; — rema contro: il verdetto è solido. Nella seconda, &#8220;statistica&#8221; è una parola spiccatamente informazionale, e &#8220;imparare&#8221;, pur essendo fuori vocabolario, non fa danni grazie a quel <code>p[is.na(p)] &lt;- 1</code> che assegna alle parole mai viste un peso neutro, identico per entrambe le classi: non potendo dire nulla, semplicemente non vota.</p>



<div class="wp-block-group has-background" style="background-color:#f5f7f9;margin-top:2.5rem;margin-bottom:2.5rem;padding-top:1.5rem;padding-right:1.5rem;padding-bottom:1rem;padding-left:1.5rem"><div class="wp-block-group__inner-container is-layout-constrained wp-container-core-group-is-layout-eed7543b wp-block-group-is-layout-constrained">

<figure class="wp-block-image size-large"><img decoding="async" width="1110" height="585" src="https://www.gironi.it/blog/wp-content/uploads/2026/07/naive-bayes-tiro-alla-fune.png" alt="Il tiro alla fune delle parole: per ogni query, il log-rapporto di verosimiglianza di ciascuna parola. A destra (arancio) la parola spinge verso il transazionale, a sinistra (blu) verso l'informazionale, vicino allo zero le parole neutre o fuori vocabolario (come "imparare", che non vota). La somma dei contributi decide il verdetto." class="wp-image-4033" srcset="https://www.gironi.it/blog/wp-content/uploads/2026/07/naive-bayes-tiro-alla-fune.png 1110w, https://www.gironi.it/blog/wp-content/uploads/2026/07/naive-bayes-tiro-alla-fune-300x158.png 300w, https://www.gironi.it/blog/wp-content/uploads/2026/07/naive-bayes-tiro-alla-fune-1024x540.png 1024w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /><figcaption class="wp-element-caption">Il tiro alla fune delle parole: per ogni query, il log-rapporto di verosimiglianza di ciascuna parola. A destra (arancio) la parola spinge verso il transazionale, a sinistra (blu) verso l&#8217;informazionale, vicino allo zero le parole neutre o fuori vocabolario (come &#8220;imparare&#8221;, che non vota). La somma dei contributi decide il verdetto.</figcaption></figure>

</div></div>



<p class="wp-block-paragraph">Cinque query d&#8217;esempio per parte sono pochissime, eppure il meccanismo è già tutto qui. In un caso reale basta sostituire le manciate di query etichettate con qualche centinaio o migliaio di query estratte dalla Search Console e annotate per intento, e lo stesso codice — invariato nella struttura — diventa un classificatore di intento utilizzabile sul serio.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="limiti">I limiti del &#8220;naive&#8221;</h2>



<p class="wp-block-paragraph">Prima di lanciarsi, però, è bene sapere dove il metodo mostra la corda, perché la sua semplicità ha un rovescio.</p>



<p class="wp-block-paragraph">Il limite più evidente è proprio l&#8217;assunzione di indipendenza da cui prende il nome. Trattando ogni parola come slegata dalle altre, il Naive Bayes ignora del tutto l&#8217;ordine e il contesto: per lui &#8220;scarpe da corsa economiche&#8221; e &#8220;economia delle scarpe da corsa&#8221; sono lo stesso sacchetto di parole (<em>bag of words</em>, come dicono gli anglosassoni). Nella classificazione d&#8217;intento questo conta poco, ma in compiti più sottili può ingannare. C&#8217;è poi la questione delle <strong>parole fuori vocabolario</strong>: una query fatta solo di termini mai visti in addestramento finirebbe decisa dal solo prior, cioè da un puro testa o croce — il segnale che il dataset di esempi è troppo magro e va ampliato.</p>



<p class="has-light-gray-background-color has-background wp-block-paragraph">Un avvertimento che vale per qualunque classificatore, e a maggior ragione per uno addestrato su pochi dati: il modello impara <em>esattamente</em> ciò che gli mostriamo, bias compresi. Se le query d&#8217;esempio transazionali contengono tutte la parola &#8220;comprare&#8221;, il classificatore assocerà l&#8217;intento d&#8217;acquisto a quel termine e arrancherà su un &#8220;aggiungi al carrello&#8221; altrettanto transazionale ma lessicalmente diverso. La qualità e la rappresentatività dei dati etichettati contano più della raffinatezza dell&#8217;algoritmo: un Naive Bayes nutrito di esempi vari e bilanciati batte un modello sofisticato addestrato male.</p>



<p class="wp-block-paragraph">Detto questo, il Naive Bayes resta un <em>preziosissimo strumento</em> da avere nella cassetta degli attrezzi: è velocissimo da addestrare, richiede pochi dati per partire, è interpretabile (possiamo sempre andare a vedere quali parole hanno spinto la decisione) e nella classificazione di testo regge il confronto con modelli ben più complessi. È spesso la <em>baseline</em> da battere prima di tirare in ballo artiglieria più pesante — ed è qui che si apre la porta sul machine learning vero e proprio, dove la classificazione si fa con alberi, regressioni e reti che lasciano cadere l&#8217;ipotesi di indipendenza in cambio di più potenza (e meno trasparenza).</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="prova-tu">Prova tu</h2>



<p class="wp-block-paragraph">Il codice qui sopra è un terreno di gioco perfetto per costruire l&#8217;intuizione. Le query di ricerca non sono solo informazionali o transazionali: ne manca almeno una terza famiglia, quella <strong>navigazionale</strong> (chi cerca &#8220;facebook login&#8221; o &#8220;gironi blog&#8221; vuole solo arrivare a un sito preciso). Ecco qualche modifica da provare:</p>



<ol class="wp-block-list"><li>Aggiungi una classe <code>nav</code> al <code>train</code> con quattro o cinque query navigazionali (&#8220;facebook login&#8221;, &#8220;youtube&#8221;, &#8220;amazon accedi&#8221;, &#8220;gmail posta&#8221;), poi riaddestra: il <code>prior</code> non sarà più 0,5 ma circa un terzo per classe. Come cambiano le classificazioni delle query precedenti?</li><li>Dài in pasto al classificatore una query ambigua come &#8220;recensione iphone&#8221; (informativa? transazionale?) e guarda da che parte pende. Aveva senso il verdetto, viste le parole in addestramento?</li><li>Togli lo smoothing (sostituisci <code>w + 1</code> con <code>w</code>) e prova a classificare una query con una parola rara: cosa succede al punteggio quando un conteggio è zero? È il modo più rapido per vedere con i propri occhi perché Laplace serve.</li></ol>



<p class="wp-block-paragraph">Il bello è che la struttura del codice non cambia: aggiungere una classe vuol dire solo allungare la lista <code>train</code>, e tutto il resto — vocabolario, conteggi, prior, regola di classificazione — si adatta da sé.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p class="wp-block-paragraph">Con questo chiudiamo il filo bayesiano che abbiamo dipanato di articolo in articolo: dalla <a href="https://www.gironi.it/blog/conversion-rate-bayesiano/">stima di un conversion rate</a> al confronto fra varianti, dall&#8217;allocazione adattiva del traffico fino a quest&#8217;ultimo salto, dal decidere al classificare. Lo stesso teorema, riproposto ogni volta in una veste diversa, si è rivelato un filo conduttore sorprendentemente robusto. Da qui in avanti la strada si biforca verso il machine learning in senso pieno — gli alberi decisionali, la regressione logistica, le reti — dove i metodi rinunciano alle ipotesi più comode in cambio di potenza, e dove Bayes resta sullo sfondo come la grammatica con cui, in fondo, si impara sempre dai dati.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading" id="per-approfondire">Per approfondire</h3>



<p class="wp-block-paragraph">Se vuoi passare dal Naive Bayes al machine learning vero e proprio restando con i piedi per terra (e con R sotto mano), <a href="https://www.amazon.it/dp/1461471370?tag=consulenzeinf-21" rel="nofollow sponsored noopener" target="_blank"><em>An Introduction to Statistical Learning</em></a> di James, Witten, Hastie e Tibshirani è il testo che consiglio. È la porta d&#8217;ingresso più accessibile al machine learning applicato: spiega classificazione, alberi e regressione con il rigore giusto ma senza matematica intimidatoria, e ogni capitolo ha laboratori in R che si possono rifare passo passo. La seconda edizione dedica spazio proprio al Naive Bayes, così il salto da questo articolo al resto del percorso è naturale.</p>



<p class="wp-block-paragraph">Questo articolo fa parte del percorso <a href="https://www.gironi.it/blog/approccio-bayesiano/">«L&#8217;approccio bayesiano»</a>, la guida ragionata agli articoli su statistica e inferenza bayesiana applicate alla SEO.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.gironi.it/blog/naive-bayes-intento-ricerca/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Multi-armed bandit: ottimizzare le varianti mentre il test è ancora in corso</title>
		<link>https://www.gironi.it/blog/multi-armed-bandit-thompson/</link>
					<comments>https://www.gironi.it/blog/multi-armed-bandit-thompson/#respond</comments>
		
		<dc:creator><![CDATA[Paolo Gironi]]></dc:creator>
		<pubDate>Sat, 27 Jun 2026 17:04:59 +0000</pubDate>
				<category><![CDATA[statistica]]></category>
		<guid isPermaLink="false">https://www.gironi.it/blog/?p=3888</guid>

					<description><![CDATA[Nell&#8217;articolo sull&#8217;A/B test bayesiano abbiamo confrontato due varianti a campione fisso: si raccolgono i dati per tutta la durata pianificata, si calcola la probabilità che B batta A, si decide. È un metodo solido, ma porta con sé un costo che di solito passa sotto silenzio. Quel costo è il traffico che, per tutta la &#8230; <a href="https://www.gironi.it/blog/multi-armed-bandit-thompson/" class="more-link">Leggi tutto<span class="screen-reader-text"> "Multi-armed bandit: ottimizzare le varianti mentre il test è ancora in corso"</span></a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Nell&#8217;articolo sull&#8217;<a href="https://www.gironi.it/blog/ab-test-bayesiano/">A/B test bayesiano</a> abbiamo confrontato due varianti a campione fisso: si raccolgono i dati per tutta la durata pianificata, si calcola la probabilità che B batta A, si decide. È un metodo solido, ma porta con sé un costo che di solito passa sotto silenzio.<br> Quel costo è il traffico che, per tutta la durata del test, continuiamo a mandare sulla variante peggiore. Se a metà esperimento B sta già stravincendo, ogni visitatore assegnato ad A è una conversione che probabilmente stiamo buttando via. <strong>Il test a campione fisso ci fa pagare l&#8217;informazione che raccogliamo: per sapere quale variante è migliore, dobbiamo continuare a mostrare anche quella che sospettiamo essere la peggiore.</strong></p>



<p class="wp-block-paragraph">C&#8217;è un modo per ridurre questo conto, e si chiama <em>multi-armed bandit</em>. L&#8217;idea è spostare il traffico in modo adattivo verso la variante che sta vincendo <em>mentre il test è ancora in corso</em>, invece di aspettare il verdetto finale. In questo articolo lo costruiamo con uno degli algoritmi più eleganti e pratici, il <em>Thompson sampling</em>, che è la naturale prosecuzione del ragionamento <a href="https://www.gironi.it/blog/statistica-bayesiana/">bayesiano</a> che abbiamo seguito finora.</p>



<span id="more-3888"></span>



<p class="wp-block-paragraph"><strong>Di cosa parleremo</strong>:</p>



<ul class="wp-block-list"><li><a href="#esplorazione-sfruttamento">Il dilemma fra esplorazione e sfruttamento</a></li><li><a href="#thompson-sampling">Thompson sampling: lasciar decidere il posterior</a></li><li><a href="#regret-evitato">Quanto si guadagna davvero: il regret evitato</a></li><li><a href="#quando-ha-senso">Quando un bandit ha senso, e quando no</a></li><li><a href="#prova-tu">Prova tu</a></li><li><a href="#per-approfondire">Per approfondire</a></li></ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="esplorazione-sfruttamento">Esplorazione contro sfruttamento</h2>



<p class="wp-block-paragraph">Il nome viene dalle slot machine: un <em>bandito a un braccio solo</em> (<em>one-armed bandit</em>) è la macchinetta del casinò, e immaginiamo di averne davanti diverse, ciascuna con una probabilità di vincita sconosciuta e diversa dalle altre. Abbiamo un numero limitato di gettoni. A ogni giocata dobbiamo scegliere quale braccio tirare. Qual è la strategia che massimizza la vincita totale?</p>



<p class="wp-block-paragraph">La traduzione per chi fa SEO o marketing è immediata: i bracci sono le varianti (tre versioni di un <em>title tag</em>, di una <em>call to action</em>, di una landing page), i gettoni sono i visitatori, la vincita è la conversione. Ogni visitatore va assegnato a una variante, e vogliamo massimizzare le conversioni totali lungo tutto l&#8217;esperimento.</p>



<p class="wp-block-paragraph">Qui nasce la tensione di fondo, quella che rende il problema interessante. Da un lato vorremmo <strong>sfruttare</strong> (<em>exploit</em>) la variante che finora sembra la migliore, per incassare più conversioni possibili. Dall&#8217;altro dobbiamo continuare a <strong>esplorare</strong> (<em>explore</em>) anche le altre, perché &#8220;finora sembra la migliore&#8221; si basa su pochi dati e potrebbe essere un&#8217;impressione sbagliata.<br> È un equilibrio delicato: troppa esplorazione e sprechiamo traffico su varianti mediocri; troppo sfruttamento e rischiamo di incoronare un vincitore sbagliato sulla base di un colpo di fortuna iniziale. <strong>Il dilemma esplorazione-sfruttamento è il cuore di ogni problema di multi-armed bandit: ogni scelta è insieme un&#8217;occasione di guadagno e un&#8217;occasione di apprendimento, e le due cose tirano in direzioni opposte.</strong></p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="thompson-sampling">Thompson sampling: lasciar decidere il posterior</h2>



<p class="wp-block-paragraph">L&#8217;A/B test classico risolve il dilemma nel modo più rozzo possibile: esplora e basta, in parti uguali, fino alla fine. Metà traffico ad A, metà a B, nessun adattamento. Thompson sampling lo risolve in modo molto più astuto, e quasi sorprendente per quanto è semplice.</p>



<p class="wp-block-paragraph">Riprendiamo il filo bayesiano. Per ogni variante manteniamo un posterior sul suo tasso di conversione: come abbiamo visto stimando il conversion rate, partendo da un prior <a href="https://www.gironi.it/blog/la-distribuzione-beta-spiegata-semplice/">Beta</a> e osservando esiti binari (conversione sì/no), il posterior di ogni braccio è ancora una distribuzione Beta, che si aggiorna a ogni visitatore. All&#8217;inizio, quando non sappiamo nulla, ogni braccio parte da un prior non informativo Beta(1, 1).</p>



<p class="wp-block-paragraph">La regola di Thompson, a parole prima che in formule, è questa: invece di chiederci &#8220;qual è la variante con la media più alta finora?&#8221;, a ogni visitatore <strong>campioniamo un tasso a caso dal posterior di ciascun braccio, e giochiamo il braccio che ha prodotto il campione più alto</strong>. È un modo di scegliere &#8220;in proporzione alla probabilità di essere il migliore&#8221;: una variante di cui siamo molto incerti può ancora vincere l&#8217;estrazione ogni tanto (ed è così che continua a essere esplorata), ma man mano che i dati si accumulano i suoi campioni si concentrano e, se è davvero peggiore, smette di essere scelta quasi da sola.</p>



<p class="wp-block-paragraph">L&#8217;eleganza sta proprio qui: non c&#8217;è un parametro di esplorazione da regolare a mano. L&#8217;incertezza del posterior <em>è</em> il motore dell&#8217;esplorazione. Più un braccio è incerto, più i suoi campioni sono dispersi, più spesso capita che vinca l&#8217;estrazione e venga provato; più diventa certo, più i campioni si stringono attorno al valore vero e il braccio viene giocato (o evitato) con decisione.</p>



<div class="wp-block-group has-background" style="background-color:#f5f7f9;margin-top:2.5rem;margin-bottom:2.5rem;padding-top:1.5rem;padding-right:1.5rem;padding-bottom:1rem;padding-left:1.5rem"><div class="wp-block-group__inner-container is-layout-constrained wp-container-core-group-is-layout-eed7543b wp-block-group-is-layout-constrained">

<figure class="wp-block-image size-large"><img decoding="async" width="1080" height="555" src="https://www.gironi.it/blog/wp-content/uploads/2026/07/bandit-beta-prima-dopo.png" alt="I tre posterior Beta, uno per variante. Dopo 300 visitatori (sinistra) sono larghi e sovrapposti: l'incertezza tiene viva l'esplorazione. A fine esperimento (destra) la variante migliore è stretta e ben separata, mentre le altre due, poco esplorate, restano larghe." class="wp-image-4016" srcset="https://www.gironi.it/blog/wp-content/uploads/2026/07/bandit-beta-prima-dopo.png 1080w, https://www.gironi.it/blog/wp-content/uploads/2026/07/bandit-beta-prima-dopo-300x154.png 300w, https://www.gironi.it/blog/wp-content/uploads/2026/07/bandit-beta-prima-dopo-1024x526.png 1024w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /><figcaption class="wp-element-caption">I tre posterior Beta, uno per variante. Dopo 300 visitatori (sinistra) sono larghi e sovrapposti: l&#8217;incertezza tiene viva l&#8217;esplorazione. A fine esperimento (destra) la variante migliore è stretta e ben separata, mentre le altre due, poco esplorate, restano larghe.</figcaption></figure>

</div></div>



<p class="wp-block-paragraph">Simulo in R l&#8217;intero processo su tre varianti con tassi reali del 5%, 7% e 9% (che ovviamente l&#8217;algoritmo non conosce), su 5000 visitatori:</p>



<pre class="wp-block-code"><code>set.seed(7)
true_rates &lt;- c(0.05, 0.07, 0.09)   # 3 varianti, la terza e' la migliore
K &lt;- length(true_rates); N &lt;- 5000
alpha &lt;- rep(1, K); beta_ &lt;- rep(1, K)   # prior Beta(1,1) per braccio
pulls &lt;- rep(0, K); rewards &lt;- rep(0, K)
for (t in 1:N) {
  theta &lt;- rbeta(K, alpha, beta_)        # campiona un tasso per braccio
  arm &lt;- which.max(theta)                 # gioca il braccio campionato migliore
  r &lt;- rbinom(1, 1, true_rates[arm])      # esito (conv si/no)
  alpha[arm] &lt;- alpha[arm] + r; beta_[arm] &lt;- beta_[arm] + (1 - r)
  pulls[arm] &lt;- pulls[arm] + 1; rewards[arm] &lt;- rewards[arm] + r
}</code></pre>



<p class="wp-block-paragraph">Il ciclo è tutto qui. A ogni iterazione campioniamo un <code>theta</code> per braccio (<code>rbeta</code>), scegliamo il massimo (<code>which.max</code>), simuliamo l&#8217;esito (<code>rbinom</code>) e aggiorniamo i parametri del braccio giocato: una conversione aumenta di uno il suo <code>alpha</code>, una mancata conversione il suo <code>beta_</code>. Nessun&#8217;altra logica, nessuna soglia da tarare.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="regret-evitato">Quanto si guadagna: il regret evitato</h2>



<p class="wp-block-paragraph">Vediamo dove ci ha portato la simulazione. La prima cosa da guardare è come si sono distribuiti i 5000 visitatori fra i tre bracci:</p>



<pre class="wp-block-code"><code>cat("pull per braccio:", pulls, "\n")
cat("conv totali bandit:", sum(rewards), "\n")</code></pre>



<p class="wp-block-paragraph">Output: pull per braccio = 359, 278, 4363; conv totali bandit = 424.</p>



<p class="wp-block-paragraph"><strong>Dei 5000 visitatori, ben 4363 sono finiti sulla variante migliore, e solo poco più di 600 in totale sulle due peggiori.</strong> L&#8217;algoritmo, senza che gli avessimo detto nulla sui tassi veri, ha capito da solo quale braccio premiare e ci ha dirottato la stragrande maggioranza del traffico. È esattamente lo sfruttamento che volevamo, raggiunto attraverso un&#8217;esplorazione iniziale appena sufficiente a distinguere i bracci.</p>



<div class="wp-block-group has-background" style="background-color:#f5f7f9;margin-top:2.5rem;margin-bottom:2.5rem;padding-top:1.5rem;padding-right:1.5rem;padding-bottom:1rem;padding-left:1.5rem"><div class="wp-block-group__inner-container is-layout-constrained wp-container-core-group-is-layout-eed7543b wp-block-group-is-layout-constrained">

<figure class="wp-block-image size-large"><img decoding="async" width="975" height="600" src="https://www.gironi.it/blog/wp-content/uploads/2026/07/bandit-allocazione-tempo.png" alt="La quota di traffico assegnata a ciascuna variante mentre il test procede: senza alcun intervento, il bandit dirotta la gran parte dei visitatori (4363 su 5000) verso la variante migliore, lasciando le altre due a poco più di 300 ciascuna." class="wp-image-4017" srcset="https://www.gironi.it/blog/wp-content/uploads/2026/07/bandit-allocazione-tempo.png 975w, https://www.gironi.it/blog/wp-content/uploads/2026/07/bandit-allocazione-tempo-300x185.png 300w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /><figcaption class="wp-element-caption">La quota di traffico assegnata a ciascuna variante mentre il test procede: senza alcun intervento, il bandit dirotta la gran parte dei visitatori (4363 su 5000) verso la variante migliore, lasciando le altre due a poco più di 300 ciascuna.</figcaption></figure>

</div></div>



<p class="wp-block-paragraph">Ora il confronto che dà la misura del vantaggio. Un A/B test classico avrebbe diviso i 5000 visitatori in parti uguali fra le tre varianti, circa 1667 ciascuna, per tutta la durata. Le conversioni attese in quello scenario sono semplicemente la media dei tre tassi moltiplicata per il numero di visitatori:</p>



<pre class="wp-block-code"><code>exp_ab &lt;- sum(true_rates) / K * N
cat("conv attese A/B equipartito:", round(exp_ab), "\n")
cat("regret evitato (circa):", round(sum(rewards) - exp_ab), "conv\n")</code></pre>



<p class="wp-block-paragraph">Output: conv attese A/B equipartito = 350; regret evitato (circa) = 74 conv.</p>



<p class="wp-block-paragraph">Un A/B equipartito si sarebbe portato a casa circa 350 conversioni; il bandit ne ha raccolte 424. La differenza, circa <strong>74 conversioni in più sullo stesso identico traffico</strong>, è ciò che in gergo si chiama <em>regret</em> evitato: il rimpianto, cioè le conversioni perse per aver giocato i bracci sbagliati, che l&#8217;allocazione adattiva ci ha risparmiato. In termini più chiari e diretti, a parità di visitatori il bandit ha convertito oltre il 20% in più, semplicemente non insistendo sulle varianti deboli.<br> Si noti che non abbiamo dovuto sacrificare nulla per ottenerlo: alla fine dell&#8217;esperimento conosciamo comunque il vincitore (anzi, lo conosciamo con grande sicurezza, visti i 4363 dati raccolti su di esso), e nel frattempo abbiamo convertito di più. È il vantaggio strutturale del bandit sul test a campione fisso.</p>



<div class="wp-block-group has-background" style="background-color:#f5f7f9;margin-top:2.5rem;margin-bottom:2.5rem;padding-top:1.5rem;padding-right:1.5rem;padding-bottom:1rem;padding-left:1.5rem"><div class="wp-block-group__inner-container is-layout-constrained wp-container-core-group-is-layout-eed7543b wp-block-group-is-layout-constrained">

<figure class="wp-block-image size-large"><img decoding="async" width="975" height="600" src="https://www.gironi.it/blog/wp-content/uploads/2026/07/bandit-cumulate-vs-ab.png" alt="Conversioni cumulate del bandit contro un A/B equipartito: a parità di traffico le due curve divergono, e il divario finale — 424 contro 350, cioè le 74 conversioni in più — è il regret evitato." class="wp-image-4018" srcset="https://www.gironi.it/blog/wp-content/uploads/2026/07/bandit-cumulate-vs-ab.png 975w, https://www.gironi.it/blog/wp-content/uploads/2026/07/bandit-cumulate-vs-ab-300x185.png 300w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /><figcaption class="wp-element-caption">Conversioni cumulate del bandit contro un A/B equipartito: a parità di traffico le due curve divergono, e il divario finale — 424 contro 350, cioè le 74 conversioni in più — è il regret evitato.</figcaption></figure>

</div></div>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="quando-ha-senso">Quando ha senso (e quando no)</h2>



<p class="wp-block-paragraph">Detto questo, un bandit non è la risposta giusta a ogni domanda, ed è importante capire dove brilla e dove invece un A/B test tradizionale resta preferibile.</p>



<p class="wp-block-paragraph">Il bandit dà il meglio di sé quando il traffico è <strong>continuo e di lungo corso</strong> (una pagina sempre attiva, una campagna che gira per mesi) e quando le varianti da confrontare sono <strong>molte</strong>: lì l&#8217;allocazione adattiva ripaga, perché c&#8217;è tempo e volume per spostare il traffico e tante varianti deboli da abbandonare in fretta. È ideale per ottimizzazioni continue in cui l&#8217;obiettivo è massimizzare le conversioni lungo tutto il percorso, non scattare una fotografia statistica pulita in un dato momento.</p>



<p class="wp-block-paragraph">Quando invece l&#8217;obiettivo è proprio quella fotografia — una stima precisa e imparziale di <em>quanto</em> una variante è migliore, magari da difendere davanti a un cliente o da usare per una decisione strategica — il test a campione fisso resta più adatto: proprio perché esplora in parti uguali, raccoglie dati bilanciati su tutte le varianti e produce stime più nette dell&#8217;effetto. Il bandit, concentrandosi presto sul vincitore, raccoglie pochi dati sui perdenti e quindi stima peggio <em>di quanto</em> siano peggiori.</p>



<p class="has-light-gray-background-color has-background wp-block-paragraph">Un avvertimento: il bandit dà per scontato che i tassi di conversione restino stabili nel tempo. Ma il mondo reale è spesso non stazionario — stagionalità, cambi di pubblico, una promozione che parte. Se la variante migliore cambia <em>dopo</em> che l&#8217;algoritmo si è già concentrato su un&#8217;altra, un Thompson sampling ingenuo come quello visto qui fatica ad accorgersene, perché ha smesso di esplorare le alternative. In contesti che cambiano servono varianti che &#8220;dimenticano&#8221; i dati vecchi (per esempio scontando gradualmente le osservazioni passate), altrimenti si rischia di restare ancorati a un vincitore che non lo è più.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="prova-tu">Prova tu</h2>



<p class="wp-block-paragraph">La simulazione è un terreno di gioco perfetto per costruire l&#8217;intuizione. Riprendendo il codice qui sopra, prova a cambiare i numeri di partenza e osserva come si ridistribuiscono i <code>pulls</code>:</p>



<ol class="wp-block-list"><li>Avvicina i tassi veri, per esempio <code>true_rates &lt;- c(0.07, 0.08, 0.09)</code>: con varianti più simili, quanto più traffico serve prima che il braccio migliore si stacchi? La concentrazione dei pull resta così netta?</li><li>Riduci drasticamente il traffico (<code>N &lt;- 500</code>): con pochi visitatori il bandit fa ancora in tempo a individuare il vincitore, o l&#8217;esplorazione si mangia tutto il budget?</li><li>Aggiungi varianti: passa a quattro o cinque bracci. Con più alternative da scartare, il vantaggio sul <code>regret evitato</code> rispetto all&#8217;A/B equipartito cresce o si assottiglia?</li></ol>



<p class="wp-block-paragraph">Suggerimento: la struttura del ciclo non cambia mai, basta modificare <code>true_rates</code> e <code>N</code>. È proprio osservando come il vettore <code>pulls</code> si sbilancia (o non si sbilancia) in funzione della distanza fra i tassi che si capisce davvero cosa fa, sotto il cofano, il Thompson sampling.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p class="wp-block-paragraph">Finora abbiamo usato Bayes per <em>decidere fra varianti</em>: stimare un tasso, confrontarne due, allocare il traffico in modo adattivo. Ma lo stesso impianto — un prior, dei dati, un posterior — serve anche per un compito diverso e molto comune: <em>classificare</em>, cioè assegnare a ogni nuova osservazione l&#8217;etichetta più probabile date le sue caratteristiche. È il salto dal decidere al classificare, e l&#8217;algoritmo che lo fa con eleganza disarmante, ancora una volta partendo dal teorema di Bayes, è il <em>Naive Bayes</em>: l&#8217;argomento del prossimo articolo.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading" id="per-approfondire">Per approfondire</h3>



<p class="wp-block-paragraph">Se vuoi approfondire i bandit applicati all&#8217;ottimizzazione dei siti web, <a href="https://www.amazon.it/dp/1449341330?tag=consulenzeinf-21" rel="nofollow sponsored noopener" target="_blank"><em>Bandit Algorithms for Website Optimization</em></a> di John Myles White è il libro che consiglio. È un volumetto agile e dichiaratamente pratico, che mette a confronto A/B test e algoritmi bandit (epsilon-greedy, softmax, UCB) proprio nell&#8217;ottica di chi ottimizza pagine e conversioni, con il codice sotto mano. È il punto di partenza ideale per chi vuole passare dalla simulazione che abbiamo visto a un bandit che gira davvero sul proprio sito.</p>



<p class="wp-block-paragraph">Questo articolo fa parte del percorso <a href="https://www.gironi.it/blog/approccio-bayesiano/">«L&#8217;approccio bayesiano»</a>, la guida ragionata agli articoli su statistica e inferenza bayesiana applicate alla SEO.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.gironi.it/blog/multi-armed-bandit-thompson/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>A/B test bayesiano: non solo &#8220;se&#8221; B è meglio di A, ma &#8220;di quanto&#8221;</title>
		<link>https://www.gironi.it/blog/ab-test-bayesiano/</link>
					<comments>https://www.gironi.it/blog/ab-test-bayesiano/#respond</comments>
		
		<dc:creator><![CDATA[Paolo Gironi]]></dc:creator>
		<pubDate>Fri, 26 Jun 2026 07:58:45 +0000</pubDate>
				<category><![CDATA[statistica]]></category>
		<guid isPermaLink="false">https://www.gironi.it/blog/?p=3880</guid>

					<description><![CDATA[Abbiamo avuto modo di esaminare, nell&#8217;articolo sull&#8217;A/B testing classico, come confrontare due varianti con il test per due proporzioni: calcoliamo una statistica, otteniamo un p-value, decidiamo se rifiutare l&#8217;ipotesi nulla. Funziona, ed è il pane quotidiano di chi fa esperimenti online. Ma c&#8217;è uno scarto sottile tra quello che il p-value ci dice e quello &#8230; <a href="https://www.gironi.it/blog/ab-test-bayesiano/" class="more-link">Leggi tutto<span class="screen-reader-text"> "A/B test bayesiano: non solo &#8220;se&#8221; B è meglio di A, ma &#8220;di quanto&#8221;"</span></a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Abbiamo avuto modo di esaminare, nell&#8217;articolo sull&#8217;<a href="https://www.gironi.it/blog/ab-testing/">A/B testing classico</a>, come confrontare due varianti con il test per due proporzioni: calcoliamo una statistica, otteniamo un p-value, decidiamo se rifiutare l&#8217;ipotesi nulla. Funziona, ed è il pane quotidiano di chi fa esperimenti online. Ma c&#8217;è uno scarto sottile tra quello che il p-value ci dice e quello che vorremmo davvero sapere.<br> Il p-value risponde a una domanda contorta: &#8220;se A e B fossero identiche, quanto sarebbe improbabile osservare una differenza grande come questa?&#8221;. La domanda che ci interessa nella realtà operativa è un&#8217;altra, molto più diretta: <strong>qual è la probabilità che B sia meglio di A?</strong> E, subito dopo: di quanto è meglio, e quanto possiamo fidarci di quel &#8220;di quanto&#8221;?</p>



<p class="wp-block-paragraph">L&#8217;approccio <a href="https://www.gironi.it/blog/statistica-bayesiana/">bayesiano</a> risponde a queste due domande in modo nativo. In questo articolo lo applichiamo al confronto tra due varianti, riprendendo il filo lasciato in sospeso quando abbiamo stimato <a href="https://www.gironi.it/blog/conversion-rate-bayesiano/">il conversion rate di una singola variante</a>.</p>



<p class="wp-block-paragraph">È la domanda di ogni test di conversione con un risvolto SEO: due versioni della stessa landing page che si contendono lo stesso traffico organico, oppure due formulazioni di title e meta description giudicate dal loro <a href="https://www.gironi.it/blog/ctr-atteso-vs-reale/">CTR reale rispetto a quello atteso</a> nella SERP. Ovunque ci siano <em>successi su tentativi</em> — clic su impression, iscrizioni su visite, conversioni su sessioni — il ragionamento che segue è lo stesso.</p>



<span id="more-3880"></span>



<p class="wp-block-paragraph"><strong>Di cosa parleremo</strong>:</p>



<ul class="wp-block-list"><li><a href="#due-posterior">Due posterior invece di uno: una distribuzione per ciascuna variante</a></li><li><a href="#probabilita-b-vince">Qual è la probabilità che B vinca davvero</a></li><li><a href="#distribuzione-differenza">Di quanto è meglio: la distribuzione della differenza</a></li><li><a href="#quando-fermarsi">Quando fermarsi: expected loss e il problema del peeking</a></li><li><a href="#prova-tu">Prova tu</a></li><li><a href="#per-approfondire">Per approfondire</a></li></ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="due-posterior">Due posterior invece di uno</h2>



<p class="wp-block-paragraph">Quando abbiamo stimato il conversion rate di una singola variante, abbiamo visto che — partendo da un prior <a href="https://www.gironi.it/blog/la-distribuzione-beta-spiegata-semplice/">Beta</a> e osservando dati binomiali — il posterior è ancora una distribuzione Beta. La regola di aggiornamento era una semplice aritmetica: se il prior è Beta(α, β) e osserviamo \( k \) conversioni su \( n \) sessioni, il posterior è:</p>



\( Beta(\alpha + k,\ \beta + (n &#8211; k)) \\ \)



<p class="wp-block-paragraph">Detto a parole: aggiungiamo le conversioni osservate al primo parametro e le mancate conversioni al secondo, e non serve altro.</p>



<p class="wp-block-paragraph">In un A/B test non abbiamo una proporzione sola, ne abbiamo due: una per il controllo (A) e una per il trattamento (B). Il meccanismo, però, è identico: costruiamo <strong>un posterior per ciascuna variante</strong>, in modo indipendente, applicando la stessa regola due volte.</p>



<p class="wp-block-paragraph">Facciamo un esempio al volo. Abbiamo testato due versioni di una landing page che riceve traffico organico, assegnando i visitatori a caso:</p>



<ul class="wp-block-list"><li><strong>Variante A</strong> (controllo): 90 conversioni su 1000 sessioni → tasso grezzo 9,0%</li><li><strong>Variante B</strong> (trattamento): 120 conversioni su 1000 sessioni → tasso grezzo 12,0%</li></ul>



<p class="wp-block-paragraph">Partiamo da un prior non informativo Beta(1, 1) per entrambe — &#8220;non sappiamo nulla, prima dei dati ogni tasso è ugualmente plausibile&#8221;. Applicando la regola, il posterior di A è Beta(91, 911) e quello di B è Beta(121, 881).</p>



<p class="wp-block-paragraph">Costruisco i due posterior in R, campionandoli per simulazione:</p>



<pre class="wp-block-code"><code>set.seed(42)
# Variante A: 90 conv / 1000 ; Variante B: 120 conv / 1000
cA &lt;- 90; nA &lt;- 1000; cB &lt;- 120; nB &lt;- 1000

# Posterior con prior uniforme Beta(1,1)
postA &lt;- rbeta(1e5, 1 + cA, 1 + nA - cA)   # Beta(91, 911)
postB &lt;- rbeta(1e5, 1 + cB, 1 + nB - cB)   # Beta(121, 881)</code></pre>



<p class="wp-block-paragraph">Adesso abbiamo in mano due distribuzioni, non due numeri. Ed è proprio questo il punto: invece di confrontare 9,0% contro 12,0% come fossero due valori fissi, confrontiamo l&#8217;intera incertezza che li circonda. Le domande operative diventano operazioni su queste distribuzioni.</p>



<p class="wp-block-paragraph">Vale la pena tenere a mente l&#8217;immagine: due rilievi di probabilità affiancati sull&#8217;asse dei tassi di conversione. Più quello di B è spostato a destra rispetto a quello di A, e meno i due si sovrappongono, più diventa probabile che B sia davvero la variante migliore.</p>



<div class="wp-block-group has-background" style="background-color:#f5f7f9;margin-top:2.5rem;margin-bottom:2.5rem;padding-top:1.5rem;padding-right:1.5rem;padding-bottom:1rem;padding-left:1.5rem"><div class="wp-block-group__inner-container is-layout-constrained wp-container-core-group-is-layout-eed7543b wp-block-group-is-layout-constrained">

<figure class="wp-block-image size-large"><img decoding="async" width="1050" height="630" src="https://www.gironi.it/blog/wp-content/uploads/2026/07/ab-test-due-posterior-1.png" alt="Le due posterior Beta messe a confronto: non due numeri (9% e 12%) ma due distribuzioni, che ancora si sovrappongono un po' — ed è in quella sovrapposizione che vive il dubbio residuo." class="wp-image-4213" srcset="https://www.gironi.it/blog/wp-content/uploads/2026/07/ab-test-due-posterior-1.png 1050w, https://www.gironi.it/blog/wp-content/uploads/2026/07/ab-test-due-posterior-1-300x180.png 300w, https://www.gironi.it/blog/wp-content/uploads/2026/07/ab-test-due-posterior-1-1024x614.png 1024w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /><figcaption class="wp-element-caption">Le due posterior Beta messe a confronto: non due numeri (9% e 12%) ma due distribuzioni, che ancora si sovrappongono un po&#8217; — ed è in quella sovrapposizione che vive il dubbio residuo.</figcaption></figure>

</div></div>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="probabilita-b-vince">Qual è la probabilità che B vinca?</h2>



<p class="wp-block-paragraph">La prima domanda — quella che il p-value non risponde mai direttamente — è la probabilità che B sia davvero migliore di A.<br> Con i posterior in mano, il calcolo è disarmante: confrontiamo i campioni di B con quelli di A, coppia per coppia, e contiamo in quale frazione dei casi B supera A. Quella frazione <em>è</em> la probabilità che cerchiamo.</p>



<p class="wp-block-paragraph">Calcolo in R la probabilità che B batta A:</p>



<pre class="wp-block-code"><code>cat("P(B&gt;A) =", round(mean(postB &gt; postA), 3), "\n")</code></pre>



<p class="wp-block-paragraph">Output: P(B&gt;A) = 0.985.</p>



<p class="wp-block-paragraph"><strong>C&#8217;è il 98,5% di probabilità che la variante B converta meglio della variante A.</strong><br> Si noti la differenza di registro rispetto al frequentista. Non stiamo dicendo &#8220;la differenza osservata è improbabile sotto l&#8217;ipotesi nulla&#8221;: stiamo dicendo, in modo diretto, che date le evidenze raccolte è quasi certo che B sia la variante migliore. È esattamente l&#8217;affermazione su cui vorremmo basare una decisione — e l&#8217;approccio bayesiano ce la consegna senza giri di parole.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="distribuzione-differenza">Di quanto è meglio: la distribuzione della differenza</h2>



<p class="wp-block-paragraph">Sapere che B vince con probabilità del 98,5% non basta a decidere. Un miglioramento c&#8217;è quasi di sicuro, ma se fosse di due decimi di punto percentuale, forse non varrebbe la pena di mandare in produzione la nuova pagina. La domanda successiva è quindi: <em>di quanto</em> è meglio?</p>



<p class="wp-block-paragraph">Finora abbiamo tenuto A e B separati, un posterior ciascuno. Ma l&#8217;oggetto che ci interessa davvero non è nessuno dei due preso da solo: è la loro distanza. La risposta vive perciò nella <strong>distribuzione della differenza</strong> tra i due posterior. Sottraiamo, campione per campione, il tasso di A da quello di B: otteniamo una nuova distribuzione, quella dell&#8217;uplift. Da lì leggiamo sia il valore tipico (la media) sia un intervallo di credibilità che ne quantifica l&#8217;incertezza.</p>



<p class="wp-block-paragraph">Calcolo in R la differenza e il suo intervallo al 95%:</p>



<pre class="wp-block-code"><code>diff &lt;- postB - postA
cat("uplift medio (punti %) =", round(mean(diff)*100, 2), "\n")
cat("CI 95% differenza =", round(quantile(diff, c(.025,.975))*100, 2), "\n")</code></pre>



<p class="wp-block-paragraph">Output: uplift medio = 3.00 punti %, CI 95% = [0.31, 5.68].</p>



<p class="wp-block-paragraph"><strong>Il guadagno atteso è di circa 3 punti percentuali di conversione, con un intervallo di credibilità al 95% che va da 0,31 a 5,68 punti.</strong><br> Anche qui il significato è diretto, non una proprietà astratta della procedura: c&#8217;è il 95% di probabilità che il vero miglioramento di B su A stia tra 0,3 e 5,7 punti percentuali. L&#8217;intervallo non tocca lo zero, il che conferma — coerentemente con il 98,5% di prima — che B è quasi certamente superiore. Ma il dato prezioso è l&#8217;ampiezza: il miglioramento potrebbe essere modesto (mezzo punto) o robusto (oltre cinque punti), e questa forbice è un&#8217;informazione che la decisione operativa deve tenere presente.</p>



<div class="wp-block-group has-background" style="background-color:#f5f7f9;margin-top:2.5rem;margin-bottom:2.5rem;padding-top:1.5rem;padding-right:1.5rem;padding-bottom:1rem;padding-left:1.5rem"><div class="wp-block-group__inner-container is-layout-constrained wp-container-core-group-is-layout-eed7543b wp-block-group-is-layout-constrained">

<figure class="wp-block-image size-large"><img decoding="async" width="1080" height="645" src="https://www.gironi.it/blog/wp-content/uploads/2026/07/ab-test-differenza-1.png" alt="La distribuzione dell'uplift B − A in punti percentuali: l'area a destra dello zero è la probabilità che B vinca (98,5%), la barra azzurra l'intervallo di credibilità al 95%, da 0,31 a 5,68 punti. Un solo grafico risponde a entrambe le domande — «se» e «di quanto»." class="wp-image-4214" srcset="https://www.gironi.it/blog/wp-content/uploads/2026/07/ab-test-differenza-1.png 1080w, https://www.gironi.it/blog/wp-content/uploads/2026/07/ab-test-differenza-1-300x179.png 300w, https://www.gironi.it/blog/wp-content/uploads/2026/07/ab-test-differenza-1-1024x612.png 1024w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /><figcaption class="wp-element-caption">La distribuzione dell&#8217;uplift B − A in punti percentuali: l&#8217;area a destra dello zero è la probabilità che B vinca (98,5%), la barra azzurra l&#8217;intervallo di credibilità al 95%, da 0,31 a 5,68 punti. Un solo grafico risponde a entrambe le domande — «se» e «di quanto».</figcaption></figure>

</div></div>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="quando-fermarsi">Quando fermarsi: expected loss e il problema del peeking</h2>



<p class="wp-block-paragraph">Nell&#8217;articolo sull&#8217;A/B testing classico abbiamo dedicato spazio a uno degli errori più insidiosi: il <a href="https://www.gironi.it/blog/peeking-problem/">peeking</a>, cioè sbirciare i dati intermedi e fermarsi appena la differenza sembra significativa. Nell&#8217;impianto frequentista questo gonfia il tasso di falsi positivi, perché ogni occhiata è di fatto un nuovo test sulla stessa ipotesi nulla, e i test ripetuti moltiplicano le occasioni di sbagliare.</p>



<p class="wp-block-paragraph">L&#8217;approccio bayesiano cambia la natura del problema. Qui non ripetiamo un test su un&#8217;ipotesi nulla: aggiorniamo una credenza. Il posterior di oggi diventa il prior di domani, e guardare i dati man mano non &#8220;consuma&#8221; un budget di errore nello stesso modo. Questo non vuol dire che possiamo fermarci a capriccio: ci serve comunque una <strong>regola di arresto</strong> dichiarata in anticipo. E la regola bayesiana naturale non è &#8220;fermati quando P(B&gt;A) è alta&#8221;, ma si basa sull&#8217;<strong>expected loss</strong> — la perdita attesa.</p>



<p class="wp-block-paragraph">L&#8217;idea è questa: se scegliamo B ma in realtà fosse A la variante migliore, sbagliamo, e l&#8217;entità dell&#8217;errore è quanto A batte B in quei casi. La perdita attesa di scegliere B è la media di questo &#8220;rimpianto&#8221; su tutta l&#8217;incertezza residua. A parole: di quanto, in media, ci pentiremmo di aver scelto B se ci sbagliassimo.</p>



<p class="wp-block-paragraph">Calcolo in R la perdita attesa scegliendo B:</p>



<pre class="wp-block-code"><code># Expected loss scegliendo B: perdita attesa se in realta' A fosse meglio
loss_B &lt;- mean(pmax(postA - postB, 0))
cat("expected loss scegliendo B =", round(loss_B*100, 3), "punti %\n")</code></pre>



<p class="wp-block-paragraph">Output: expected loss scegliendo B = 0.007 punti %.</p>



<p class="wp-block-paragraph">La perdita attesa scegliendo B è di appena 0,007 punti percentuali: trascurabile. In termini più chiari e diretti, anche nello scenario sfortunato in cui ci sbagliassimo, il danno medio sarebbe minuscolo. Si fissa allora una soglia di tolleranza <em>prima</em> di iniziare — per esempio &#8220;mi fermo quando la perdita attesa scende sotto 0,01 punti&#8221; — e si lascia correre il test finché non la si raggiunge.</p>



<p class="wp-block-paragraph">Ed è qui che si vede perché la perdita attesa, e non &#8220;P(B&gt;A) è alta&#8221;, è il criterio giusto. Se rifacciamo i conti a ogni tappa del test, man mano che il traffico arriva, le due grandezze non scattano nello stesso momento: la probabilità che B vinca supera il 95% molto prima che la perdita attesa diventi davvero trascurabile.</p>



<div class="wp-block-group has-background" style="background-color:#f5f7f9;margin-top:2.5rem;margin-bottom:2.5rem;padding-top:1.5rem;padding-right:1.5rem;padding-bottom:1rem;padding-left:1.5rem"><div class="wp-block-group__inner-container is-layout-constrained wp-container-core-group-is-layout-eed7543b wp-block-group-is-layout-constrained">

<figure class="wp-block-image size-large"><img decoding="async" width="1140" height="600" src="https://www.gironi.it/blog/wp-content/uploads/2026/07/ab-test-expected-loss-1.png" alt="Al crescere dei dati P(B&gt;A) supera il 95% già intorno ai 570 visitatori per variante, ma la perdita attesa scende sotto la soglia di 0,01 punti solo verso gli 855: fermarsi appena la probabilità «sembra alta» è precisamente la trappola del peeking." class="wp-image-4215" srcset="https://www.gironi.it/blog/wp-content/uploads/2026/07/ab-test-expected-loss-1.png 1140w, https://www.gironi.it/blog/wp-content/uploads/2026/07/ab-test-expected-loss-1-300x158.png 300w, https://www.gironi.it/blog/wp-content/uploads/2026/07/ab-test-expected-loss-1-1024x539.png 1024w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /><figcaption class="wp-element-caption">Al crescere dei dati P(B&gt;A) supera il 95% già intorno ai 570 visitatori per variante, ma la perdita attesa scende sotto la soglia di 0,01 punti solo verso gli 855: fermarsi appena la probabilità «sembra alta» è precisamente la trappola del peeking.</figcaption></figure>

</div></div>



<div class="wp-block-group has-background" style="background-color:#f5f7f9;margin-top:2.5rem;margin-bottom:2.5rem;padding-top:1.5rem;padding-right:1.5rem;padding-bottom:1.5rem;padding-left:1.5rem"><div class="wp-block-group__inner-container is-layout-flow wp-block-group-is-layout-flow">
<p>Un avvertimento: la libertà di guardare i dati in corsa non è un permesso di fermarsi quando il risultato ci aggrada. La regola di arresto — la soglia di expected loss, o un livello minimo di P(B&gt;A) — va fissata prima di raccogliere i dati, esattamente come nel frequentista si fissa la dimensione campionaria. Il rigore non sta nel metodo che usiamo, ma nel decidere il criterio prima di vedere i numeri.</p>
</div></div>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="prova-tu">Prova tu</h2>



<p class="wp-block-paragraph">Un sito di lead generation testa due varianti del modulo di contatto. Sulla variante A si osservano 45 conversioni su 600 sessioni; sulla B, 52 conversioni su 600 sessioni.</p>



<ol class="wp-block-list"><li>Costruisci i due posterior con prior non informativo Beta(1, 1): <code>postA &lt;- rbeta(1e5, 1 + 45, 1 + 555)</code> e l&#8217;analogo per B.</li><li>Calcola <strong>P(B&gt;A)</strong>: B è migliore con probabilità abbastanza alta da convincerti?</li><li>Calcola <strong>uplift medio e intervallo al 95%</strong> della differenza: l&#8217;intervallo tocca lo zero?</li><li>Calcola la <strong>expected loss</strong> scegliendo B. Con questi numeri (più vicini tra loro del caso visto sopra), come cambia rispetto all&#8217;esempio dell&#8217;articolo?</li></ol>



<p class="wp-block-paragraph">Suggerimento: la struttura del codice è identica a quella che abbiamo usato. Cambiano solo i conteggi di partenza — e il risultato, molto meno netto, è proprio il motivo per cui l&#8217;intervallo e la perdita attesa contano più di un semplice &#8220;ha vinto B&#8221;.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p class="wp-block-paragraph">Finora abbiamo confrontato due varianti a campione fisso: raccogliamo i dati, calcoliamo, decidiamo. Eppure la probabilità che B sia la migliore l&#8217;abbiamo appena vista ricalcolarsi a ogni tappa, mentre il traffico arriva — e se una variante si sta rivelando nettamente migliore, perché continuare a mandare metà dei visitatori su quella peggiore? Si può fare di meglio: usare quella probabilità in tempo reale per allocare il traffico in modo adattivo, spostandolo verso la variante che sta vincendo mentre il test è ancora in corso. È il salto dal test al <em>bandit</em>, l&#8217;argomento del prossimo articolo.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading" id="per-approfondire">Per approfondire</h3>



<p class="wp-block-paragraph">Se vuoi approfondire l&#8217;A/B testing bayesiano con un taglio pratico e orientato al codice, <a href="https://www.amazon.it/dp/0133902838?tag=consulenzeinf-21" rel="nofollow sponsored noopener" target="_blank"><em>Bayesian Methods for Hackers</em></a> di Cameron Davidson-Pilon è il libro che consiglio. Affronta il ragionamento bayesiano partendo dalla programmazione invece che dalla matematica formale, e dedica un capitolo esplicito proprio al confronto bayesiano tra varianti — probabilità che B vinca, distribuzione della differenza, perdita attesa. È pensato per chi impara meglio leggendo codice che dimostrazioni.</p>



<p class="wp-block-paragraph">Questo articolo fa parte del percorso <a href="https://www.gironi.it/blog/approccio-bayesiano/">«L&#8217;approccio bayesiano»</a>, la guida ragionata agli articoli su statistica e inferenza bayesiana applicate alla SEO.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.gironi.it/blog/ab-test-bayesiano/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Stima bayesiana di un conversion rate: quanto possiamo fidarci dei pochi dati che abbiamo</title>
		<link>https://www.gironi.it/blog/conversion-rate-bayesiano/</link>
					<comments>https://www.gironi.it/blog/conversion-rate-bayesiano/#respond</comments>
		
		<dc:creator><![CDATA[Paolo Gironi]]></dc:creator>
		<pubDate>Thu, 25 Jun 2026 08:34:36 +0000</pubDate>
				<category><![CDATA[statistica]]></category>
		<guid isPermaLink="false">https://www.gironi.it/blog/?p=3871</guid>

					<description><![CDATA[Abbiamo avuto modo di esaminare, nell&#8217;articolo sulle fondamenta della statistica bayesiana, come l&#8217;aggiornamento bayesiano funzioni con la simulazione: generiamo campioni dal prior, simuliamo i dati, filtriamo. Un metodo intuitivo, ma che si scontra con un limite pratico non appena i dati diventano minimamente numerosi. In questo articolo passiamo alla soluzione analitica elegante che l&#8217;approccio bayesiano &#8230; <a href="https://www.gironi.it/blog/conversion-rate-bayesiano/" class="more-link">Leggi tutto<span class="screen-reader-text"> "Stima bayesiana di un conversion rate: quanto possiamo fidarci dei pochi dati che abbiamo"</span></a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Abbiamo avuto modo di esaminare, nell&#8217;articolo sulle <a href="https://www.gironi.it/blog/statistica-bayesiana/">fondamenta della statistica bayesiana</a>, come l&#8217;aggiornamento bayesiano funzioni con la simulazione: generiamo campioni dal prior, simuliamo i dati, filtriamo. Un metodo intuitivo, ma che si scontra con un limite pratico non appena i dati diventano minimamente numerosi.<br> In questo articolo passiamo alla soluzione analitica elegante che l&#8217;approccio bayesiano mette a disposizione per uno dei problemi più comuni nell&#8217;analisi di marketing: stimare un conversion rate con pochi dati.</p>



<p class="wp-block-paragraph">Il problema nasce sempre allo stesso modo. Un piccolo e-commerce ha raccolto 23 conversioni su 412 sessioni. Il tasso grezzo è 23/412 ≈ 5,6%. Un numero preciso, apparentemente. Ma quanta fiducia gli diamo? Potremmo trovarci davanti al 3% vero o al 9% vero — con quel campione, non lo sappiamo. La stima puntuale &#8220;5,6%&#8221; non dice niente sulla propria incertezza.</p>



<span id="more-3871"></span>



<p class="wp-block-paragraph"><strong>Di cosa parleremo</strong>:</p>



<ul class="wp-block-list"><li><a href="#modello-beta-binomiale">Il modello Beta-Binomiale: perché la Beta è la distribuzione naturale per un conversion rate</a></li><li><a href="#prior-non-informativo">Prior non informativo: lasciar parlare i dati</a></li><li><a href="#prior-informativo">Prior informativo: usare lo storico senza barare</a></li><li><a href="#posterior-prior-domani">Il posterior di oggi è il prior di domani</a></li><li><a href="#prova-tu">Prova tu</a></li><li><a href="#per-approfondire">Per approfondire</a></li></ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="modello-beta-binomiale">Dal click singolo al tasso: il modello Beta-Binomiale</h2>



<p class="wp-block-paragraph">Ogni sessione è un evento binario: l&#8217;utente converte oppure no.<br> Con \( n \) sessioni e \( k \) conversioni, il meccanismo generativo è binomiale. Il parametro che vogliamo stimare — il vero tasso di conversione \( \theta \) — è una proporzione: un valore tra 0 e 1.</p>



<p class="wp-block-paragraph">Quando il prior su \( \theta \) è una <a href="https://www.gironi.it/blog/la-distribuzione-beta-spiegata-semplice/">distribuzione Beta</a> e i dati sono binomiali, accade qualcosa di molto comodo: <strong>il posterior è ancora una distribuzione Beta</strong>. Questo si chiama prior <em>coniugato</em>, e significa che l&#8217;aggiornamento bayesiano si riduce a una semplice operazione aritmetica sui parametri.</p>



<p class="wp-block-paragraph">La regola di aggiornamento è questa: se il prior è Beta(α, β), dopo aver osservato \( k \) conversioni su \( n \) sessioni il posterior è:</p>



\( Beta(\alpha + k,\ \beta + (n &#8211; k)) \\ \)



<p class="wp-block-paragraph">In parole: aggiungiamo le conversioni osservate ad α e i fallimenti osservati a β. Il prior Beta(α, β) raccoglie in α le &#8220;conversioni già viste&#8221; (o una credenza equivalente) e in β i &#8220;non-click&#8221;. Ogni nuovo dato aggiorna entrambi i contatori.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="prior-non-informativo">Prior non informativo: lasciar parlare i dati</h2>



<p class="wp-block-paragraph">Il prior più neutro possibile su una proporzione è la distribuzione uniforme su [0, 1], che corrisponde a Beta(1, 1): tutti i valori del tasso sono considerati ugualmente plausibili prima di vedere i dati.</p>



<p class="wp-block-paragraph">Il nostro caso: 23 conversioni su 412 sessioni (389 non-conversioni).</p>



<p class="wp-block-paragraph">Calcoliamo il posterior in R:</p>



<pre class="wp-block-code"><code># Dati osservati
conv &lt;- 23; sess &lt;- 412; nonconv &lt;- sess - conv

# Prior NON informativo: uniforme Beta(1,1)
a0 &lt;- 1; b0 &lt;- 1
a1 &lt;- a0 + conv; b1 &lt;- b0 + nonconv      # posterior Beta(24, 390)

cat("Non-informativo: media =", round(a1/(a1+b1), 4), "\n")
cat("  CI 95% =", round(qbeta(c(.025,.975), a1, b1), 4), "\n")</code></pre>



<p class="wp-block-paragraph">Output: media = 0.058, CI 95% = [0.0376, 0.0824].</p>



<p class="wp-block-paragraph"><strong>Il posterior Beta(24, 390) ha media 5,8% e credible interval al 95% compreso tra 3,8% e 8,2%.</strong><br> Il credible interval bayesiano non è un esercizio statistico astratto: significa direttamente che c&#8217;è il 95% di probabilità che il vero tasso di conversione sia tra il 3,8% e l&#8217;8,2%. Non una frequenza su ripetizioni infinite — una probabilità diretta sul parametro.</p>



<figure class="wp-block-image size-large"><img decoding="async" width="975" height="600" src="https://www.gironi.it/blog/wp-content/uploads/2026/07/conversion-rate-posterior.png" alt="Prior non informativo Beta(1,1) e posterior Beta(24,390) dopo 23 conversioni su 412 sessioni: i dati concentrano la stima attorno al 5,8%." class="wp-image-3950" srcset="https://www.gironi.it/blog/wp-content/uploads/2026/07/conversion-rate-posterior.png 975w, https://www.gironi.it/blog/wp-content/uploads/2026/07/conversion-rate-posterior-300x185.png 300w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /><figcaption class="wp-element-caption">Prior non informativo Beta(1,1) e posterior Beta(24,390) dopo 23 conversioni su 412 sessioni: i dati concentrano la stima attorno al 5,8%.</figcaption></figure>



<p class="wp-block-paragraph">Con 412 sessioni, l&#8217;incertezza è ancora apprezzabile: quasi 5 punti percentuali di ampiezza. La stima puntuale 5,6% era fuorviante nella sua precisione.</p>



<p class="has-light-gray-background-color has-background wp-block-paragraph">Un avvertimento: il credible interval bayesiano e l&#8217;intervallo di confidenza frequentista hanno numeri simili ma significati profondamente diversi. Il 95% frequentista è una proprietà della procedura (&#8220;se ripetessi l&#8217;esperimento 100 volte, 95 intervalli conterrebbero il vero parametro&#8221;); il 95% bayesiano è un&#8217;affermazione diretta sul parametro nel caso specifico che stiamo analizzando.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="prior-informativo">Prior informativo: usare lo storico senza barare</h2>



<p class="wp-block-paragraph">Il prior non informativo è il punto di partenza onesto quando non sappiamo nulla. Ma spesso sappiamo qualcosa: anni di campagne, storico del settore, benchmark di categoria.<br> Il nostro e-commerce ha uno storico di quattro stagioni con un conversion rate medio intorno al 4%. Come traduciamo questa conoscenza in un prior?</p>



<p class="wp-block-paragraph">La distribuzione Beta(8, 192) ha media esattamente 8/(8+192) ≈ 4% e — grazie alla somma α+β = 200 — ha una concentrazione che equivale a &#8220;credere&#8221; ai nostri dati quanto si crede a 200 sessioni fittizie di storico. Non è un numero arbitrario: è una scelta dichiarata e verificabile.</p>



<p class="wp-block-paragraph">Calcoliamo il posterior informativo in R:</p>



<pre class="wp-block-code"><code># Prior INFORMATIVO da storico: media ~4% -&gt; Beta(8, 192)
a0i &lt;- 8; b0i &lt;- 192
a1i &lt;- a0i + conv; b1i &lt;- b0i + nonconv  # posterior Beta(31, 581)

cat("Informativo: media =", round(a1i/(a1i+b1i), 4), "\n")
cat("  CI 95% =", round(qbeta(c(.025,.975), a1i, b1i), 4), "\n")</code></pre>



<p class="wp-block-paragraph">Output: media = 0.0507, CI 95% = [0.0347, 0.0694].</p>



<p class="wp-block-paragraph"><strong>Il posterior informativo Beta(31, 581) dà media 5,1% e credible interval al 95% tra 3,5% e 6,9%.</strong><br> Due cose da notare. Prima: la media scende leggermente da 5,8% a 5,1% — il prior &#8220;tira&#8221; la stima verso il 4% dello storico. Seconda: l&#8217;intervallo si stringe (da 4,4 a 3,4 punti percentuali) — lo storico vale come informazione aggiuntiva, quindi l&#8217;incertezza cala.</p>



<p class="wp-block-paragraph">Con pochi dati, il prior informativo aiuta: aggiunge informazione dove i dati non bastano. Con molti dati — migliaia di sessioni — il prior viene <em>sommerso</em> dai dati e la differenza tra prior informativo e non informativo diventa trascurabile. Questa è una caratteristica fondamentale dell&#8217;inferenza bayesiana: <strong>il prior conta quando i dati sono pochi; i dati vincono sempre, alla lunga</strong>.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="posterior-prior-domani">Il posterior di oggi è il prior di domani</h2>



<p class="wp-block-paragraph">L&#8217;eleganza più pratica dell&#8217;approccio bayesiano è l&#8217;aggiornamento sequenziale. Dopo un mese arrivano nuovi dati: 15 conversioni su 300 sessioni aggiuntive. Non dobbiamo ricominciare da zero — il posterior che abbiamo calcolato <em>diventa</em> il nuovo prior.</p>



<p class="wp-block-paragraph">Aggiorniamo in R:</p>



<pre class="wp-block-code"><code># Nuovi dati: 15 conversioni su 300 sessioni aggiuntive
conv2 &lt;- 15; sess2 &lt;- 300
a2 &lt;- a1i + conv2; b2 &lt;- b1i + (sess2 - conv2)   # Beta(46, 866)

cat("Dopo update: media =", round(a2/(a2+b2), 4), "\n")
cat("  CI 95% =", round(qbeta(c(.025,.975), a2, b2), 4), "\n")</code></pre>



<p class="wp-block-paragraph">Output: media = 0.0504, CI 95% = [0.0372, 0.0655].</p>



<p class="wp-block-paragraph">Tre stadi a confronto:</p>



<figure class="wp-block-table"><table><thead><tr><th>Stadio</th><th>Dati accumulati</th><th>Media</th><th>CI 95%</th></tr></thead><tbody><tr><td>Prior puro (prima dei dati)</td><td>—</td><td>4,0%</td><td>[1,8%, 7,1%]</td></tr><tr><td>Dopo primo mese</td><td>23/412</td><td>5,1%</td><td>[3,5%, 6,9%]</td></tr><tr><td>Dopo secondo mese</td><td>38/712</td><td>5,0%</td><td>[3,7%, 6,6%]</td></tr></tbody></table></figure>



<p class="wp-block-paragraph"><strong>Il credible interval si è ristretto</strong> ad ogni stadio: 5,3 punti percentuali nel prior puro, 3,4 dopo il primo mese, 2,8 dopo l&#8217;aggiornamento. La media è rimasta stabile: i nuovi dati confermano la stima precedente invece di spostarla, e l&#8217;incertezza cala come atteso.<br> Questo è il vero vantaggio operativo: non occorre aspettare che si accumuli un campione &#8220;abbastanza grande&#8221; prima di fare qualsiasi stima. Si parte da una stima incerta e la si affina progressivamente, con ogni nuovo dato.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="prova-tu">Prova tu</h2>



<p class="wp-block-paragraph">Un sito di lead generation ha un tasso di conversione storico intorno al 2%. Dopo una campagna di ottimizzazione, su 150 sessioni si osservano 8 conversioni.</p>



<ol class="wp-block-list"><li>Costruisci un <strong>prior informativo</strong> che rifletta il 2% storico: prova Beta(4, 196) — ha media esattamente 2%.</li><li>Calcola il <strong>posterior</strong> dopo le 8 conversioni su 150 sessioni.</li><li>Calcola il <strong>credible interval al 95%</strong>.</li><li>Calcola poi con <strong>prior non informativo</strong> Beta(1, 1): il posterior cambia molto? Perché?</li></ol>



<p class="wp-block-paragraph">Suggerimento: la formula è sempre la stessa — <code>qbeta(c(.025, .975), a0 + conv, b0 + nonconv)</code>. L&#8217;unica cosa che cambia è il punto di partenza.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p class="wp-block-paragraph">Quello che abbiamo costruito finora è la stima di un tasso su una singola variante. Il passo successivo è confrontare due varianti — una pagina di controllo e una pagina modificata — e calcolare la probabilità bayesiana che una superi l&#8217;altra. È esattamente quello che faremo nel prossimo articolo: l&#8217;A/B test bayesiano.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading" id="per-approfondire">Per approfondire</h3>



<p class="wp-block-paragraph">Se vuoi capire come il ragionamento bayesiano entra nelle decisioni pratiche — dalla previsione dei mercati alla stima dell&#8217;incertezza nei dati reali — <a href="https://www.amazon.it/dp/8860443865?tag=consulenzeinf-21" rel="nofollow sponsored noopener" target="_blank"><em>Il segnale e il rumore</em></a> di Nate Silver è il libro che consiglio. Silver dedica capitoli espliciti all&#8217;aggiornamento bayesiano, mostrandolo in contesti concreti (previsioni meteorologiche, politica, sport) che rendono immediato il concetto di &#8220;aggiornare le credenze con nuovi dati&#8221;. È divulgativo ma rigoroso, e si legge d&#8217;un fiato.</p>



<p class="wp-block-paragraph">Questo articolo fa parte del percorso <a href="https://www.gironi.it/blog/approccio-bayesiano/">«L&#8217;approccio bayesiano»</a>, la guida ragionata agli articoli su statistica e inferenza bayesiana applicate alla SEO.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.gironi.it/blog/conversion-rate-bayesiano/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Il peeking problem: perché sbirciare l&#8217;A/B test gonfia i falsi positivi</title>
		<link>https://www.gironi.it/blog/peeking-problem/</link>
					<comments>https://www.gironi.it/blog/peeking-problem/#respond</comments>
		
		<dc:creator><![CDATA[Paolo Gironi]]></dc:creator>
		<pubDate>Tue, 23 Jun 2026 09:40:33 +0000</pubDate>
				<category><![CDATA[statistica]]></category>
		<guid isPermaLink="false">https://www.gironi.it/blog/?p=3846</guid>

					<description><![CDATA[Il 21 gennaio 2015 Optimizely — una delle piattaforme di A/B testing più usate al mondo — accese per tutti i suoi clienti un motore statistico completamente nuovo, il New Stats Engine. Non era un capriccio tecnico: il vecchio motore, costruito attorno a un classico t-test a orizzonte fisso (Fixed Horizon) e sviluppato con statistici &#8230; <a href="https://www.gironi.it/blog/peeking-problem/" class="more-link">Leggi tutto<span class="screen-reader-text"> "Il peeking problem: perché sbirciare l&#8217;A/B test gonfia i falsi positivi"</span></a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Il 21 gennaio 2015 Optimizely — una delle piattaforme di A/B testing più usate al mondo — accese per tutti i suoi clienti un motore statistico completamente nuovo, il <em>New Stats Engine</em>. <br>Non era un capriccio tecnico: il vecchio motore, costruito attorno a un classico t-test a orizzonte fisso (<em>Fixed Horizon</em>) e sviluppato con statistici di Stanford, aveva un difetto che riguardava chiunque guardasse i risultati di un test prima della fine. E noi i risultati di un test li guardiamo <em>sempre</em> prima della fine.</p>



<p class="wp-block-paragraph">Il problema lo avevano misurato loro stessi, simulando dei test A/A — due varianti identiche, dove per costruzione nessuna è migliore dell&#8217;altra, quindi qualunque &#8220;vincitore&#8221; dichiarato è un falso allarme. <br>Stando ai dati pubblicati da Optimizely, su test da 5.000 visitatori chi controllava i numeri dopo <em>ogni</em> visitatore vedeva il <strong>57% dei test A/A dichiarare un falso vincitore almeno una volta</strong>; controllando ogni 500 visitatori il numero scendeva al 26%, ogni 1.000 al 20%. Numeri da brivido per uno strumento che dovrebbe servire a decidere con rigore. La riscrittura — inferenza sequenziale più controllo del false discovery rate, quella che chiamano always-valid — serviva proprio a riportare l&#8217;errore, come scrivevano loro, &#8220;da oltre il 30% al 5%&#8221;.</p>



<p class="wp-block-paragraph">È lo stesso inganno che abbiamo incontrato chiudendo l&#8217;articolo sulla <a href="https://www.gironi.it/blog/regressione-verso-la-media/">regressione verso la media</a>: lì selezionavamo le pagine messe peggio — un istante estremo nello <em>spazio</em> dei dati — e ci facevamo ingannare dal loro rientro. Qui selezioniamo un istante estremo nel <em>tempo</em>: ci fermiamo appena il test ci dà ragione. Il meccanismo è cugino, il rischio identico.</p>



<span id="more-3846"></span>



<h2 class="wp-block-heading">Cos&#8217;è il peeking</h2>



<p class="wp-block-paragraph">Chi gestisce un <a href="https://www.gironi.it/blog/ab-testing/">A/B test</a> lo conosce bene: il test è in corso, i dati arrivano giorno dopo giorno, e la tentazione di sbirciare la dashboard è irresistibile. <br>Il <em>peeking</em> — letteralmente &#8220;sbirciare&#8221; — non è il semplice atto di guardare: è guardare <em>riservandosi di fermare il test</em> nel momento in cui il risultato diventa significativo. È quel &#8220;ottimo, la variante B ha superato la soglia, chiudiamo qui e dichiariamo il vincitore&#8221; detto a metà raccolta dati.</p>



<p class="wp-block-paragraph">Il punto delicato è che ogni sguardo accompagnato dalla possibilità di fermarsi <strong>è un test statistico in più</strong>. <br>Un singolo test con soglia al 5% accetta, per definizione, un 5% di probabilità di gridare al vincitore quando in realtà non c&#8217;è alcuna differenza. Ma se quello stesso test lo ripetiamo venti volte lungo la raccolta, e ci basta che <em>una sola</em> di quelle venti volte superi la soglia per fermarci e cantare vittoria, allora le probabilità di inciampare in un falso positivo non sono più il 5%: si <strong>accumulano</strong> a ogni sguardo.</p>



<p class="wp-block-paragraph">Non è la solita molteplicità di chi confronta dieci varianti insieme. Qui la molteplicità è nascosta nel <em>tempo</em>: una sola variante, guardata tante volte. È la stessa logica per cui se si lancia una moneta una volta sola un risultato strano è raro, ma se ci si concede di guardare dopo ogni lancio e di fermarsi al primo momento favorevole, prima o poi quel momento arriva — e lo si scambia per un segnale.</p>



<h2 class="wp-block-heading">Quanto costa sbirciare: una simulazione</h2>



<p class="wp-block-paragraph">Le parole convincono fino a un certo punto; i numeri molto di più. Simulo in R un A/A test, cioè due varianti con <strong>esattamente lo stesso</strong> tasso di conversione vero (il 10%): qualunque differenza che emerge è rumore, e qualunque &#8220;vittoria&#8221; dichiarata è un falso positivo per costruzione. <br>Preparo il terreno fissando il seme del generatore casuale (così i numeri sono riproducibili), la funzione che calcola il p-value del confronto tra due proporzioni, e la funzione che simula un singolo esperimento e dice se a un certo punto ha dichiarato un (falso) vincitore:</p>



<pre class="wp-block-code"><code>set.seed(2025)

p_vero  &lt;- 0.10    # stesso conversion rate per A e B (H0 vera)
n_arm   &lt;- 2000    # visitatori per variante a fine test
n_sim   &lt;- 4000    # numero di esperimenti simulati
alpha   &lt;- 0.05
sguardi &lt;- 20      # quante volte "sbirciamo" durante la raccolta
look_at &lt;- round(seq(n_arm / sguardi, n_arm, length.out = sguardi))

# p-value z-test due proporzioni, due code
pval_ab &lt;- function(xa, na, xb, nb) {
  pp &lt;- (xa + xb) / (na + nb)
  se &lt;- sqrt(pp * (1 - pp) * (1 / na + 1 / nb))
  2 * pnorm(-abs((xa / na - xb / nb) / se))
}

# un esperimento A/A: TRUE se dichiara un (falso) vincitore
esperimento &lt;- function(soglia, guarda) {
  a &lt;- cumsum(rbinom(n_arm, 1, p_vero))
  b &lt;- cumsum(rbinom(n_arm, 1, p_vero))
  for (k in guarda) {
    p &lt;- pval_ab(a[k], k, b[k], k)
    if (!is.na(p) &amp;&amp; p &lt; soglia) return(TRUE)
  }
  FALSE
}</code></pre>



<p class="wp-block-paragraph">Cominciamo dal comportamento corretto: un solo test, alla fine, sui 2.000 visitatori per variante. Lo eseguo 4.000 volte e conto quante dichiarano un vincitore:</p>



<pre class="wp-block-code"><code># orizzonte fisso: un solo test, alla fine
fisso &lt;- mean(replicate(n_sim, esperimento(alpha, n_arm)))
cat(sprintf("Orizzonte fisso: %.1f%% falsi positivi\n", 100 * fisso))
# Orizzonte fisso: 5.0% falsi positivi</code></pre>



<p class="wp-block-paragraph">Esce <strong>5,0%</strong>: esattamente il livello che avevamo dichiarato con la soglia al 5%. Il test, usato come si deve, mantiene la promessa. <br>Ora cambio una cosa sola: invece di guardare una volta alla fine, guardo venti volte durante la raccolta e mi fermo al primo momento in cui il p-value scende sotto 0,05. Aggiungo gli sguardi intermedi e rieseguo:</p>



<pre class="wp-block-code"><code># peeking: test a ogni sguardo, stop al primo significativo
peek &lt;- mean(replicate(n_sim, esperimento(alpha, look_at)))
cat(sprintf("Peeking (%d sguardi): %.1f%% falsi positivi\n", sguardi, 100 * peek))
# Peeking (20 sguardi): 24.3% falsi positivi</code></pre>



<p class="wp-block-paragraph">Da <strong>5,0%</strong> a <strong>24,3%</strong>. <br><strong>Gli stessi dati, lo stesso test, la stessa soglia: l&#8217;unica cosa cambiata è quando abbiamo deciso di guardare, e il tasso di falsi positivi è quasi quintuplicato.</strong> Quasi un test A/A su quattro, in cui le due varianti sono identiche per costruzione, ci convince di aver trovato un vincitore. Il 24,3% della nostra simulazione e il 30% riportato da Optimizely raccontano la stessa storia con dati diversi: sbirciare non è un peccato veniale, è il modo più efficace per ingannarsi da soli.</p>



<div class="wp-block-group has-background" style="background-color:#f5f7f9;margin-top:2.5rem;margin-bottom:2.5rem;padding-top:1.5rem;padding-right:1.5rem;padding-bottom:1rem;padding-left:1.5rem"><div class="wp-block-group__inner-container is-layout-constrained wp-container-core-group-is-layout-eed7543b wp-block-group-is-layout-constrained">

<figure class="wp-block-image size-large"><img decoding="async" width="1020" height="660" src="https://www.gironi.it/blog/wp-content/uploads/2026/07/peeking-inflazione.png" alt="Il tasso di falsi positivi in un test A/A (dove per costruzione nessuna variante è migliore) al crescere del numero di sguardi. Tenendo fissi i dati simulati e cambiando solo quante volte sbirciamo, si passa dal 5% nominale di un unico test a fine raccolta a circa il 25% con venti sguardi: la frequenza degli sguardi è la benzina del problema." class="wp-image-4068" srcset="https://www.gironi.it/blog/wp-content/uploads/2026/07/peeking-inflazione.png 1020w, https://www.gironi.it/blog/wp-content/uploads/2026/07/peeking-inflazione-300x194.png 300w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /><figcaption class="wp-element-caption">Il tasso di falsi positivi in un test A/A (dove per costruzione nessuna variante è migliore) al crescere del numero di sguardi. Tenendo fissi i dati simulati e cambiando solo quante volte sbirciamo, si passa dal 5% nominale di un unico test a fine raccolta a circa il 25% con venti sguardi: la frequenza degli sguardi è la benzina del problema.</figcaption></figure>

</div></div>



<h2 class="wp-block-heading">Soluzione 1: l&#8217;orizzonte fisso</h2>



<p class="wp-block-paragraph">La cura più semplice è anche la più antipatica: decidere <em>prima</em> quanti dati raccogliere, e poi avere la disciplina di aspettare fino alla fine senza fermarsi in anticipo, qualunque cosa dica la dashboard nel frattempo. <br>È quello che la simulazione ci ha appena mostrato: con un solo test alla fine, il falso positivo resta inchiodato al 5% promesso. Nessuna magia, solo l&#8217;aver eliminato gli sguardi opportunistici.</p>



<p class="wp-block-paragraph">&#8220;Quanti dati&#8221; non è una cifra a caso: dipende da quanto è piccola la differenza che vogliamo essere in grado di cogliere e da quanta certezza pretendiamo. È il calcolo della dimensione campionaria, che si fa prima di lanciare il test con il nostro <a href="https://www.gironi.it/blog/calcolatore-significativita-ab-test/">calcolatore di significatività</a> e che poggia sui concetti di <a href="https://www.gironi.it/blog/effect-size-e-power-analysis/">effect size e power analysis</a>. <br>Fissato quel numero, l&#8217;orizzonte fisso è la strada più sicura: nessuna correzione statistica da applicare, nessuna soglia da ritoccare. Si paga però un prezzo in pazienza — bisogna resistere alla curiosità per giorni o settimane — e questo, nella realtà operativa, è esattamente ciò che quasi nessuno riesce a fare.</p>



<h2 class="wp-block-heading">Soluzione 2: guardare senza barare</h2>



<p class="wp-block-paragraph">E se monitorare in corsa fosse davvero necessario — perché un test che sta andando malissimo va fermato, perché gli stakeholder vogliono aggiornamenti? <br>Allora la strada non è guardare di nascosto con la soglia di sempre, ma guardare <em>apertamente</em> con una soglia più severa. L&#8217;idea è semplice: se a ogni sguardo alziamo l&#8217;asticella, rendendo più difficile gridare al vincitore in ciascuna occasione, possiamo fare in modo che l&#8217;errore <strong>complessivo</strong> — sommato su tutti gli sguardi — resti il 5% che volevamo. Calibro in R la soglia per-sguardo, provando valori via via più stringenti sugli stessi venti sguardi di prima:</p>



<pre class="wp-block-code"><code># soglia per-sguardo più severa che riporta l'errore complessivo ~5%
for (sg in c(0.05, 0.02, 0.01, 0.005)) {
  fp &lt;- mean(replicate(n_sim, esperimento(sg, look_at)))
  cat(sprintf("  soglia %.3f -&gt; %.1f%% complessivo\n", sg, 100 * fp))
}
#   soglia 0.050 -&gt; 25.1% complessivo
#   soglia 0.020 -&gt; 11.7% complessivo
#   soglia 0.010 -&gt;  6.6% complessivo
#   soglia 0.005 -&gt;  3.3% complessivo</code></pre>



<p class="wp-block-paragraph">Come si vede, la soglia abituale dello 0,05 produce un 25,1% di errore complessivo (di nuovo il disastro del peeking), ma man mano che la rendiamo più severa l&#8217;errore rientra: <strong>intorno allo 0,01 — una soglia cinque volte più stringente di quella standard — l&#8217;errore complessivo torna vicino al 5% nominale.</strong> È il prezzo da pagare per il diritto di sbirciare: a ogni singolo sguardo si pretende molta più evidenza, in cambio della libertà di guardare spesso.</p>



<div class="wp-block-group has-background" style="background-color:#f5f7f9;margin-top:2.5rem;margin-bottom:2.5rem;padding-top:1.5rem;padding-right:1.5rem;padding-bottom:1rem;padding-left:1.5rem"><div class="wp-block-group__inner-container is-layout-constrained wp-container-core-group-is-layout-eed7543b wp-block-group-is-layout-constrained">

<figure class="wp-block-image size-large"><img decoding="async" width="1020" height="660" src="https://www.gironi.it/blog/wp-content/uploads/2026/07/peeking-soglia.png" alt="L'effetto di alzare l'asticella a ogni sguardo (sempre venti sguardi). Con la soglia standard 0,05 l'errore complessivo è il 25,1% del peeking; stringendo la soglia per-sguardo l'errore scende, e intorno a 0,01 torna vicino al 5% nominale (la linea tratteggiata). È la versione artigianale, a soglia costante, di ciò che i confini di Pocock o O'Brien-Fleming fanno in modo più raffinato." class="wp-image-4069" srcset="https://www.gironi.it/blog/wp-content/uploads/2026/07/peeking-soglia.png 1020w, https://www.gironi.it/blog/wp-content/uploads/2026/07/peeking-soglia-300x194.png 300w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /><figcaption class="wp-element-caption">L&#8217;effetto di alzare l&#8217;asticella a ogni sguardo (sempre venti sguardi). Con la soglia standard 0,05 l&#8217;errore complessivo è il 25,1% del peeking; stringendo la soglia per-sguardo l&#8217;errore scende, e intorno a 0,01 torna vicino al 5% nominale (la linea tratteggiata). È la versione artigianale, a soglia costante, di ciò che i confini di Pocock o O&#8217;Brien-Fleming fanno in modo più raffinato.</figcaption></figure>

</div></div>



<p class="wp-block-paragraph">Quella appena mostrata è una versione artigianale e a soglia costante dell&#8217;idea. I confini &#8220;da manuale&#8221; — più raffinati, con soglie che cambiano nel corso del test, come quelli di Pocock o O&#8217;Brien-Fleming — si ottengono in R con il pacchetto <code>gsDesign</code>, e i tool commerciali come Optimizely usano una variante <em>always-valid</em> (la cosiddetta mSPRT) della stessa idea di fondo. <br>Cambia la matematica fine, non il principio: per guardare spesso senza barare bisogna chiedere, a ogni sguardo, più evidenza di quanta ne chiederebbe un test singolo.</p>



<p class="has-light-gray-background-color has-background wp-block-paragraph">Un avvertimento: <strong>un risultato visto durante il test, da solo, non prova niente: conta quando si è deciso di guardarlo.</strong> <br>Lo stesso p-value sotto 0,05 significa cose diverse a seconda che sia l&#8217;unico test a orizzonte fisso o il primo dei venti in cui ci si è riservati di fermarsi. Senza dichiarare in anticipo come e quando si guarderanno i dati, qualunque &#8220;vincitore&#8221; emerso in corsa è sospetto.</p>



<h2 class="wp-block-heading">Prova tu</h2>



<p class="wp-block-paragraph">Per sentire il meccanismo da vicino, si parte dallo script e si cambia un solo parametro: il numero di <code>sguardi</code>. <br>Si prova a passare da un monitoraggio settimanale (pochi sguardi) a uno giornaliero (molti sguardi) e si riesegue la simulazione del peeking. Cosa aspettarsi: più di frequente si sbircia, più sale il tasso di falsi positivi — la frequenza degli sguardi è la benzina del problema. Poi si rifà la calibrazione della soglia con quel nuovo numero di sguardi e si verifica che, scegliendo una soglia abbastanza severa, l&#8217;errore complessivo torna comunque sotto controllo. È la dimostrazione, fatta in prima persona, che il peeking non è una maledizione: è solo un conto che va pagato.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p class="wp-block-paragraph">C&#8217;è un&#8217;ultima trappola di questa famiglia, forse la più subdola, perché non si nasconde nei nostri dati ma in quelli che ci raccontano gli altri. Quando leggiamo il case study di un&#8217;agenzia — &#8220;abbiamo aumentato le conversioni del 300% con questa tattica&#8221; — stiamo guardando un sopravvissuto: i mille tentativi identici che sono falliti non li racconta nessuno. È il <em>survivorship bias</em>, il motivo per cui i case study mentono anche quando dicono il vero, ed è il prossimo passo del nostro viaggio tra i tranelli dei dati di marketing.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">Per approfondire</h3>



<p class="wp-block-paragraph">Su peeking, arresto anticipato e test sequenziali il riferimento — in inglese — resta <a href="https://www.amazon.it/dp/1108724264?tag=consulenzeinf-21" rel="nofollow sponsored noopener" target="_blank"><em>Trustworthy Online Controlled Experiments</em></a> di Ron Kohavi, Diane Tang e Ya Xu: scritto da chi ha guidato le piattaforme di sperimentazione di Microsoft, Google e LinkedIn, dedica pagine esplicite a tutti i modi in cui un A/B test in corsa può ingannarci, e a come difendersi. È il libro che tira fuori dal cassetto chiunque debba prendere sul serio gli esperimenti online.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.gironi.it/blog/peeking-problem/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Regressione verso la media: l&#8217;ottimizzazione SEO che ha funzionato… per caso</title>
		<link>https://www.gironi.it/blog/regressione-verso-la-media/</link>
					<comments>https://www.gironi.it/blog/regressione-verso-la-media/#respond</comments>
		
		<dc:creator><![CDATA[Paolo Gironi]]></dc:creator>
		<pubDate>Mon, 22 Jun 2026 08:51:35 +0000</pubDate>
				<category><![CDATA[statistica]]></category>
		<guid isPermaLink="false">https://www.gironi.it/blog/?p=3840</guid>

					<description><![CDATA[Nell&#8217;aeronautica militare israeliana, racconta Daniel Kahneman, gli istruttori erano convinti di una cosa: lodare un allievo dopo una manovra eccellente lo peggiorava, sgridarlo dopo una pessima lo migliorava. L&#8217;avevano osservato mille volte sul campo, quindi doveva essere vero: con i piloti la severità funziona, i complimenti rovinano. Solo che non era vero. Una manovra eccezionale &#8230; <a href="https://www.gironi.it/blog/regressione-verso-la-media/" class="more-link">Leggi tutto<span class="screen-reader-text"> "Regressione verso la media: l&#8217;ottimizzazione SEO che ha funzionato… per caso"</span></a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Nell&#8217;aeronautica militare israeliana, <strong>racconta Daniel Kahneman</strong>, gli istruttori erano convinti di una cosa: lodare un allievo dopo una manovra eccellente lo peggiorava, sgridarlo dopo una pessima lo migliorava. <br>L&#8217;avevano osservato mille volte sul campo, quindi doveva essere vero: con i piloti la severità funziona, i complimenti rovinano. <br>Solo che non era vero. Una manovra eccezionale — in un senso o nell&#8217;altro — è in parte bravura e in parte fortuna; e la fortuna, alla prova successiva, non si ripete. Dopo un volo splendido si tende a tornare verso la propria media (e sembra che la lode abbia nuociuto), dopo un volo disastroso si torna verso la media (e sembra che la sgridata abbia giovato). Gli istruttori stavano attribuendo a sé stessi un effetto che era solo <strong>regressione verso la media</strong>.</p>



<p class="wp-block-paragraph">Lo stesso identico inganno ci aspetta ogni volta che guardiamo i dati di un sito e decidiamo se un nostro intervento &#8220;ha funzionato&#8221;.</p>



<span id="more-3840"></span>



<p class="wp-block-paragraph">Vale la pena chiarire subito una cosa sul nome, perché trae in inganno: la regressione verso la media <strong>non ha niente a che vedere con la <a href="https://www.gironi.it/blog/regressione-lineare-semplice/">regressione lineare</a></strong>, il modello che traccia una retta tra due variabili. Qui &#8220;regressione&#8221; va inteso nel senso comune di <em>ritorno</em>, <em>rientro</em>: i valori estremi che tornano indietro verso la loro media. Sono due cose diverse che condividono solo una parola.</p>



<h2 class="wp-block-heading">Cos&#8217;è la regressione verso la media</h2>



<p class="wp-block-paragraph">Il meccanismo è semplice, e una volta visto non si riesce più a non vederlo. <br>Quasi ogni numero che misuriamo è la somma di due parti: un valore &#8220;vero&#8221;, stabile, e una dose di <strong>rumore</strong> — fluttuazione casuale del momento. La posizione media in SERP di una pagina in un certo mese dipende dalla sua reale rilevanza, ma anche dal caso: l&#8217;algoritmo che oscilla, un competitor che quel mese ha spinto, la stagionalità della query, un pugno di clic in più o in meno.</p>



<p class="wp-block-paragraph">Ora, quando isoliamo i casi <strong>estremi</strong> — le pagine messe peggio, il mese andato peggio — stiamo quasi sempre selezionando situazioni in cui il rumore ha spinto <em>tutto nella stessa direzione sfavorevole</em>. <br><strong>Alla misura successiva quel rumore non si ripeterà identico, e il valore tenderà a risalire verso la sua media vera — senza che nessuno abbia fatto nulla.</strong> È un fatto puramente statistico, non un fenomeno SEO: più una misura è estrema, più è probabile che la prossima sia meno estrema.</p>



<h2 class="wp-block-heading">L&#8217;ottimizzazione che ha funzionato per caso</h2>



<p class="wp-block-paragraph">Vediamo cosa combina questo meccanismo nel lavoro di tutti i giorni. Immaginiamo di tenere d&#8217;occhio la posizione media in SERP di 300 pagine per due mesi consecutivi. Simulo in R lo scenario, dando a ogni pagina una posizione &#8220;vera&#8221; stabile e aggiungendo a ciascun mese una fluttuazione casuale:</p>



<pre class="wp-block-code"><code>set.seed(48)

# 300 pagine, ognuna con la sua posizione "vera" in SERP (stabile nel tempo)
pos_vera &lt;- runif(300, 3, 40)

# due mesi consecutivi: stessa posizione vera, rumore casuale diverso
mese1 &lt;- pos_vera + rnorm(300, 0, 8)
mese2 &lt;- pos_vera + rnorm(300, 0, 8)

# le 60 pagine messe peggio nel mese 1 (in SERP "peggio" = numero più alto)
peggiori &lt;- order(mese1, decreasing = TRUE)[1:60]

round(mean(mese1[peggiori]), 1)   # posizione media di partenza
# [1] 39.1</code></pre>



<p class="wp-block-paragraph">Le nostre 60 pagine peggiori partono da una posizione media di <strong>39,1</strong>: roba da fondo della terza pagina. Decidiamo di intervenire — riscriviamo i title, aggiorniamo i contenuti, sistemiamo i link interni — e il mese dopo ricontrolliamo. <br>Ecco il risultato, <strong>senza che nel frattempo l&#8217;algoritmo sia cambiato e, soprattutto, senza che nella simulazione io abbia toccato davvero nulla</strong>:</p>



<pre class="wp-block-code"><code>round(mean(mese2[peggiori]), 1)   # un mese dopo
# [1] 33</code></pre>



<p class="wp-block-paragraph">Da <strong>39,1</strong> a <strong>33</strong>: circa <strong>sei posizioni guadagnate</strong>. Un balzo che in un report farebbe un figurone, e che chiunque sarebbe tentato di attribuire all&#8217;ottimizzazione appena fatta. <br>Peccato che nel codice non ci sia <em>nessuna</em> ottimizzazione: le pagine sono migliorate da sole, perché erano state scelte proprio in quanto estreme e il rumore che le aveva affossate nel mese 1 non si è ripetuto. Per dare la misura: la posizione media di <em>tutte</em> le 300 pagine è circa 22, ed è verso quel valore che le peggiori stanno rientrando.</p>



<div class="wp-block-group has-background" style="background-color:#f5f7f9;margin-top:2.5rem;margin-bottom:2.5rem;padding-top:1.5rem;padding-right:1.5rem;padding-bottom:1rem;padding-left:1.5rem"><div class="wp-block-group__inner-container is-layout-constrained wp-container-core-group-is-layout-eed7543b wp-block-group-is-layout-constrained">

<figure class="wp-block-image size-large"><img decoding="async" width="990" height="810" src="https://www.gironi.it/blog/wp-content/uploads/2026/07/regressione-scatter.png" alt="Lo scatter firma della regressione verso la media: ogni punto è una pagina, posizione nel mese 1 (x) contro mese 2 (y). Le 60 peggiori (rosso, in alto a destra) scivolano sotto la bisettrice — migliorano — e le 60 migliori (verde, in basso a sinistra) risalgono verso il centro. La retta di regressione (pendenza 0,67) è più piatta della bisettrice «nessun cambiamento»: è tutta qui la regressione verso la media." class="wp-image-4062" srcset="https://www.gironi.it/blog/wp-content/uploads/2026/07/regressione-scatter.png 990w, https://www.gironi.it/blog/wp-content/uploads/2026/07/regressione-scatter-300x245.png 300w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /><figcaption class="wp-element-caption">Lo scatter firma della regressione verso la media: ogni punto è una pagina, posizione nel mese 1 (x) contro mese 2 (y). Le 60 peggiori (rosso, in alto a destra) scivolano sotto la bisettrice — migliorano — e le 60 migliori (verde, in basso a sinistra) risalgono verso il centro. La retta di regressione (pendenza 0,67) è più piatta della bisettrice «nessun cambiamento»: è tutta qui la regressione verso la media.</figcaption></figure>

</div></div>



<h2 class="wp-block-heading">Come non farsi ingannare: il gruppo di controllo</h2>



<p class="wp-block-paragraph">Se il miglioramento arriva comunque, come facciamo a sapere se la nostra ottimizzazione ha avuto un qualche effetto <em>reale</em>? <br>La risposta è la stessa che useremmo per un farmaco: serve un <strong>gruppo di controllo</strong>. Dividiamo le 60 pagine peggiori in due metà a caso: una la &#8220;ottimizziamo&#8221;, l&#8217;altra la lasciamo deliberatamente in pace. Poi confrontiamo quanto migliorano:</p>



<pre class="wp-block-code"><code># divido le 60 peggiori in due gruppi casuali
gruppo &lt;- sample(rep(c("ottimizzate", "controllo"), each = 30))

# miglioramento medio (mese1 - mese2) nei due gruppi
round(tapply(mese1[peggiori] - mese2[peggiori], gruppo, mean), 1)
# controllo ottimizzate
#       6.0         6.1</code></pre>



<p class="wp-block-paragraph">Il gruppo &#8220;ottimizzato&#8221; guadagna <strong>6,1</strong> posizioni, quello di controllo — lasciato intatto — ne guadagna <strong>6,0</strong>. <br>Praticamente lo stesso identico miglioramento. La nostra ottimizzazione, nella simulazione, non ha aggiunto nulla: tutto il guadagno era regressione verso la media, e il controllo lo smaschera mostrando che sarebbe successo comunque.</p>



<p class="has-light-gray-background-color has-background wp-block-paragraph">La lezione è questa: <strong>un miglioramento, da solo, non prova niente.</strong> <br>Quando intervieni proprio sulle pagine (o sulle campagne) che andavano peggio, una parte della loro ripresa è garantita a prescindere da te. Senza un confronto con ciò che <em>non</em> hai toccato, non puoi sapere quanto del risultato è merito tuo e quanto è solo rientro verso la media.</p>



<p class="wp-block-paragraph">È lo stesso ragionamento, va detto, che sta dietro a un <a href="https://www.gironi.it/blog/ab-testing/">A/B test</a> fatto come si deve: confrontare la variante con un controllo concorrente, non con il &#8220;prima&#8221;. Ed è cugino stretto della trappola che abbiamo visto parlando di <a href="https://www.gironi.it/blog/correlazione/">correlazione e causazione</a>: anche qui scambiamo una sequenza temporale (&#8220;ho agito, poi è migliorato&#8221;) per un nesso causale.</p>



<h2 class="wp-block-heading">Prova tu</h2>



<p class="wp-block-paragraph">Per fissare il meccanismo, prova a ricostruire lo scenario cambiando un solo dettaglio: invece delle 60 pagine <em>peggiori</em>, seleziona le 60 <em>migliori</em> del mese 1 (<code>order(mese1)[1:60]</code>, senza <code>decreasing</code>) e guarda come si comportano nel mese 2.</p>



<p class="wp-block-paragraph">Cosa aspettarti: anche le campionesse rientrano verso la media, ma <em>peggiorando</em> — le posizioni di vertice contengono fortuna che non si ripete. È la faccia speculare dello stesso fenomeno, e spiega perché &#8220;quel mese d&#8217;oro&#8221; o &#8220;quella pagina che volava&#8221; così spesso non si riescono a replicare: non avevi perso il tocco magico, stavi solo tornando verso la tua media.</p>



<div class="wp-block-group has-background" style="background-color:#f5f7f9;margin-top:2.5rem;margin-bottom:2.5rem;padding-top:1.5rem;padding-right:1.5rem;padding-bottom:1rem;padding-left:1.5rem"><div class="wp-block-group__inner-container is-layout-constrained wp-container-core-group-is-layout-eed7543b wp-block-group-is-layout-constrained">

<figure class="wp-block-image size-large"><img decoding="async" width="990" height="690" src="https://www.gironi.it/blog/wp-content/uploads/2026/07/regressione-code.png" alt="Le due code, in media, rientrano verso la media generale (~22). Le 60 peggiori passano da 39,1 a 33 (e sembra che l'ottimizzazione abbia funzionato); le 60 migliori da 4,0 a 9,7 (il «mese d'oro» che non si ripete). In entrambi i casi nessuno ha toccato nulla: è solo il rumore che, alla misura successiva, non si ripete identico." class="wp-image-4063" srcset="https://www.gironi.it/blog/wp-content/uploads/2026/07/regressione-code.png 990w, https://www.gironi.it/blog/wp-content/uploads/2026/07/regressione-code-300x209.png 300w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /><figcaption class="wp-element-caption">Le due code, in media, rientrano verso la media generale (~22). Le 60 peggiori passano da 39,1 a 33 (e sembra che l&#8217;ottimizzazione abbia funzionato); le 60 migliori da 4,0 a 9,7 (il «mese d&#8217;oro» che non si ripete). In entrambi i casi nessuno ha toccato nulla: è solo il rumore che, alla misura successiva, non si ripete identico.</figcaption></figure>

</div></div>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p class="wp-block-paragraph">C&#8217;è una variante ancora più insidiosa di questa trappola, perché si nasconde dentro gli strumenti che usiamo proprio <em>per</em> decidere con rigore. Se guardiamo un test mentre è ancora in corso e ci fermiamo appena i numeri ci piacciono, stiamo selezionando un istante estremo esattamente come abbiamo selezionato le pagine peggiori — e ci faremo ingannare allo stesso modo. È il <em>peeking problem</em>, ed è il prossimo passo del nostro viaggio tra i tranelli dei dati di marketing.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">Per approfondire</h3>



<p class="wp-block-paragraph">Il racconto degli istruttori di volo con cui abbiamo aperto viene da <a href="https://www.amazon.it/dp/8804736127?tag=consulenzeinf-21" rel="nofollow sponsored noopener" target="_blank"><em>Pensieri lenti e veloci</em></a> di Daniel Kahneman, premio Nobel per l&#8217;economia: è il libro che ha reso popolari la regressione verso la media e decine di altri bias del nostro ragionamento, raccontati con esempi che restano addosso. Se di questo articolo vuoi tenere una cosa sola, tienilo: è una vaccinazione contro l&#8217;illusione di aver capito perché i numeri si muovono.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.gironi.it/blog/regressione-verso-la-media/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Correlazione: Pearson, Spearman e Kendall (e perché non è causazione)</title>
		<link>https://www.gironi.it/blog/correlazione/</link>
					<comments>https://www.gironi.it/blog/correlazione/#respond</comments>
		
		<dc:creator><![CDATA[Paolo Gironi]]></dc:creator>
		<pubDate>Fri, 19 Jun 2026 07:10:54 +0000</pubDate>
				<category><![CDATA[statistica]]></category>
		<guid isPermaLink="false">https://www.gironi.it/blog/?p=3820</guid>

					<description><![CDATA[Chi guarda i dati di un sito lo fa di continuo, spesso senza nemmeno accorgersene, e talvolta nota che due cose sembrano muoversi insieme. Le pagine che stanno più in alto in SERP prendono più clic; quelle dove gli utenti restano più a lungo convertono di più; gli articoli più lunghi paiono posizionarsi meglio. Sono &#8230; <a href="https://www.gironi.it/blog/correlazione/" class="more-link">Leggi tutto<span class="screen-reader-text"> "Correlazione: Pearson, Spearman e Kendall (e perché non è causazione)"</span></a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Chi guarda i dati di un sito lo fa di continuo, spesso senza nemmeno accorgersene, e talvolta nota che due cose sembrano muoversi insieme. <br>Le pagine che stanno più in alto in SERP prendono più clic; quelle dove gli utenti restano più a lungo convertono di più; gli articoli più lunghi paiono posizionarsi meglio. <br>Sono intuizioni preziose, ma restano vaghe finché non rispondiamo a una domanda precisa: <em>quanto</em> si muovono insieme, queste coppie di numeri? E in che senso? <br>Serve un indice che trasformi l&#8217;impressione &#8220;vanno di pari passo&#8221; in una misura confrontabile. Quell&#8217;indice è la <strong>correlazione</strong>, ed è uno degli strumenti più usati — e più fraintesi — di tutta la statistica applicata.</p>



<span id="more-3820"></span>



<p class="wp-block-paragraph">Diciamo subito cosa la correlazione <em>non</em> è, perché è qui che nascono i guai. <br><strong>La correlazione misura se e quanto due variabili si associano; non dice che una causa l&#8217;altra, e non costruisce un modello per prevedere l&#8217;una dall&#8217;altra</strong>. <br>Quel secondo passo — la previsione — è il mestiere della <a href="https://www.gironi.it/blog/regressione-lineare-semplice/" data-type="post" data-id="1807">regressione</a>, di cui ci occuperemo a parte. <br>Qui restiamo al passo precedente: capire, con un solo numero, se due metriche viaggiano insieme.</p>



<h2 class="wp-block-heading">Dalla covarianza alla correlazione</h2>



<p class="wp-block-paragraph">L&#8217;idea di partenza è semplice. Se due variabili si muovono insieme, quando una sta sopra la propria media anche l&#8217;altra tende a stare sopra la sua; quando una scende sotto, l&#8217;altra la segue. <br>Possiamo misurare questa tendenza moltiplicando, per ogni osservazione, lo scarto di <em>x</em> dalla sua media per lo scarto di <em>y</em> dalla sua, e facendone la media. <br>È la <strong>covarianza</strong>:</p>



\( \text{cov}(x, y) = \frac{1}{n} \sum_{i=1}^{n} (x_i &#8211; \bar{x})(y_i &#8211; \bar{y}) \\ \)



<p class="wp-block-paragraph">dove <em>x̄</em> e <em>ȳ</em> sono le medie delle due variabili e <em>n</em> il numero di osservazioni. <br>Quando gli scarti hanno lo stesso segno (entrambi sopra o entrambi sotto la media) il prodotto è positivo; quando hanno segni opposti è negativo. <br><strong>Una covarianza positiva segnala dunque che le due variabili tendono a crescere insieme, una negativa che quando l&#8217;una sale l&#8217;altra scende.</strong></p>



<p class="wp-block-paragraph">La covarianza, però, ha un difetto che la rende inutilizzabile come metro di confronto: <strong>dipende dalle unità di misura</strong>. <br>La covarianza tra sessioni e secondi di permanenza è un numero, quella tra sessioni e tasso di conversione un altro, e i due non si possono paragonare perché parlano lingue diverse. <br><strong>Per ottenere una misura pulita la dividiamo per le due deviazioni standard, in modo da spogliarla delle unità e costringerla in un intervallo fisso. </strong><br>Otteniamo così il <strong>coefficiente di correlazione di Pearson</strong>:</p>



\( r = \frac{\sum_{i=1}^{n} (x_i &#8211; \bar{x})(y_i &#8211; \bar{y})}{\sqrt{\sum_{i=1}^{n} (x_i &#8211; \bar{x})^2} \; \sqrt{\sum_{i=1}^{n} (y_i &#8211; \bar{y})^2}} \\ \)



<p class="wp-block-paragraph">Il numeratore non è altro che la covarianza (a meno del fattore <em>n</em>); il denominatore è il prodotto delle due variabilità, e serve appunto a normalizzare. <br>Il risultato è un numero puro compreso tra <strong>−1 e +1</strong>: vale +1 quando i punti stanno esattamente su una retta crescente, −1 quando stanno su una retta decrescente, 0 quando non c&#8217;è alcuna associazione lineare. <br>Quanto più <em>r</em> si avvicina agli estremi, tanto più stretta è la relazione lineare.</p>



<h2 class="wp-block-heading">Pearson: l&#8217;associazione lineare (e la sua trappola)</h2>



<p class="wp-block-paragraph">Mettiamolo subito al lavoro su un caso che chi fa SEO conosce a memoria: il legame tra la <strong>posizione in SERP</strong> e il <strong>CTR</strong>, la percentuale di clic. Sappiamo tutti che più si scende nella pagina dei risultati, meno si viene cliccati. Prendiamo dieci posizioni con i rispettivi CTR osservati e calcoliamo in R il coefficiente di Pearson:</p>



<pre class="wp-block-code"><code>pos &lt;- 1:10
ctr &lt;- c(28.5, 15.7, 11.0, 7.2, 8.0, 5.1, 4.0, 3.2, 2.8, 2.6)  # CTR % per posizione

cor(pos, ctr)
# &#091;1] -0.852</code></pre>



<p class="wp-block-paragraph">Il coefficiente è <strong>−0,852</strong>: forte, negativo, esattamente come ci aspettavamo. Eppure c&#8217;è qualcosa che non torna. Il legame tra posizione e CTR è ferreo — non capita quasi mai che una posizione più bassa renda più clic — e ci aspetteremmo un valore ancora più vicino a −1. Perché Pearson si ferma a −0,85?</p>



<p class="wp-block-paragraph">La risposta è il punto più importante di tutto l&#8217;articolo. <strong>Pearson misura solo l&#8217;associazione lineare</strong>, cioè quanto bene i punti si dispongono lungo una <em>retta</em>. Ma la curva del CTR non è una retta: crolla a precipizio dalla prima alla terza posizione e poi si appiattisce. La relazione è fortissima, solo che è <em>curva</em>. Pearson, che cerca rette, vede quella curvatura come &#8220;imperfezione&#8221; e abbassa il voto. Non sta sbagliando: sta rispondendo a una domanda — &#8220;quanto è lineare?&#8221; — che in questo caso non è quella giusta.</p>



<h2 class="wp-block-heading">Spearman e Kendall: l&#8217;associazione monotòna</h2>



<p class="wp-block-paragraph">Per molte relazioni del mondo SEO ci interessa una cosa più debole della linearità: ci basta sapere se, quando una variabile cresce, l&#8217;altra cresce <em>sistematicamente</em> (o cala sistematicamente), senza pretendere che lo faccia a passo costante. Una relazione del genere si dice <strong>monotòna</strong>, e per misurarla esiste il coefficiente di correlazione per ranghi di <strong>Spearman</strong>, indicato con ρ (<em>rho</em>).</p>



<p class="wp-block-paragraph">Il trucco di Spearman è elegante: invece di lavorare sui valori, lavora sui loro <strong>ranghi</strong>. <br>Sostituisce a ogni numero la sua posizione in classifica (il più piccolo diventa 1, il successivo 2, e così via) e poi calcola un normale Pearson su questi ranghi. In questo modo la forma esatta della curva sparisce — conta solo l&#8217;ordine — e ciò che resta è quanto fedelmente l&#8217;ordine di <em>x</em> riproduce quello di <em>y</em>. </p>



<p class="wp-block-paragraph">Lo calcoliamo sugli stessi dati di prima:</p>



<pre class="wp-block-code"><code>cor(pos, ctr, method = "spearman")
# &#091;1] -0.988</code></pre>



<p class="wp-block-paragraph">Adesso il coefficiente è <strong>−0,988</strong>, schiacciato contro −1. È la fotografia corretta della situazione: man mano che la posizione peggiora, il CTR cala quasi senza eccezioni. (Quel &#8220;quasi&#8221; non è un caso: nei dati ho lasciato una piccola inversione realistica, la posizione 5 che rende più della 4, come capita quando un <em>rich snippet </em>gonfia il CTR di un risultato; è esattamente il genere di increspatura che impedisce a ρ di toccare il −1 esatto.). Dove Pearson vedeva un&#8217;associazione &#8220;buona ma non ottima&#8221;, Spearman riconosce la relazione monotòna quasi perfetta che effettivamente c&#8217;è.</p>



<p class="wp-block-paragraph">Esiste una terza misura che vale la pena conoscere, il <strong>tau di Kendall</strong> (τ). Anch&#8217;essa lavora sull&#8217;ordine, ma con una logica diversa: conta, su tutte le coppie di osservazioni, quante sono <em>concordi</em> (se <em>x</em> cresce, cresce anche <em>y</em>) e quante <em>discordi</em>, e ne fa il bilancio. La calcolo in R sempre sugli stessi dati:</p>



<pre class="wp-block-code"><code>cor(pos, ctr, method = "kendall")
# &#091;1] -0.956</code></pre>



<p class="wp-block-paragraph">Kendall restituisce <strong>−0,956</strong>, anch&#8217;esso vicino agli estremi ma tipicamente un po&#8217; più prudente di Spearman. </p>



<div class="wp-block-group has-background" style="background-color:#f5f7f9;margin-top:2.5rem;margin-bottom:2.5rem;padding-top:1.5rem;padding-right:1.5rem;padding-bottom:1rem;padding-left:1.5rem"><div class="wp-block-group__inner-container is-layout-constrained wp-container-core-group-is-layout-eed7543b wp-block-group-is-layout-constrained">

<figure class="wp-block-image size-large"><img decoding="async" width="1020" height="660" src="https://www.gironi.it/blog/wp-content/uploads/2026/07/correlazione-pearson-spearman.png" alt="Posizione in SERP contro CTR sui dieci risultati dell'esempio. La retta di Pearson (tratteggiata) lascia i punti lontani: la relazione è fortissima ma curva, ed è per questo che Pearson si ferma a −0,85 mentre Spearman (ρ = −0,99) e Kendall (τ = −0,96), che guardano solo all'ordine, la riconoscono quasi perfetta. I due punti cerchiati sono l'increspatura realistica: la posizione 5 rende più della 4." class="wp-image-4038" srcset="https://www.gironi.it/blog/wp-content/uploads/2026/07/correlazione-pearson-spearman.png 1020w, https://www.gironi.it/blog/wp-content/uploads/2026/07/correlazione-pearson-spearman-300x194.png 300w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /><figcaption class="wp-element-caption">Posizione in SERP contro CTR sui dieci risultati dell&#8217;esempio. La retta di Pearson (tratteggiata) lascia i punti lontani: la relazione è fortissima ma curva, ed è per questo che Pearson si ferma a −0,85 mentre Spearman (ρ = −0,99) e Kendall (τ = −0,96), che guardano solo all&#8217;ordine, la riconoscono quasi perfetta. I due punti cerchiati sono l&#8217;increspatura realistica: la posizione 5 rende più della 4.</figcaption></figure>

</div></div>



<p class="wp-block-paragraph">Nella pratica quotidiana la scelta è meno complicata di quanto sembri: <strong>Pearson</strong> quando ci interessa una relazione lineare e i dati non hanno code o valori anomali violenti; <strong>Spearman</strong> quando la relazione è monotòna ma curva, o quando i dati sono già ranghi (posizioni, classifiche), o quando un paio di outlier rischiano di sballare Pearson; <strong>Kendall</strong> quando le osservazioni sono poche o ci sono molti valori a pari merito, situazione in cui le sue proprietà statistiche reggono meglio.</p>



<h2 class="wp-block-heading">La matrice di correlazione</h2>



<p class="wp-block-paragraph">Raramente abbiamo solo due metriche da confrontare. Più spesso ne abbiamo una manciata — sessioni, durata media, conversioni, frequenza di rimbalzo — e vorremmo vedere <em>tutte</em> le associazioni a colpo d&#8217;occhio. La funzione <code>cor()</code> di R, applicata a un intero data frame, restituisce la <strong>matrice di correlazione</strong>: il coefficiente di ogni variabile con ogni altra. <br>La costruisco su dodici pagine di esempio:</p>



<pre class="wp-block-code"><code>ga4 &lt;- data.frame(
  sessioni      = c(120, 340, 210, 560, 430, 780, 650, 290, 510, 880, 360, 720),
  durata_media  = c(31,  55,  48,  44,  58,  63,  71,  52,  46,  68,  60,  64),
  conversioni   = c(3,   8,   4,   21,  11,  24,  19,  9,   17,  29,  7,   22),
  freq_rimbalzo = c(70,  61,  66,  44,  57,  41,  46,  59,  52,  38,  63,  45)
)

round(cor(ga4), 2)
#               sessioni durata_media conversioni freq_rimbalzo
# sessioni          1.00         0.73        0.98         -0.97
# durata_media      0.73         1.00        0.58         -0.62
# conversioni       0.98         0.58        1.00         -0.99
# freq_rimbalzo    -0.97        -0.62       -0.99          1.00</code></pre>



<p class="wp-block-paragraph">Si legge come una tabella a doppia entrata: sulla diagonale ci sono tutti 1 (ogni variabile è perfettamente correlata con sé stessa), e la matrice è simmetrica perché la correlazione di <em>x</em> con <em>y</em> è la stessa di <em>y</em> con <em>x</em>. Come si vede, sessioni e conversioni viaggiano quasi all&#8217;unisono (0,98: più traffico, più conversioni — niente di sorprendente), la frequenza di rimbalzo è correlata negativamente con tutto il resto, mentre la durata media si associa alle conversioni molto meno di quanto l&#8217;intuizione suggerirebbe (0,58). <br>Una matrice del genere è una mappa di partenza preziosa per decidere dove guardare. È utile visualizzarla come <strong>heatmap</strong> (con pacchetti come <code>corrplot</code>), dove l&#8217;intensità del colore rende immediato cogliere i legami forti.</p>



<div class="wp-block-group has-background" style="background-color:#f5f7f9;margin-top:2.5rem;margin-bottom:2.5rem;padding-top:1.5rem;padding-right:1.5rem;padding-bottom:1rem;padding-left:1.5rem"><div class="wp-block-group__inner-container is-layout-constrained wp-container-core-group-is-layout-eed7543b wp-block-group-is-layout-constrained">

<figure class="wp-block-image size-large"><img decoding="async" width="930" height="780" src="https://www.gironi.it/blog/wp-content/uploads/2026/07/correlazione-heatmap.png" alt="La stessa matrice di correlazione, resa come heatmap: il blu segnala le associazioni positive, l'arancio quelle negative, l'intensità la forza del legame. Sessioni e conversioni (0,98) e la frequenza di rimbalzo contro tutto il resto saltano all'occhio; la durata media resta più tiepida (0,58)." class="wp-image-4039" srcset="https://www.gironi.it/blog/wp-content/uploads/2026/07/correlazione-heatmap.png 930w, https://www.gironi.it/blog/wp-content/uploads/2026/07/correlazione-heatmap-300x252.png 300w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /><figcaption class="wp-element-caption">La stessa matrice di correlazione, resa come heatmap: il blu segnala le associazioni positive, l&#8217;arancio quelle negative, l&#8217;intensità la forza del legame. Sessioni e conversioni (0,98) e la frequenza di rimbalzo contro tutto il resto saltano all&#8217;occhio; la durata media resta più tiepida (0,58).</figcaption></figure>

</div></div>



<p class="has-light-gray-background-color has-background wp-block-paragraph">Un avvertimento: <strong>una matrice di correlazione non è una mappa causale</strong>. <br>Ci dice quali numeri si muovono insieme, non quale muove quale, né se a muoverli sia un terzo fattore che non abbiamo nemmeno in tabella.</p>



<h2 class="wp-block-heading">Correlazione non è causazione</h2>



<p class="wp-block-paragraph"><strong>È la frase più ripetuta della statistica, e la più disattesa nei fatti. </strong><br>Vale la pena vedere dove inciampa, perché in SEO l&#8217;inciampo è quotidiano. Prendiamo l&#8217;osservazione classica: gli articoli più lunghi si posizionano meglio. Misuriamo l&#8217;associazione tra lunghezza del contenuto e un punteggio di ranking (più alto = meglio piazzato):</p>



<pre class="wp-block-code"><code>lung       &lt;- c(620, 850, 1100, 1300, 1500, 1800, 2100, 2400, 2800, 3200)
rank_score &lt;- c(3,   8,   6,    11,   9,    7,    14,   10,   16,   15)

cor(lung, rank_score)
# &#091;1] 0.842</code></pre>



<p class="wp-block-paragraph">Un bel <strong>0,842</strong>: la correlazione c&#8217;è, ed è robusta. <br>La tentazione di concludere &#8220;allungo gli articoli e salgo in classifica&#8221; è fortissima — e quasi sempre sbagliata. Davanti a una correlazione, prima di parlare di causa, dobbiamo metterci sul tavolo almeno tre spiegazioni alternative. <br>Potrebbe essere una <strong>causa diretta</strong> (la lunghezza aiuta davvero il ranking). Potrebbe essere una <strong>causa inversa</strong> (le pagine che già rankano bene ricevono più cure e vengono ampliate nel tempo). <br>Oppure — il caso più frequente e più insidioso — potrebbe esserci un <strong>fattore confondente</strong> che muove entrambe: l&#8217;autorevolezza del sito. Un dominio autorevole tende sia a produrre contenuti più approfonditi (quindi più lunghi) sia a posizionarsi meglio (per ragioni che con la lunghezza non c&#8217;entrano). La lunghezza e il ranking salgono insieme non perché l&#8217;una causi l&#8217;altro, ma perché un terzo elemento li traina entrambi.</p>



<p class="wp-block-paragraph">Questo terzo elemento nascosto è la radice di alcuni degli errori più spettacolari nell&#8217;analisi dei dati: arriva persino a invertire il segno di una relazione quando i dati vengono aggregati nel modo sbagliato, il fenomeno che va sotto il nome di <a href="https://www.gironi.it/blog/il-paradosso-di-simpson-nella-seo-quando-i-dati-aggregati-possono-mentire/">paradosso di Simpson</a>. <br>Stabilire un nesso causale è un mestiere a sé, che richiede esperimenti controllati o tecniche dedicate; la correlazione, da sola, non ci arriverà mai. Il suo compito è un altro, ed è prezioso: segnalarci le coppie di metriche che vale la pena indagare più a fondo.</p>



<h2 class="wp-block-heading">Prova tu</h2>



<p class="wp-block-paragraph">Per fissare il meccanismo, ecco un esercizio con dati verosimili. Abbiamo, per dieci pagine, il numero di domini referral che le linkano e il loro traffico organico mensile, e vogliamo capire quanto i due si associno:</p>



<pre class="wp-block-code"><code>bl  &lt;- c(5, 12, 8, 25, 18, 40, 33, 60, 52, 95)        # domini referral
org &lt;- c(180, 240, 420, 510, 760, 690, 1250, 1100, 1900, 1650)  # sessioni organiche/mese</code></pre>



<p class="wp-block-paragraph">Il compito: calcolare sia il coefficiente di Pearson con <code>cor(bl, org)</code>, sia quello di Spearman con <code>cor(bl, org, method = "spearman")</code>, e ragionare sul perché differiscono.</p>



<p class="wp-block-paragraph">Per controllare i conti: Pearson vale <strong>0,815</strong> e Spearman <strong>0,855</strong>. Sono entrambi alti e raccontano la stessa storia di fondo — più domini referral, più traffico — ma il fatto che Spearman sia un po&#8217; più alto di Pearson ci dice qualcosa: la relazione è più <em>monotòna</em> che <em>lineare</em>, segno che oltre una certa soglia ogni link aggiuntivo porta meno traffico marginale di quanto la retta vorrebbe. E, naturalmente, nessuno dei due numeri ci autorizza a dire che comprare backlink <em>farà</em> salire il traffico: anche qui l&#8217;autorevolezza del sito potrebbe star muovendo le due cose insieme.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p class="wp-block-paragraph">Con la correlazione abbiamo imparato a rispondere alla domanda <em>se, e quanto, due metriche si associano</em> — scegliendo di volta in volta Pearson, Spearman o Kendall a seconda della forma del legame. <strong>È il gradino indispensabile prima della domanda successiva, quella che chiunque analizzi dei dati finisce per porsi: data un&#8217;associazione, posso usare una variabile per <em>prevedere</em> l&#8217;altra, e tracciare la retta che le lega?</strong> Da qui in poi non misuriamo più soltanto la forza di un legame, ma lo modelliamo: è il territorio della <a href="https://www.gironi.it/blog/regressione-lineare-semplice/">regressione lineare</a>, dove quel coefficiente <em>r</em> che abbiamo appena conosciuto torna protagonista, questa volta al servizio della previsione.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">Per approfondire</h3>



<p class="wp-block-paragraph">Per i fondamenti di correlazione e regressione spiegati con rigore ma senza inutili tecnicismi, il manuale italiano che tengo più volentieri a portata di mano è <a href="https://www.amazon.it/dp/8891910651?tag=consulenzeinf-21" rel="nofollow sponsored noopener" target="_blank"><em>Statistica</em></a> di Newbold, Carlson e Thorne: i capitoli sull&#8217;associazione tra variabili coprono Pearson, Spearman e le insidie dell&#8217;interpretazione causale con la chiarezza che serve a chi parte dalle applicazioni.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.gironi.it/blog/correlazione/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Effect size e power analysis: quanto è grande l&#8217;effetto (e quanti dati servono)</title>
		<link>https://www.gironi.it/blog/effect-size-e-power-analysis/</link>
					<comments>https://www.gironi.it/blog/effect-size-e-power-analysis/#respond</comments>
		
		<dc:creator><![CDATA[Paolo Gironi]]></dc:creator>
		<pubDate>Wed, 17 Jun 2026 07:33:32 +0000</pubDate>
				<category><![CDATA[statistica]]></category>
		<guid isPermaLink="false">https://www.gironi.it/blog/?p=3691</guid>

					<description><![CDATA[Abbiamo chiuso l&#8217;articolo sul calcolatore di significatività con una promessa. Dicevamo che il p-value risponde a una sola domanda — l&#8217;effetto esiste? — e che a quella domanda, da solo, non ne aggiunge nessun&#8217;altra. Non ci dice quanto è grande l&#8217;effetto, né se vale lo sforzo di portarlo in produzione. È il momento di mantenere &#8230; <a href="https://www.gironi.it/blog/effect-size-e-power-analysis/" class="more-link">Leggi tutto<span class="screen-reader-text"> "Effect size e power analysis: quanto è grande l&#8217;effetto (e quanti dati servono)"</span></a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Abbiamo chiuso l&#8217;articolo sul <a href="https://www.gironi.it/blog/calcolatore-significativita-ab-test/">calcolatore di significatività</a> con una promessa. Dicevamo che il p-value risponde a una sola domanda — <em>l&#8217;effetto esiste?</em> — e che a quella domanda, da solo, non ne aggiunge nessun&#8217;altra. Non ci dice quanto è grande l&#8217;effetto, né se vale lo sforzo di portarlo in produzione. È il momento di mantenere quella promessa, perché sono proprio le due domande che il p-value lascia in sospeso a separare chi legge i dati con metodo da chi si ferma alla prima soglia che luccica.</p>



<p class="wp-block-paragraph">Le due domande hanno un nome preciso. La prima — <em>quanto è grande?</em> — è l&#8217;<strong>effect size</strong>. La seconda — <em>con i dati che ho, avrei potuto vederlo, un effetto del genere?</em> — è la <strong>potenza</strong> del test, e il ragionamento che ci porta a rispondere si chiama <strong>power analysis</strong>. Le esaminiamo una alla volta, come sempre con un esempio sotto mano.</p>



<span id="more-3691"></span>



<h2 class="wp-block-heading">Significativo non vuol dire grande</h2>



<p class="wp-block-paragraph">Partiamo da una situazione che a chi gestisce test online capita più spesso di quanto vorrebbe. Mettiamo di aver provato due title tag su una pagina ad altissimo traffico, e di aver raccolto un milione di sessioni per variante. Il CTR della versione A è il 3,00%, quello della versione B il 3,05%: cinque centesimi di punto di differenza. Verifichiamo in R se lo scarto è statisticamente significativo:</p>



<pre class="wp-block-code"><code># un milione di sessioni per variante, CTR 3,00% contro 3,05%
prop.test(c(30000, 30500), c(1000000, 1000000), correct = FALSE)$p.value
# [1] 0.03899</code></pre>



<p class="wp-block-paragraph">Il p-value è 0,039, sotto la soglia dello 0,05. Da manuale, dovremmo brindare: la differenza è &#8220;significativa&#8221;. Ma fermiamoci un attimo. Stiamo davvero per riscrivere i title di tutto il sito per guadagnare cinque centesimi di punto di CTR? Quel risultato significativo nasconde un effetto di dimensioni ridicole, reso rilevabile soltanto dalla mole spropositata di dati.</p>



<p class="wp-block-paragraph"><strong>Questo è il punto da cui non si torna indietro</strong>: con un campione abbastanza grande, <em>qualsiasi</em> differenza diventa statisticamente significativa, anche la più trascurabile. Il p-value misura quanto siamo sicuri che l&#8217;effetto non sia zero; non misura quanto l&#8217;effetto sia grande. Sono due cose diverse, e confonderle è l&#8217;errore che porta a inseguire vittorie che sul fatturato non lasciano traccia. L&#8217;effect size esiste proprio per rimettere la grandezza al centro.</p>



<h2 class="wp-block-heading">L&#8217;effect size: misurare il &#8220;quanto&#8221;</h2>



<p class="wp-block-paragraph">L&#8217;idea dell&#8217;effect size è semplice e, vista una volta, difficile da dimenticare: invece di chiederci solo <em>se</em> due gruppi differiscono, misuriamo <em>di quanto</em> differiscono, in una scala che non dipende dalla dimensione del campione. È la differenza tra dire &#8220;B è meglio di A&#8221; e dire &#8220;B è meglio di A di mezza deviazione standard&#8221;. La prima è una notizia; la seconda è un&#8217;informazione su cui si può decidere.</p>



<p class="wp-block-paragraph">Di misure di effect size ne esistono diverse, ciascuna tagliata su un tipo di confronto. Ne vediamo a fondo due — una per le medie, una per le proporzioni — perché coprono il grosso del lavoro quotidiano; delle altre daremo un cenno alla fine, con i rimandi giusti.</p>



<h2 class="wp-block-heading">Cohen&#8217;s d: l&#8217;effetto fra due medie</h2>



<p class="wp-block-paragraph">Quando confrontiamo due medie — il tempo medio sulla pagina di due varianti, la durata media di sessione di due segmenti — la misura di riferimento è il <strong>Cohen&#8217;s d</strong>. L&#8217;intuizione è questa: prendiamo la differenza tra le due medie e la esprimiamo in &#8220;unità di deviazione standard&#8221;, così da renderla confrontabile tra contesti diversi. Una differenza di tre secondi pesa molto se le sessioni durano tutte attorno a quel valore, pochissimo se variano di minuti.</p>



<p class="wp-block-paragraph">In formula, il Cohen&#8217;s d è il rapporto tra la differenza delle medie e la deviazione standard combinata dei due gruppi:</p>



\( d = \frac{\bar{x}_B &#8211; \bar{x}_A}{s_p} \\ \)



<p class="wp-block-paragraph">dove <em>x̄</em><sub>A</sub> e <em>x̄</em><sub>B</sub> sono le medie dei due gruppi e <em>s</em><sub>p</sub> è la <strong>deviazione standard pooled</strong>, cioè una media ponderata delle due deviazioni standard, che mette insieme la variabilità interna di entrambi i gruppi:</p>



\( s_p = \sqrt{\frac{(n_A &#8211; 1)\,s_A^2 + (n_B &#8211; 1)\,s_B^2}{n_A + n_B &#8211; 2}} \\ \)



<p class="wp-block-paragraph">con <em>n</em><sub>A</sub>, <em>n</em><sub>B</sub> le numerosità e <em>s</em><sub>A</sub>, <em>s</em><sub>B</sub> le deviazioni standard dei due gruppi. Il denominatore non è altro che il modo corretto di fondere due variabilità in una sola misura di riferimento.</p>



<p class="wp-block-paragraph">Facciamo un esempio. Abbiamo misurato la durata di sessione (in secondi) su due versioni di una pagina, dodici sessioni per versione. Calcolo il Cohen&#8217;s d in R appoggiandomi al pacchetto <code>effsize</code>, che fa il conto e ci restituisce anche l&#8217;etichetta qualitativa:</p>



<pre class="wp-block-code"><code>A &lt;- c(48, 55, 52, 60, 46, 58, 51, 57, 49, 54, 53, 50)  # versione A
B &lt;- c(50, 58, 52, 62, 49, 57, 60, 53, 61, 51, 59, 54)  # versione B

library(effsize)
cohen.d(B, A)

# Cohen's d
#
# d estimate: 0.6254922 (medium)
# 95 percent confidence interval:
#      lower      upper
# -0.2416187  1.4926030</code></pre>



<p class="wp-block-paragraph">Il d stimato è <strong>0,63</strong>, che <code>effsize</code> classifica come effetto <strong>medio</strong>. Le soglie convenzionali, proposte da Jacob Cohen, sono 0,2 per un effetto piccolo, 0,5 per uno medio, 0,8 per uno grande — ma vanno prese per quello che sono: convenzioni utili a orientarsi, non leggi di natura. Lo stesso Cohen raccomandava di interpretarle alla luce del proprio campo, non di applicarle a scatola chiusa. <em>Nella pratica quotidiana</em> della SEO, un d di 0,63 sul tempo di sessione è un cambiamento che ha senso prendere sul serio.</p>



<div class="wp-block-group has-background" style="background-color:#f5f7f9;margin-top:2.5rem;margin-bottom:2.5rem;padding-top:1.5rem;padding-right:1.5rem;padding-bottom:1rem;padding-left:1.5rem"><div class="wp-block-group__inner-container is-layout-constrained wp-container-core-group-is-layout-eed7543b wp-block-group-is-layout-constrained">

<figure class="wp-block-image size-large"><img decoding="async" width="1290" height="510" src="https://www.gironi.it/blog/wp-content/uploads/2026/07/effect-size-sovrapposizione.png" alt="Che aspetto ha un effect size: due gruppi normali separati di d deviazioni standard. Anche un effetto «grande» (d = 0,8) lascia i gruppi sovrapposti al 69%, un effetto medio all'80%, uno piccolo al 92%. Il d = 0,63 dell'esempio cade fra il pannello centrale e quello di destra — «medio tendente a grande»." class="wp-image-4056" srcset="https://www.gironi.it/blog/wp-content/uploads/2026/07/effect-size-sovrapposizione.png 1290w, https://www.gironi.it/blog/wp-content/uploads/2026/07/effect-size-sovrapposizione-300x119.png 300w, https://www.gironi.it/blog/wp-content/uploads/2026/07/effect-size-sovrapposizione-1024x405.png 1024w, https://www.gironi.it/blog/wp-content/uploads/2026/07/effect-size-sovrapposizione-1200x474.png 1200w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /><figcaption class="wp-element-caption">Che aspetto ha un effect size: due gruppi normali separati di d deviazioni standard. Anche un effetto «grande» (d = 0,8) lascia i gruppi sovrapposti al 69%, un effetto medio all&#8217;80%, uno piccolo al 92%. Il d = 0,63 dell&#8217;esempio cade fra il pannello centrale e quello di destra — «medio tendente a grande».</figcaption></figure>

</div></div>



<p class="wp-block-paragraph">C&#8217;è però un dettaglio che vale tutto il resto dell&#8217;articolo, ed è già visibile qui sopra: l&#8217;intervallo di confidenza del d va da −0,24 a 1,49. Attraversa lo zero. In altri termini, con dodici sole sessioni per gruppo, l&#8217;effetto <em>stimato</em> è medio, ma i dati non bastano a escludere che quello <em>vero</em> sia nullo. E infatti, se sottoponiamo gli stessi numeri a un t-test, troviamo un p-value tutt&#8217;altro che rassicurante:</p>



<pre class="wp-block-code"><code>t.test(B, A)
#
# 	Welch Two Sample t-test
# t = 1.5321, df = 21.9, p-value = 0.1398</code></pre>



<p class="wp-block-paragraph">Un effetto medio che il test dichiara <em>non</em> significativo. Non è una contraddizione: è esattamente il fenomeno che la potenza di un test serve a spiegare. Teniamolo a mente, ci torniamo tra poco.</p>



<h2 class="wp-block-heading">L&#8217;effect size per le proporzioni (CTR e conversioni)</h2>



<p class="wp-block-paragraph">Il tempo sulla pagina è una media, ma il pane quotidiano di chi fa SEO sono le proporzioni: CTR, tasso di conversione, percentuale di rimbalzo. Qui il Cohen&#8217;s d non si applica direttamente, e la misura naturale di effect size è il <strong>Cohen&#8217;s h</strong>, costruito apposta per la differenza tra due proporzioni.</p>



<p class="wp-block-paragraph">Il dettaglio tecnico che lo rende affidabile è una trasformazione — l&#8217;arcoseno della radice della proporzione — che serve a stabilizzare la variabilità (in una proporzione, la variabilità dipende dal valore stesso, ed è massima attorno al 50%). La formula è:</p>



\( h = 2\arcsin\sqrt{p_2} &#8211; 2\arcsin\sqrt{p_1} \\ \)



<p class="wp-block-paragraph">dove <em>p</em><sub>1</sub> e <em>p</em><sub>2</sub> sono le due proporzioni a confronto. Non serve calcolarla a mano: ce la fornisce la funzione <code>ES.h</code> del pacchetto <code>pwr</code>. Ma prima di vederla all&#8217;opera conviene introdurre l&#8217;altra metà del discorso, perché è lì che il Cohen&#8217;s h dà il meglio di sé.</p>



<p class="wp-block-paragraph">Prima però chiudiamo il capitolo effect size con un cenno onesto alle altre misure. Quando i gruppi a confronto sono più di due — il classico scenario dell&#8217;ANOVA — la misura tipica è l&#8217;<strong>eta quadro</strong> (η²), che dice quale frazione della variabilità totale è spiegata dal fattore che stiamo studiando; ne abbiamo gettato le basi parlando di <a href="https://www.gironi.it/blog/lanalisi-della-varianza-anova-spiegata-semplice/">analisi della varianza</a>. Quando invece il risultato è binario — converte / non converte — l&#8217;effect size si esprime spesso come <strong>odds ratio</strong>, il rapporto tra le quote di successo, lo stesso oggetto che governa la <a href="https://www.gironi.it/blog/la-regressione-logistica/">regressione logistica</a>. Strumenti diversi per domande diverse, ma l&#8217;idea di fondo non cambia: mettere un numero sulla grandezza, non solo sull&#8217;esistenza.</p>



<h2 class="wp-block-heading">La potenza di un test: avremmo potuto vederlo?</h2>



<p class="wp-block-paragraph">Torniamo al nostro effetto medio dichiarato non significativo. Come è possibile che un d di 0,63 produca un p-value di 0,14? La risposta sta in un concetto che chiude il cerchio dell&#8217;inferenza: la <strong>potenza</strong> di un test.</p>



<p class="wp-block-paragraph">Quando facciamo un test delle ipotesi rischiamo due tipi di errore. Il primo, l&#8217;errore di tipo I, è gridare a un effetto che non c&#8217;è: lo teniamo sotto controllo con la soglia α (di solito 0,05). Il secondo, l&#8217;errore di tipo II, è il suo opposto ed è molto più subdolo: <em>non vedere</em> un effetto che invece c&#8217;è. La probabilità di commetterlo si indica con β, e la <strong>potenza</strong> è il suo complemento:</p>



\( \text{potenza} = 1 &#8211; \beta \\ \)



<p class="wp-block-paragraph">Detto in termini più chiari e diretti, la potenza è la probabilità di accorgersi di un effetto reale quando c&#8217;è davvero. Una potenza di 0,80 — lo standard a cui si punta — significa che, se l&#8217;effetto esiste delle dimensioni ipotizzate, il nostro test lo individua quattro volte su cinque.</p>



<p class="wp-block-paragraph">Il punto cruciale è che potenza, soglia α, effect size e dimensione del campione non sono quattro manopole indipendenti: sono <strong>legate da un vincolo</strong>. Fissati tre di questi valori, il quarto è determinato. Questa è l&#8217;intera idea della power analysis, ed è ciò che la rende così utile: a seconda di quale incognita lasciamo libera, risponde a due domande operative diverse.</p>



<p class="wp-block-paragraph">Ed ecco perché il nostro effetto medio è rimasto invisibile. Con dodici sessioni per gruppo la potenza del test era bassissima: il test era, semplicemente, <em>cieco</em>. Un risultato non significativo, in queste condizioni, non dice &#8220;l&#8217;effetto non c&#8217;è&#8221;; dice &#8220;non avevo occhi abbastanza buoni per vederlo&#8221;. Confondere le due cose è uno degli errori più costosi che si possano fare leggendo un A/B test.</p>



<h2 class="wp-block-heading">Power analysis in R: quanti dati servono</h2>



<p class="wp-block-paragraph">La prima domanda che la power analysis sa risolvere è quella che ogni test dovrebbe affrontare <em>prima</em> di partire: quanti dati mi servono? Riprendiamo il nostro effetto medio. Se volessimo progettare un test capace di rilevare un d di 0,63 con potenza 0,80 e soglia 0,05, calcolo in R con il pacchetto <code>pwr</code>:</p>



<pre class="wp-block-code"><code>library(pwr)
pwr.t.test(d = 0.63, sig.level = 0.05, power = 0.80, type = "two.sample")
#
#      Two-sample t test power calculation
#               n = 40.53396
#               d = 0.63
#       sig.level = 0.05
#           power = 0.8
#     alternative = two.sided
# NOTE: n is number in *each* group</code></pre>



<p class="wp-block-paragraph">Servirebbero circa <strong>41 sessioni per gruppo</strong>, non dodici. Ecco perché il nostro test era muto: stava cercando un effetto medio con un terzo dei dati necessari. La power analysis, fatta <em>a monte</em>, ci avrebbe risparmiato un test inconcludente — ed è esattamente il ragionamento che alimenta il <a href="https://www.gironi.it/blog/calcolatore-sample-size-ab-test/">calcolatore di sample size</a>: dimensione del campione e potenza sono le due facce della stessa medaglia.</p>



<div class="wp-block-group has-background" style="background-color:#f5f7f9;margin-top:2.5rem;margin-bottom:2.5rem;padding-top:1.5rem;padding-right:1.5rem;padding-bottom:1rem;padding-left:1.5rem"><div class="wp-block-group__inner-container is-layout-constrained wp-container-core-group-is-layout-eed7543b wp-block-group-is-layout-constrained">

<figure class="wp-block-image size-large"><img decoding="async" width="1020" height="630" src="https://www.gironi.it/blog/wp-content/uploads/2026/07/power-curva-n.png" alt="La curva di potenza per l'esempio del Cohen's d = 0,63 (t-test a due campioni, α = 0,05). Con dodici sessioni per gruppo la potenza era appena 0,31 — il test era «cieco»; per raggiungere lo standard di 0,80 servono 41 sessioni per gruppo. Ecco perché un effetto medio poteva risultare non significativo." class="wp-image-4057" srcset="https://www.gironi.it/blog/wp-content/uploads/2026/07/power-curva-n.png 1020w, https://www.gironi.it/blog/wp-content/uploads/2026/07/power-curva-n-300x185.png 300w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /><figcaption class="wp-element-caption">La curva di potenza per l&#8217;esempio del Cohen&#8217;s d = 0,63 (t-test a due campioni, α = 0,05). Con dodici sessioni per gruppo la potenza era appena 0,31 — il test era «cieco»; per raggiungere lo standard di 0,80 servono 41 sessioni per gruppo. Ecco perché un effetto medio poteva risultare non significativo.</figcaption></figure>

</div></div>



<p class="wp-block-paragraph">La seconda domanda è speculare e si pone <em>a posteriori</em>, quando il test l&#8217;abbiamo già fatto: con i dati che avevo, quanta potenza avevo davvero? Lo vediamo meglio su un caso concreto.</p>



<h2 class="wp-block-heading">Un caso pratico: l&#8217;A/B test che &#8220;non ha funzionato&#8221;</h2>



<p class="wp-block-paragraph">Mettiamo di aver testato due landing page. La A ha convertito 60 visitatori su 1.500 (4,0%), la B ne ha convertiti 78 su 1.500 (5,2%). A occhio la B sembra nettamente migliore — un punto e due decimi di conversione in più non è poco. Verifichiamo in R se la differenza regge:</p>



<pre class="wp-block-code"><code>prop.test(c(60, 78), c(1500, 1500), correct = FALSE)
#
# 	2-sample test for equality of proportions
# X-squared = 2.461, df = 1, p-value = 0.1167</code></pre>



<p class="wp-block-paragraph">Il p-value è 0,117: sopra lo 0,05. Verdetto da manuale: differenza non significativa, test fallito, si archivia. Ma noi adesso sappiamo di non fermarci qui. Calcoliamo la potenza che quel test aveva davvero, partendo dall&#8217;effect size osservato:</p>



<pre class="wp-block-code"><code>library(pwr)
h &lt;- ES.h(0.052, 0.040)   # Cohen's h tra le due proporzioni
h
# [1] 0.0574024

pwr.2p.test(h = h, n = 1500, sig.level = 0.05)
#               power = 0.3492384</code></pre>



<p class="wp-block-paragraph">La potenza era <strong>0,35</strong>. In altri termini: anche se la B fosse stata davvero migliore di quel tanto, avevamo poco più di una probabilità su tre di accorgercene. Il test non ha &#8220;dimostrato che le due pagine sono uguali&#8221;: era semplicemente troppo debole per pronunciarsi. E quanti dati sarebbero serviti per arrivare a una potenza decente?</p>



<pre class="wp-block-code"><code>pwr.2p.test(h = h, power = 0.80, sig.level = 0.05)
#               n = 4764.053</code></pre>



<p class="wp-block-paragraph">Quasi <strong>4.800 visitatori per variante</strong>, contro i 1.500 che avevamo. La differenza tra un test che &#8220;non ha funzionato&#8221; e un test mai realmente in condizione di funzionare è tutta qui — e si vede solo se all&#8217;effect size affianchiamo la potenza. <strong>Attenzione</strong> dunque a derubricare un risultato non significativo a &#8220;nessun effetto&#8221;: quasi sempre stiamo solo guardando un test sottodimensionato.</p>



<h2 class="wp-block-heading">Prova tu</h2>



<p class="wp-block-paragraph">Per fissare il meccanismo, ecco un esercizio con dati verosimili. Stiamo progettando un A/B test su un modulo di contatto. Il tasso di conversione attuale (baseline) è il <strong>2,5%</strong>, e considereremmo un successo portarlo al <strong>3,0%</strong>: mezzo punto di miglioramento. Vogliamo un test con potenza 0,80 e soglia 0,05.</p>



<p class="wp-block-paragraph">Il compito: calcolare l&#8217;effect size con <code>ES.h(0.030, 0.025)</code>, passarlo a <code>pwr.2p.test</code> fissando <code>power = 0.80</code>, e leggere quanti visitatori per variante servono. Poi, come controprova, calcolare la potenza che avremmo se ci fermassimo a 3.000 visitatori per variante con <code>pwr.2p.test(h = ..., n = 3000, ...)</code>.</p>



<p class="wp-block-paragraph">Per controllare i conti: l&#8217;effect size è <em>h</em> = 0,031, servono circa <strong>16.759 visitatori per variante</strong> per avere una potenza dello 0,80, e con soli 3.000 la potenza crollerebbe a <strong>0,22</strong>. La morale è quella che ormai conosciamo: più l&#8217;effetto che cerchiamo è piccolo, più dati servono per vederlo: dimezzare la differenza minima rilevabile non raddoppia il campione necessario, lo quadruplica.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p class="wp-block-paragraph">Effect size e potenza completano la triade che il p-value, da solo, lasciava monca: non più soltanto <em>l&#8217;effetto esiste?</em>, ma anche <em>quanto è grande?</em> e <em>avrei potuto vederlo?</em>. Sono le tre domande che trasformano un test da rito propiziatorio in uno strumento di decisione. E tutte e tre, a ben guardare, dipendono da una scelta che viene <em>prima</em> del test: quanti dati raccogliere, e come. È il terreno del disegno sperimentale e del <a href="https://www.gironi.it/blog/campionamento-e-dimensione-campionaria-quanti-dati-servono/">campionamento</a> — il punto in cui la statistica smette di limitarsi a giudicare i numeri che le mettiamo davanti e comincia a dirci quali numeri andare a cercare.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">Per approfondire</h3>



<p class="wp-block-paragraph">Sull&#8217;uso rigoroso di effect size, potenza e dimensionamento nel contesto degli esperimenti online, il riferimento più completo resta <a href="https://www.amazon.it/dp/1108724264?tag=consulenzeinf-21&#038;ascsubtag=effect-size-e-power-analysis" rel="nofollow sponsored noopener" target="_blank"><em>Trustworthy Online Controlled Experiments</em></a> di Ron Kohavi, Diane Tang e Ya Xu: i capitoli su come dimensionare un test e interpretarne i risultati valgono da soli l&#8217;acquisto. Per i fondamenti statistici alle spalle di questi concetti — errori di tipo I e II, potenza, inferenza — un buon manuale italiano di riferimento è <a href="https://www.amazon.it/dp/8891910651?tag=consulenzeinf-21&#038;ascsubtag=effect-size-e-power-analysis" rel="nofollow sponsored noopener" target="_blank"><em>Statistica</em></a> di Newbold, Carlson e Thorne.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.gironi.it/blog/effect-size-e-power-analysis/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
