BigQueryで統計的検定をできるようにする

BigQueryで検定をしたい

分析データの格納先として BigQuery(BQ)が多くの会社で使われていますが、実際に分析をするときに統計的検定を行いたいことがあると思います。

しかし、BQ で検定をするためには面倒なことも多く、「もっと手軽にできるようにしたい」と感じることがあります。

そんな場面で役立つようなツールを今回作りました。現時点で対応している検定の種類は少ないですが、このツールを使えば BigQuery 内で簡単に検定を行えるようになります。

BigQueryでの検定はなぜ面倒か?

そもそも BigQuery 内での検定がなぜ面倒かというと、検定が BQ 内部で完結せず、何かしら外部のツールを使わなければいけないからです。

BQ ではデフォルトで備わっている統計関連の処理として、算術平均を計算する AVG 関数や不偏標本分散を計算する STDDEV_SAMP などの関数があります。ただ、残念ながら現状これらの結果を用いて検定を行う機能はありません。

また、BQ には User Defined Functions(UDF)という機能もあり、有志の方が統計処理を行うための関数を作っていらっしゃいますが、例えばt検定を行う t_test 関数では算出されるのがt値のみでp値は自分で確認しなければなりません(以下の GitHub ページから関数の詳細を確認できます)*1

github.com

そのため、現状 BQ で統計的検定をしようとすると、おおよそ以下のような方法で行うことになります。

  • 前述のUDFを実行し、t分布表などを元にp値を出す。それを元に検定結果を出す。
  • BQ のデータを Python で扱い、scipystatsmodels パッケージを使って検定する。

ここで問題なのが、どちらの方法においても何かしら外部のツールが必要になることです(一点目の方法なら分布表、二点目の方法なら Jupyter Notebook などの Python の実行環境)。

検定をする毎にこのように何かしら使わなければならないのは非常に骨が折れます。

どのような解決を行ったか?

そこで今回利用したのが、現在プレビューで提供されているリモート関数という機能です。こちらを使うことで検定を BQ 内で完結させ、前述の手間を無くすことができます。

cloud.google.com

この機能について簡単に説明すると、リモート関数とは BigQuery から Cloud Functions / Cloud Run を実行することができる機能で、これにより BQ のデータに対して何らか加工を行い、その結果を BQ に返すことができるようになっています。

今回私が作成したツールでは、この機能を使って検定に必要なデータを Cloud Functions に送り、それを元に検定を行い結果を返す、という処理を行うようにしました。図示すると以下のようになります。

BQのクエリ内で分析データから検定に必要な情報を取得し、Cloud Functionsで検定を行う

実装の詳細

以下は検定をする上では必須の情報ではないため、必要に応じて「## 実際に検定をしてみる」まで読み飛ばすなどして頂ければと思います。

今回実装したリモート関数の詳細ですが、次のようになっています。なお、ソースコード・Cloud Functions へのデプロイの方法に関しては次の GitHub のレポジトリに載せています。

github.com


bq-remote-functions-stats/
├── .env.template
├── .gcloudignore
├── .gitignore
├── main.py
├── Makefile
├── README.md
├── requirements.txt
├── statstest.py
└── statstests/
    ├── __init__.py
    └── prop.py

この中で主要なファイルの役割は以下のようになっています。

  • main.py
    • BQ から受け取ったデータをパースし、実施したい検定の種類(母比率の差の検定/母平均の差の検定など)と、検定に必要な標本に関する情報を取得する。
    • これらの情報を元に statstest.py で定義されたクラスをインスタンス化し、検定を行い、その結果を BQ に返す。
  • statstest.py
    • 検定を行うためのクラス(StaticalTest クラス)を定義したファイル。
    • このクラスでは、行いたい検定の種類に応じて statstests ディレクトリ配下で定義されたモジュールを呼び出し、検定を行う。
  • statstests ディレクト
    • 各種検定を定義したディレクトリ。現在は母比率の差の検定(prop.py)にのみ対応(今後対応できる検定を増やして行こうと考えています)。
  • statstests/prop.py
    • 母比率の差の検定を定義したモジュール。現在は両側検定にのみ対応。
    • 検定自体は statsmodels パッケージで行い、p値などを出す。

実際に検定をしてみる

では、上記のツールを実際に使って BQ で検定を行ってみましょう。

例えば、ある施策を AB テストで実施し、A を従来のパターン、B を新パターンとして CVR に対して有意水準10%の両側検定をしたいとします。すなわち、帰無仮説と対立仮説はそれぞれ以下になります。

  • 帰無仮説: AパターンとBパターンの母比率は等しい
  • 対立仮説: AパターンとBパターンの母比率は等しくない(差がある)

そして、データの集計が終わり以下のような結果が出たとします。

  • Aパターン: 対象者100人、20人がCV
  • Bパターン: 対象者100人、10人がCV

この結果を元に BQ で以下のようなクエリを実行します。

WITH
-- 施策結果のデータを作成
prep AS (
  SELECT
    10 AS successes,   -- AパターンのCV人数
    100 AS trials  -- Aパターンの対象者数
  UNION ALL
  SELECT
    20,  -- BパターンのCV人数
    100  -- Bパターンの対象者数
)

SELECT
  `analysis-test-337607.stats.prop_test`(successes, trials)  -- リモート関数の実行
FROM
  prep

すると、以下のような結果になります。

画像より Pval: 0.047670 となっておりp値が0.1よりも小さいことから、帰無仮説は棄却され、AパターンとBパターンは等しくないと考えられます。このようにして検定結果を導くことができました。

まとめ

今回は BigQuery で検定を行うのに手間が掛かるという問題に対して、リモート関数を使って BQ 内で検定を完結できるようにしました。

まだ対応しているものは少ないですが、Python で出来る検定はリモート関数でも出来るはずなので、これから増やしていこうと考えています。

今回の記事がもし興味深いと思われましたら、スターを付けて頂けると嬉しいです。(対応可能な検定を増やすモチベーションになり、また、次回以降の記事を書く励みにもなります!)

*1:JavaScript で定義した UDF を使えばp値も一度に出すことが出来ますが、両側/片側検定、対応のある/なし、t検定以外の検定をしたくなったときに、BQ 内が非常に複雑になってしまうというデメリットがあります。