渋谷駅前で働くデータサイエンティストのブログ

元祖「六本木で働くデータサイエンティスト」です / 道玄坂→銀座→東京→六本木→渋谷駅前

「カイゼンしたらコンバージョン率が○○%→△△%にup!」は分母を無視したら成り立たないかもしれない

大体どこの会社でも、KPIというと「△△数(実測数)」「○○率」のような数字が多いのではないかと思います。


そのようなKPIを特定のコンバージョン(課金・契約・定着…)と関連づけて、例えばプロモーション・UI改修・導線変更などの改善施策の効果を検証する、というのはWebデータ分析ではよくあるパターンでしょう。


例えば、ある課金を伴うゲームイベント(これをコンバージョンとする)の導線を改善したいと思って、従来の古いイベント導線に加えて新しく別のイベント導線を追加したとしましょう。こういう時に、以下のような2×2の表を作ることはありませんか*1

課金した 課金しなかった
新イベント導線 5 2
旧イベント導線 150 140


で、さらにこれを以下のように解釈することはありませんか?

課金した 課金しなかった 課金率
新イベント導線 5 2 71%
旧イベント導線 150 140 52%


「新しいイベント導線の方が課金率71%!だから絶対新しいイベント導線の方が売上を伸ばせるから全面的に切り替えよう!」と、喜んでいたりしませんか?*2


・・・確かに、課金した「比率」だけ見比べれば新しい導線の方が、古い導線よりもずっと高くなっています。これは事実です。けれども、そこで「52%が71%になったのだから新しい導線になってカイゼン効果が出た!」と思うのは、もしかしたら間違っているかもしれないんです。今回はその辺の仕組みを書いてみようと思います。


「分母」の問題


そもそも、上の例題はまず「分母」に注目する必要があります。ここで想定しているのは「古い導線はそのまま置いといて新たな導線をさらに加える」というようなケースだとしましょう。で、上の例題の「分母」を見てみると・・・

課金した 課金しなかった 分母
新イベント導線 5 2 7
旧イベント導線 150 140 290


ということで、新イベント導線の分母は7、旧イベント導線の分母は290です。そして、それぞれの課金率が71%と52%。これでいきなり「新イベント導線71% > 旧イベント導線52%だから新イベント導線に効果あり!」と結論付けるのは、正しいでしょうか?


これは、基本的にはおかしいです。何故なら、そもそも分母の比率が7:290と40倍以上違うし、それ以上に課金したユーザー数も5:150とやはり30倍も違うからです。


つまり、この2つの結果を並べること自体がおかしいと思わなければいけないはずなのです。でも、これをもって「新イベント導線にカイゼン効果あり!」と喜んじゃう人って多いんですよねぇ。。。


「分布」の問題:95%信頼区間とブートストラップ標本


そこで、もう一つちょっとしたポイントを。それは「分布」と「信頼区間」の問題です。平たく言えば、「真の割合が(例えば)50%の時にサンプルの個数が○○個あったら実際に測定される割合はどれくらいばらつくのか」というお話です。


ここで「信頼区間」について簡単にふれておきます。端的に言えば、「偶然(例えば測定誤差であったり何かしらの避けられないサンプルの偏りであったり)によってある量(数でも率でも)がばらつき得る範囲」のことです。


2つの数字同士の大小を比べる場合、この信頼区間同士が重なってしまう場合は「偶然のばらつきの範囲内でも『2つの数字は等しい』ことになってしまうケースがある」=「2つの数字に差はない」と解釈されます。これらは統計学的にきちんと定義されていて、例えばRなどで簡単に求めることができます。


具体的には、ブートストラップ標本を作ることで求められます。要は「n個のデータがある時に真の割合が○○%だったと仮定して、そのn個のデータをもとに無数のランダムデータを発生させる」というやり方です。


ということで、今回のイベント導線カイゼンの例についても折角なのでデータ分析ツールRで確かめてみましょう。

### パラメータ設定 ###

npar<-N  # サンプルサイズそのもの。Nは好きなように定めて良い
nratio<-r # 帰無仮説として設定したい○○率の値を小数で(60%なら0.6)
nboot<-2000  # ブートストラップ再抽出の回数
Data<-c(rep(1,ceiling(npar*nratio)),rep(0,floor(npar*(1-nratio))))  # 0/1でnratioに応じた数をNから割り振っていく
Data<-as.matrix(Data)  # マトリクスに変換しておく

### 以下分布の計算 ###

#  データ格納用の空行列を作る
ResultBoot      <- matrix(NA, nboot, npar)

#  ブートストラップ再抽出繰り返し計算を行う
for(i in 1:nboot){
    BootID          <- sample(1:nrow(Data), nrow(Data), replace = T)
    BootSample      <- Data[BootID, ]
    ResultBoot[i, ] <- mean(BootSample)
}
#  ここでは「割合」が見たいのでダミー変数の平均mean(BootSample)を見ているが、勿論別の統計値でも良い
#  その場合はsome_function(BootSample, parameter)と事前に決め打ちで書いておく必要がある

(MeanBoot <- mean(ResultBoot[, 1]))  #  ブートストラップ標本平均=仮想上の母平均
(SDBoot   <- sd(ResultBoot[, 1]))  #  ブートストラップ標本標準偏差=仮想上の母標準偏差

#  95%ブートストラップ信頼区間
(ci_low<-MeanBoot - 1.96*SDBoot) #  下側95%点
(ci_up<-MeanBoot + 1.96*SDBoot)  #  上側95%点
hist(ResultBoot) # ヒストグラムを描いて分布を見る


という感じで、今回の例だと「新イベント導線」についてはN = 7, r = 0.71、「旧イベント導線」についてはN = 290, r = 0.52を事前に代入しておけば良いです。その結果を見てみると。。。


「新イベント導線」の場合:信頼区間の下側95%点 = 0.37、上側95%点 = 1.00

f:id:TJO:20130425111833p:plain


「旧イベント導線」の場合:信頼区間の下側95%点 = 0.46、上側95%点 = 0.58

f:id:TJO:20130425111919p:plain


これ、まるでダメですね。ばっちり信頼区間同士が重なっています。ということで、「新イベント導線の課金率:71%」と「旧イベント導線の課金率:52%」は統計学的に見れば差はない(有意差ではない)ということが言えます。


適合度検定で確かめてみる

2グループに差はあるか?

実は、統計学の世界にはズバリこういう問題を検証するための方法が存在します。詳しくは例えば『自然科学の統計学

自然科学の統計学 (基礎統計学)

自然科学の統計学 (基礎統計学)

あたりを読んでもらえるとわかりやすいと思います。要するに、サンプルサイズ(=分母)の異なる2つのグループにおけるYes/No(e.g. 「カイゼンした/しない」「効いた/効かない」「死んだ/死なない」…)の割合が異なる時に、それらが本当に統計的に有意に異なるか?、つまりカイゼンしたのかしないのか?を、検定するという手法です。


基本的なパターンとしては、上の例と同じく

グループ OFF ON
1 y_{11} y_{12}
2 y_{21} y_{22}


というように、2×2分割表の形に書けます。このような場合に、グループ1と2がON/OFFの比率に与える影響が違うか否かを判定する方法の一つとして、独立性の検定というものがあります。代表例としてカイ二乗検定フィッシャーの正確確率検定などがあります。今回は詳しい定義や導き方は省略して*3、手っ取り早くRで実践する方法を紹介します。


まず、上の例をRにマトリクスとして読み込ませます。

> x=matrix(c(5,2,150,140),ncol=2,byrow=T)
> print(x)
     [,1] [,2]
[1,]    5    2
[2,]  150  140


次に、標準パッケージの中に入っているchisq.test関数とfisher.test関数を使います。たったこれだけです。

> chisq.test(x) # カイ二乗検定

	Pearson's Chi-squared test with Yates' continuity correction

data:  x 
X-squared = 0.4205, df = 1, p-value = 0.5167 # 有意差なし:つまり両者に差はない

 警告メッセージ: 
In chisq.test(x) :  カイ自乗近似は不正確かもしれません # 「新イベント導線」のサンプルサイズが小さすぎると文句を言っている
> fisher.test(x) # フィッシャーの正確確率検定:サンプルサイズの大小を問わず使えるが時間がかかることもある

	Fisher's Exact Test for Count Data

data:  x 
p-value = 0.4507 # やはり有意差なし:つまり両者に差はない
alternative hypothesis: true odds ratio is not equal to 1 
95 percent confidence interval:
  0.3736408 24.8107839 
sample estimates:
odds ratio 
  2.326977 


ということで、独立性の検定に基づいてチェックしてみても、やはり「新イベント導線は旧イベント導線に比べて課金率をカイゼンしたとは言えない」という結論になります。

ならどこまでサンプルが増えれば良いのか?

「新イベント導線のカイゼン効果は、そもそも分母が小さすぎるからよく分からない」というのがここまでの結論でした。それでは、課金率71%のままどこまで分母が増えたら「効果あり」と言えるんでしょうか?


試しにRでやってみると、この辺が判断ラインになるようです。

> x=matrix(c(25,10,150,140),ncol=2,byrow=T)
> print(x)
     [,1] [,2]
[1,]   25   10
[2,]  150  140
> chisq.test(x)

	Pearson's Chi-squared test with Yates' continuity correction

data:  x 
X-squared = 4.1185, df = 1, p-value = 0.04242 # 有意差あり

> fisher.test(x)

	Fisher's Exact Test for Count Data

data:  x 
p-value = 0.03126 # 有意差あり
alternative hypothesis: true odds ratio is not equal to 1 
95 percent confidence interval:
 1.034614 5.634335 
sample estimates:
odds ratio 
  2.327572
課金した 課金しなかった 分母
新イベント導線 25 10 35
旧イベント導線 150 140 290


ということで、分母が5倍になったところでようやく独立性の検定の結果から言えば「新イベント導線に改善効果あり」と言ってよいかな?という感じになります。


既に気付いた人もいるかと思いますが、これは旧イベント導線の分母の大きさにもある程度依存します。Rをインストール済みの方は、ぜひ自分で色々数字を変えてみて結果がどう変わるかを試してみて下さい。


なお、今回取り上げた例はものすごーーーーーく極端なケースです。さすがにここまで極端に分母が違うケースは珍しいと思います。けれども、これに近い状況はWebデータ分析の世界ではしょっちゅう起きている、というのが個人的な印象です*4。そういう場面で、うっかり拙速な結論を出さないように気を付けたいものです。


では、どうすれば良いのか?


結論から言うと、「構わず強行する」か「ある程度証拠がたまるまで待つ」かの二択です。


理由はシンプルで、実は新イベント導線がこのまま流入数が7から(例えば)300ぐらいに増えても課金率71%を保ち続けるということもあり得ますし、もちろんその反対もあり得ます。


なので、もしこの先も課金率71%が続くと固く信じるのなら構わず強行して完全に新イベント導線に置き換えても良いし、もちろんしばらく時間が経って実際に新イベント導線の流入数が十分にたまってきて、上記の分布の比較や独立性の検定をクリアして、なおかつ旧イベント導線よりも課金率で有意に上回ると立証されたら、新イベント導線のみに固定させるという考え方もあります。中には、新イベント導線の課金率がもっともっと高くなって、結果的にできるだけ早いタイミングで新イベント導線に全面的に切り替えた方が良かったのに!なんてケースもあるかもしれません。


この辺はもうただのバクチなので、要は誰かが決断しなきゃいけないことです。けれども、その決断に必要なエビデンスが得られるまで、慌てずに待つ、そして見極める、という態度もありなのではないかと僕は思っています。

*1:これは猛烈に極端な例です、念のため

*2:何度でも書きますがこれは猛烈に極端な例です、念のため

*3:見せかけの回帰の記事が全然人気がなかったので懲りました(泣)

*4:というか有意差があるかどうか微妙なケースで改善効果を判定しなきゃいけなくなったことは実際にある