A/Bテストで使える統計的検定手法をPython で実装する


統計検定の問題を解いていると、有意差etc.. 検定という語句が登場します。
その有意差という言葉で、統計学が最強の学問である | 西内 啓 |本 | 通販 | Amazon に、「ABテストの結果の有意差の判断に統計が使える」 ということが書かれていることを思い出しました。

思い出したことをきっかけで検索すると、以下のような記事がヒットし、なるほどと思っておりました。

記事は実装がRでしたので、上記のデータを拝借して、 Python で各種検定を実装してみました。
実装した結果を記載します。


参考

実装時に、以下の記事を参考にしました。


使用するデータについて

A/Bテストに用いられれる統計的検定手法(ロジック)のまとめ&比較 | RCO Ad-Tech Lab Blogに記載されている、コンバージョンが発生したか否かを示すデータを用います。

施策名 / コンバージョンしたか?YESNO
施策A(改善案サイト)45420,933
施策B(現状サイト)18910,845

実装する検定について

こちらも、A/Bテストに用いられれる統計的検定手法(ロジック)のまとめ&比較 | RCO Ad-Tech Lab Blog に記載されている以下検定を Python で実装します。

  • χ2検定
  • 二項検定
  • t検定

また、上記とともに CVR の計算を行います。

CVR の計算

#施策AのCVR
yes = 454
no = 20933
result = yes / (yes + no )
print(result)
0.02122784869313134
#施策BのCVR    
yes = 189
no = 10845
result = yes / (yes + no )
print(result)
0.017128874388254486

A のほうがCVRが高いです。

χ2検定 (カイ二乗検定)

R だと、 chisq.test() で計算できますが、python の場合、scipy.stats.chi2_contingency で計算できます。

import scipy.stats
import numpy as np
data = np.matrix([ [ 454, 20933 ], [ 189, 10845 ] ])
chi2, p, ddof, expected = scipy.stats.chi2_contingency( data , correction=False)
msg = "Test Statistic: {}\np-value: {}\nDegrees of Freedom: {}\n"
print( msg.format( chi2, p, ddof ) )
print( expected )
Test Statistic: 6.291035173658824
p-value: 0.012135015711322547
Degrees of Freedom: 1

[[   424.16461553  20962.83538447]
 [   218.83538447  10815.16461553]]

R と同様の結果が得られます。
scipy.stats.chi2_contingencyの引数で指定する、correction には、補正の有無を指定します。
補正とは、イェイツのカイ二乗検定 - Wikipedia
のことかと思われます。


二項検定

scipy.stats.binom_test を使用して、計算を行います。

import scipy.stats
import numpy as np
data = np.matrix([ [ 454, 20933 ], [ 189, 10845 ] ])
p_a = np.sum(data, axis=1).item((0, 0)) / data.sum()
print("p_a=", p_a)
conversion_total = np.sum(data, axis=0).item(0, 0)
print("conversion_total=", conversion_total)
conversion_a  = data.item(0,0)
print("conversion_a=", conversion_a)
# 確率 p_a の基で、n回試行して、x回 (Aは)yesになった
p_value = scipy.stats.binom_test(x=conversion_a, n=conversion_total, p=p_a, alternative="two-sided")
print("p_value=", p_value)
p_a= 0.659665031924
conversion_total= 643
conversion_a= 454
p_value= 0.0125320280817

t検定

対応しないt検定の場合は、ttest_indequal_var = False にして計算を行います。

import scipy.stats
import numpy as np
data = np.matrix([ [ 454, 20933 ], [ 189, 10845 ] ])
# YES を 1、NO を 0 とする、配列を作成する 要素数合計 は 454 + 20933 
a = np.hstack((np.repeat(1, data.item(0,0)),  np.repeat(0, data.item(0, 1))))
print("a=", a)

# YES を 1、NO を 0 とする、配列を作成する 要素数合計 は 189 + 10845 
b = np.hstack((np.repeat(1, data.item(1,0)),  np.repeat(0, data.item(1, 1))))
print("b=", b)

# t検定実施 対応するt検定の場合は、equal_var = True
res = scipy.stats.ttest_ind(a, b,  equal_var = False)
print("t_value", res[0])
print("p_value", res[1])
a= [1 1 1 ..., 0 0 0]
b= [1 1 1 ..., 0 0 0]
t_value 2.59373998665
p_value 0.00949948651733

R の結果と変わらず、各計算結果を出力することができました。
p_value が小さければ、有意性がある(意味)とだけ覚えておけば良さそうです。

実際に仕事で使う場合は、python での計算というか、Excel や、Google スプレッドシートで式を組んで使用する気もしますので、後日、Google スプレッドシートで式を組んでみようかと思いました。

以上です。

コメント