OpenWork Tech Blog

オープンワークの開発チームが届ける、情報プラットフォームを支える技術と文化

BigQuery上でCVRを求めてp値まで計算する方法

こんにちは。 オープンワーク株式会社アナリストグループ所属の佐々木といいます。

弊社では、アナリストがA/Bテストを集計する際にはBigQueryでクエリを書いています。 その際に差が有意かどうかを判断の参考にするためにp値を算出していますが、p値を計算するためだけに、 いったんスプレッドシートやエクセルに集計結果を貼り付けたりPythonで計算したりなどして二度手間になっていました。 そこでBigQuery上で集計するときに、2つの群に対して比率の差を検定したいときに、p値まで算出できる方法を他社事例も参考にして、工夫しました。

具体的には、JavaScriptの統計ライブラリjStatをGoogle Cloud Storage(GCS)上に配置し、 (1) Zスコアを計算する (2) jStatの関数を呼び出してp値を計算する のように2つの簡単なUDFを書くだけでBigQuery上でp値が求められます。 UDFの中身をみると分かりますが、今回は、Zスコアを用いて両側検定をやっています。

例題

以下のような集計をしていて比率の差に有意差があるかをみたいケースを考えましょう。

ユーザー数 コンバージョンしたユーザー数 コンバージョン率
パターンA 3203 604 18.9%
パターンB 3018 673 22.3%

https://unpkg.com/jstat/dist/jstat.js から最新のjStatをダウンロードしてローカル端末に jstat.js と名前をつけて保存し、 GCS上の gs://custom-tools/statistics/jstat.js (一例) にアップロードします。 この場合のp値の計算の仕方は以下のようなクエリをコピペしてBigQuery上で実行すればOKです。

-- Zスコアを求める
CREATE TEMPORARY FUNCTION get_zscore(usersA FLOAT64, convA FLOAT64, usersB FLOAT64, convB FLOAT64)
RETURNS FLOAT64 
LANGUAGE js AS """
let pA = convA / usersA;
let pB = convB / usersB;

// Pooled probability
let pp = (convA + convB) / (usersA + usersB);

// Standard Error
let se = Math.sqrt(pp * (1 - pp) * (1 / usersA + 1 / usersB));

// Z-score
return (pB - pA) / se;
""";

-- jStatの関数を利用してp値を求める
CREATE TEMPORARY FUNCTION get_pvalue(z FLOAT64)
RETURNS FLOAT64 
LANGUAGE js AS """
  return 2 * (1 - jStat.normal.cdf(Math.abs(z), 0, 1))
"""
/* OPTIONSでGCSに保存したJavaScript Libraryを指定する */
OPTIONS ( 
    library=["gs://custom-tools/statistics/jstat.js"]
);
-- BigQuery上では正しく表示されるも、redash上ではNUMERICにcastしないと0と表示されてしまったのでこのように修正する SELECT CAST(get_pvalue(get_zscore(3203, 604, 3018, 673)) AS NUMERIC) AS p_value 
SELECT get_pvalue(get_zscore(3203, 604, 3018, 673)) AS p_value;

無事、p値が求められました。

BigQueryでp値を計算できました

最後に

どんどん進化しつつある弊社のデータ利活用に興味がある方は、ぜひ採用ページもご覧ください。 www.openwork.co.jp

参考資料

この集計を実施するにあたり、 BigQuery で統計処理を完結させる - GO Tech Blog のt検定のUDFを作る部分を参考にさせていただきました。