  {"id":3880,"date":"2026-06-26T08:58:45","date_gmt":"2026-06-26T07:58:45","guid":{"rendered":"https:\/\/www.gironi.it\/blog\/?p=3880"},"modified":"2026-06-26T08:58:47","modified_gmt":"2026-06-26T07:58:47","slug":"ab-test-bayesiano","status":"publish","type":"post","link":"https:\/\/www.gironi.it\/blog\/ab-test-bayesiano\/","title":{"rendered":"A\/B test bayesiano: non solo &#8220;se&#8221; B \u00e8 meglio di A, ma &#8220;di quanto&#8221;"},"content":{"rendered":"\n<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 \u00e8 il pane quotidiano di chi fa esperimenti online. Ma c&#8217;\u00e8 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\u00e0 operativa \u00e8 un&#8217;altra, molto pi\u00f9 diretta: <strong>qual \u00e8 la probabilit\u00e0 che B sia meglio di A?<\/strong> E, subito dopo: di quanto \u00e8 meglio, e quanto possiamo fidarci di quel &#8220;di quanto&#8221;?<\/p>\n\n\n\n<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>\n\n\n\n<!--more-->\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Di cosa parleremo<\/strong>:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Due posterior invece di uno: una distribuzione per ciascuna variante<\/li><li>Qual \u00e8 la probabilit\u00e0 che B vinca davvero<\/li><li>Di quanto \u00e8 meglio: la distribuzione della differenza<\/li><li>Quando fermarsi: expected loss e il problema del peeking<\/li><li>Prova tu<\/li><li>Per approfondire<\/li><\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Due posterior invece di uno<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Quando abbiamo stimato il conversion rate di una singola variante, abbiamo visto che \u2014 partendo da un prior <a href=\"https:\/\/www.gironi.it\/blog\/la-distribuzione-beta-spiegata-semplice\/\">Beta<\/a> e osservando dati binomiali \u2014 il posterior \u00e8 ancora una distribuzione Beta. La regola di aggiornamento era una semplice aritmetica: se il prior \u00e8 Beta(\u03b1, \u03b2) e osserviamo \\( k \\) conversioni su \\( n \\) sessioni, il posterior \u00e8:<\/p>\n\n\n\n\\( Beta(\\alpha + k,\\ \\beta + (n &#8211; k)) \\\\ \\)\n\n\n\n<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\u00f2, \u00e8 identico: costruiamo <strong>un posterior per ciascuna variante<\/strong>, in modo indipendente, applicando la stessa regola due volte.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Facciamo un esempio al volo. Abbiamo testato due versioni di una landing page, assegnando i visitatori a caso:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>Variante A<\/strong> (controllo): 90 conversioni su 1000 sessioni \u2192 tasso grezzo 9,0%<\/li><li><strong>Variante B<\/strong> (trattamento): 120 conversioni su 1000 sessioni \u2192 tasso grezzo 12,0%<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Partiamo da un prior non informativo Beta(1, 1) per entrambe \u2014 &#8220;non sappiamo nulla, prima dei dati ogni tasso \u00e8 ugualmente plausibile&#8221;. Applicando la regola, il posterior di A \u00e8 Beta(91, 911) e quello di B \u00e8 Beta(121, 881).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Costruisco i due posterior in R, campionandoli per simulazione:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>set.seed(42)\n# Variante A: 90 conv \/ 1000 ; Variante B: 120 conv \/ 1000\ncA &lt;- 90; nA &lt;- 1000; cB &lt;- 120; nB &lt;- 1000\n\n# Posterior con prior uniforme Beta(1,1)\npostA &lt;- rbeta(1e5, 1 + cA, 1 + nA - cA)   # Beta(91, 911)\npostB &lt;- rbeta(1e5, 1 + cB, 1 + nB - cB)   # Beta(121, 881)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Adesso abbiamo in mano due distribuzioni, non due numeri. Ed \u00e8 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>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Qual \u00e8 la probabilit\u00e0 che B vinca?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">La prima domanda \u2014 quella che il p-value non risponde mai direttamente \u2014 \u00e8 la probabilit\u00e0 che B sia davvero migliore di A.<br> Con i posterior in mano, il calcolo \u00e8 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>\u00e8<\/em> la probabilit\u00e0 che cerchiamo.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Calcolo in R la probabilit\u00e0 che B batta A:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cat(\"P(B&gt;A) =\", round(mean(postB &gt; postA), 3), \"\\n\")<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Output: P(B&gt;A) = 0.985.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>C&#8217;\u00e8 il 98,5% di probabilit\u00e0 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 \u00e8 improbabile sotto l&#8217;ipotesi nulla&#8221;: stiamo dicendo, in modo diretto, che date le evidenze raccolte \u00e8 quasi certo che B sia la variante migliore. \u00c8 esattamente l&#8217;affermazione su cui vorremmo basare una decisione \u2014 e l&#8217;approccio bayesiano ce la consegna senza giri di parole.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Di quanto \u00e8 meglio: la distribuzione della differenza<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Sapere che B vince con probabilit\u00e0 del 98,5% non basta a decidere. Un miglioramento c&#8217;\u00e8 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 \u00e8 quindi: <em>di quanto<\/em> \u00e8 meglio?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La risposta vive 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\u00ec leggiamo sia il valore tipico (la media) sia un intervallo di credibilit\u00e0 che ne quantifica l&#8217;incertezza.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Calcolo in R la differenza e il suo intervallo al 95%:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>diff &lt;- postB - postA\ncat(\"uplift medio (punti %) =\", round(mean(diff)*100, 2), \"\\n\")\ncat(\"CI 95% differenza =\", round(quantile(diff, c(.025,.975))*100, 2), \"\\n\")<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Output: uplift medio = 3.00 punti %, CI 95% = [0.31, 5.68].<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Il guadagno atteso \u00e8 di circa 3 punti percentuali di conversione, con un intervallo di credibilit\u00e0 al 95% che va da 0,31 a 5,68 punti.<\/strong><br> Anche qui il significato \u00e8 diretto, non una propriet\u00e0 astratta della procedura: c&#8217;\u00e8 il 95% di probabilit\u00e0 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 \u2014 coerentemente con il 98,5% di prima \u2014 che B \u00e8 quasi certamente superiore. Ma il dato prezioso \u00e8 l&#8217;ampiezza: il miglioramento potrebbe essere modesto (mezzo punto) o robusto (oltre cinque punti), e questa forbice \u00e8 un&#8217;informazione che la decisione operativa deve tenere presente.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Quando fermarsi: expected loss e il problema del peeking<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Nell&#8217;articolo sull&#8217;A\/B testing classico abbiamo dedicato spazio a uno degli errori pi\u00f9 insidiosi: il <a href=\"https:\/\/www.gironi.it\/blog\/peeking-problem\/\">peeking<\/a>, cio\u00e8 sbirciare i dati intermedi e fermarsi appena la differenza sembra significativa. Nell&#8217;impianto frequentista questo gonfia il tasso di falsi positivi, perch\u00e9 ogni occhiata \u00e8 di fatto un nuovo test sulla stessa ipotesi nulla, e i test ripetuti moltiplicano le occasioni di sbagliare.<\/p>\n\n\n\n<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 \u00e8 &#8220;fermati quando P(B&gt;A) \u00e8 alta&#8221;, ma si basa sull&#8217;<strong>expected loss<\/strong> \u2014 la perdita attesa.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">L&#8217;idea \u00e8 questa: se scegliamo B ma in realt\u00e0 fosse A la variante migliore, sbagliamo, e l&#8217;entit\u00e0 dell&#8217;errore \u00e8 quanto A batte B in quei casi. La perdita attesa di scegliere B \u00e8 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>\n\n\n\n<p class=\"wp-block-paragraph\">Calcolo in R la perdita attesa scegliendo B:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Expected loss scegliendo B: perdita attesa se in realta' A fosse meglio\nloss_B &lt;- mean(pmax(postA - postB, 0))\ncat(\"expected loss scegliendo B =\", round(loss_B*100, 3), \"punti %\\n\")<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Output: expected loss scegliendo B = 0.007 punti %.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La perdita attesa scegliendo B \u00e8 di appena 0,007 punti percentuali: trascurabile. In termini pi\u00f9 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 \u2014 per esempio &#8220;mi fermo quando la perdita attesa scende sotto 0,01 punti&#8221; \u2014 e si lascia correre il test finch\u00e9 non la si raggiunge.<\/p>\n\n\n\n<p class=\"has-light-gray-background-color has-background wp-block-paragraph\">Un avvertimento: la libert\u00e0 di guardare i dati in corsa non \u00e8 un permesso di fermarsi quando il risultato ci aggrada. La regola di arresto \u2014 la soglia di expected loss, o un livello minimo di P(B&gt;A) \u2014 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>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Prova tu<\/h2>\n\n\n\n<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>\n\n\n\n<p class=\"wp-block-paragraph\">1. 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. 2. Calcola <strong>P(B&gt;A)<\/strong>: B \u00e8 migliore con probabilit\u00e0 abbastanza alta da convincerti? 3. Calcola <strong>uplift medio e intervallo al 95%<\/strong> della differenza: l&#8217;intervallo tocca lo zero? 4. Calcola la <strong>expected loss<\/strong> scegliendo B. Con questi numeri (pi\u00f9 vicini tra loro del caso visto sopra), come cambia rispetto all&#8217;esempio dell&#8217;articolo?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Suggerimento: la struttura del codice \u00e8 identica a quella che abbiamo usato. Cambiano solo i conteggi di partenza \u2014 e il risultato, molto meno netto, \u00e8 proprio il motivo per cui l&#8217;intervallo e la perdita attesa contano pi\u00f9 di un semplice &#8220;ha vinto B&#8221;.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\">Finora abbiamo confrontato due varianti a campione fisso: raccogliamo i dati, calcoliamo, decidiamo. Ma se durante il test una variante si rivela nettamente migliore, perch\u00e9 continuare a mandare met\u00e0 del traffico su quella peggiore? Si pu\u00f2 fare di meglio: allocare il traffico in modo adattivo, spostandolo verso la variante che sta vincendo mentre il test \u00e8 ancora in corso. \u00c8 il salto dal test al <em>bandit<\/em>, l&#8217;argomento del prossimo articolo.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Per approfondire<\/h3>\n\n\n\n<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 \u00e8 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 \u2014 probabilit\u00e0 che B vinca, distribuzione della differenza, perdita attesa. \u00c8 pensato per chi impara meglio leggendo codice che dimostrazioni.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>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 \u00e8 il pane quotidiano di chi fa esperimenti online. Ma c&#8217;\u00e8 uno scarto sottile tra quello che il p-value ci dice e quello &hellip; <a href=\"https:\/\/www.gironi.it\/blog\/ab-test-bayesiano\/\" class=\"more-link\">Leggi tutto<span class=\"screen-reader-text\"> &#8220;A\/B test bayesiano: non solo &#8220;se&#8221; B \u00e8 meglio di A, ma &#8220;di quanto&#8221;&#8221;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_uag_custom_page_level_css":"","footnotes":""},"categories":[629],"tags":[],"class_list":["post-3880","post","type-post","status-publish","format-standard","hentry","category-statistica-it"],"lang":"it","translations":{"it":3880,"en":3881},"uagb_featured_image_src":{"full":false,"thumbnail":false,"medium":false,"medium_large":false,"large":false,"1536x1536":false,"2048x2048":false,"post-thumbnail":false},"uagb_author_info":{"display_name":"Paolo Gironi","author_link":"https:\/\/www.gironi.it\/blog\/author\/autore-articoli\/"},"uagb_comment_info":0,"uagb_excerpt":"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 \u00e8 il pane quotidiano di chi fa esperimenti online. Ma c&#8217;\u00e8 uno scarto sottile tra quello che il p-value ci dice e quello&hellip;","_links":{"self":[{"href":"https:\/\/www.gironi.it\/blog\/wp-json\/wp\/v2\/posts\/3880","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.gironi.it\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.gironi.it\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.gironi.it\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.gironi.it\/blog\/wp-json\/wp\/v2\/comments?post=3880"}],"version-history":[{"count":1,"href":"https:\/\/www.gironi.it\/blog\/wp-json\/wp\/v2\/posts\/3880\/revisions"}],"predecessor-version":[{"id":3886,"href":"https:\/\/www.gironi.it\/blog\/wp-json\/wp\/v2\/posts\/3880\/revisions\/3886"}],"wp:attachment":[{"href":"https:\/\/www.gironi.it\/blog\/wp-json\/wp\/v2\/media?parent=3880"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.gironi.it\/blog\/wp-json\/wp\/v2\/categories?post=3880"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.gironi.it\/blog\/wp-json\/wp\/v2\/tags?post=3880"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}