読者です 読者をやめる 読者になる 読者になる

廿TT

譬如水怙牛過窓櫺 頭角四蹄都過了 因甚麼尾巴過不得

A/Bテストのパラドックス(?)のシミュレーション

A/Bテスト グラフ

はじめに

A/Bテストツールというものがあります。

これはWebページ(内のパーツ)を出し分けて、どっちがユーザーに使われやすいかというのを調べるツールです。

有名なA/Bテストツールのひとつ『オプティマイズリー』を見てみます。

このツールには下図のような折れ線グラフを表示する機能があるようです。

f:id:abrahamcow:20150321092817p:plain
Optimizely Increases Homepage Conversion Rate by 29% | Optimizely Blog より)

縦軸をCVR、横軸をテスト開始からの経過日数としています。

ここでCVRとはコンバージョンレートの略で、Webページの目標(商品購入など)に到達した件数をWebページへのアクセス数で割ったものです。

A/Bテストのシミュレーション

さてちょっとしたシミュレーションをやってみます。

いま、赤いボタンと青いボタンを用意し、どっちのほうが商品購入に結びつきやすいか調べているとします。

f:id:abrahamcow:20150321094248j:plain
f:id:abrahamcow:20150321094241j:plain
カート・購入ボタン:フリー(無料)WEB素材-テンプレートキング より)

赤いボタン、青いボタンを均等に出し分け、どちらも一日あたり平均1000回表示されるだけのアクセス数があるとします(ポアソン到着を仮定)。

そして実は赤いボタンにしようが青いボタンにしようが差はなく、真のCVRは一緒、2%だったとします。

A/Bテストに長い時間をかければかけるほど、累積アクセス数、すなわちCVRの分母の数(サンプルサイズ)は増え、真のCVRが正確に推定されるように思えます。

このテストを一ヶ月観察した結果、折れ線グラフはこんな感じです。

f:id:abrahamcow:20150403230335p:plain

点線で示したのがシミュレーションで仮定した真のCVRです。

赤いボタンのCVRが若干勝っているように見えます。

#R のコード
IMP_A <- rpois(1000, 1000)
IMP_B <- rpois(1000, 1000)
CV_A <-sapply(IMP_A,function(i)rbinom(1, size=i, prob=0.02))
CV_B <-sapply(IMP_B,function(i)rbinom(1, size=i, prob=0.02))

totalIMP_A <- cumsum(IMP_A)
totalIMP_B <- cumsum(IMP_B)
totalCV_A <- cumsum(CV_A)
totalCV_B <- cumsum(CV_B)

CVR<-cbind(totalCV_A/totalIMP_A,totalCV_B/totalIMP_B)

#nihongo()
matplot(CVR[1:30,]*100,
        type="l",lwd=2,lty=1,col=c("red2","royalblue"),
        ylab="CVR(%)",xlab="経過日数",main="30日間")
abline(h=2,lty=3, lwd="3")

このテストにさらに時間をかけ、1000日間見守り続けた結果、折れ線グラフはこんな感じになります。

f:id:abrahamcow:20150403230420p:plain

matplot(CVR*100,
        type="l",lwd=2,lty=1,col=c("red2","royalblue"),
        ylab="CVR(%)",xlab="経過日数",main="1000日")
abline(h=2,lty=3, lwd="3")

赤いボタンと青いボタンともにの真のCVR、2%の周辺に落ち着いてきました。

しかし、A/Bテストに1000日というのは、時間をかけすぎていて現実的でないです。

もう一つ別のシミュレーションをやってみます。

先ほどの折れ線グラフは縦軸をCVR、横軸をテスト開始からの経過日数にしていましたが、今度は縦軸をCVR、横軸をボタンが表示された回数にします。

f:id:abrahamcow:20150321150626p:plain

CVR2 <- data.frame(sapply(1:1000,function(x)rbinom(1, size=x, prob=0.02))/1:1000,
sapply(1:1000,function(x)rbinom(1, size=x, prob=0.02))/1:1000)

matplot(CVR2*100,
        type="l",lwd=2,lty=1,col=c("red2","royalblue"),
        ylab="CVR(%)",xlab="表示回数")
abline(h=2,lty=3, lwd="3")

表示回数1000回でだいたい真のCVR、2%を中心に分布し、赤いボタンも青いボタンも大差ないように見えます。

一日あたり平均1000回アクセスがあるとして、さっきは30日間かけても真のCVRが推定できなかったのに、今度は一日で正しい結論に到達しました。

なぜこうなるのか

なんでこうなるのかはよくわかりません。

だれか教えてください。

ご提案

A/Bテストツールを開発される方は、「縦軸をCVR、横軸をテスト開始からの経過日数」の折れ線グラフではなく、「縦軸をCVR、横軸を表示回数」の折れ線グラフが表示される機能を実装したほうがいいと思います。