asken テックブログ

askenエンジニアが日々どんなことに取り組み、どんな「学び」を得ているか、よもやま話も織り交ぜつつ綴っていきます。 皆さまにも一緒に学びを楽しんでいただけたら幸いです!

Firebaseイベント、BigQuery、Google Data Studioを利用したABテストの可視化

f:id:techaskeninc:20211208103108p:plain

はじめに

はじめまして!

askenで北米版アプリのサーバーサイド開発を担当している大倉と言います。サーバーサイド開発の他、データの抽出や可視化も担当しています。

北米版では全ての施策に対してABテストを行い、その結果を元に意思決定をしています。FirebaseのコンソールやGoogle Analyticsを使えば、コンバージョン率などの簡単な指標の確認はできます。しかし、コンバージョン率だけではなく、より細かい指標を分析したい場合は、独自でFirebaseのイベントログを解析し、分析する必要があります。北米版あすけんでは、上に添付している画像のレポートを利用して、ABテストの結果を可視化・分析し、意思決定に繋げています。(一部の情報は黒塗りで表示しています。)

今回は、北米版あすけんで取り組んでいるABテストの結果を可視化する方法をご紹介したいと思います。

対象読者

  • 基本的なクエリが書ける人
  • FirebaseのコンソールやGoogle Analyticsを用いたABテスト分析では物足りない人
  • ABテストをより細かく分析して意思決定の材料にしたい人

Firebase, BigQuery, Google Data Studioを使ったABテスト結果の可視化

Firebaseイベントとして記録するべき情報

北米版あすけんでは、ユーザーの行動ログをFirebaseのイベントとして記録しています。新しい機能を実装したら、基本的には以下の3つのイベントをアプリに新しく仕込んでいます。

  1. pvイベント
  2. tapイベント
  3. actionイベント

pvイベントとは、あるページを開いた際に発火するイベントです。ユーザーがあるページに遷移したかどうかを知りたいときなどに利用できるイベントです。

tapイベントとは、ボタンやトグルなどアプリ上の押下可能なコンポーネントが押下された際に発火するイベントです。新しく追加した機能がユーザーに利用されているかどうかなどを知りたい時に使えるイベントです。

最後にactionイベントとは、ある機能を利用してアクションを起こした際に発火するイベントです。あすけんで例えると、食事を登録した時、食事を編集した時などにactionイベントを発火させています。ある機能に対するコンバージョン率などを調べたい際に利用できるイベントです。

これらのイベントを実装したら、次はBigQueryを使って、欲しいデータに加工します。

BigQueryを使ってFirebaseのイベントログを加工する

今度は、BigQueryを使ってFirebaseから送られてきたイベントログを可視化するのに必要な形に整えます。ABテストの結果として知りたい情報は、基本的には以下の二つです。

  1. イベント発火率
  2. イベント発火数

イベントの発火率は、ある機能やページがどれくらいのユーザーに利用されたのかを把握する際に使用します。

一方、イベント発火数はある機能やページがユーザーによって何回利用されたのかということを把握する際に利用することができます。

それでは実際にクエリを書いてみたいと思います。以下の手順でクエリを書きます。

  1. 対象イベントログの生ログを確認する。
  2. ユーザーごとにイベント発火率とイベント発火数の情報を持つデータを抽出する。
  3. 上記データに対してABテストの情報を付与したデータを抽出する。

まず、情報を抽出したい対象のイベントログにどのような情報が含まれているのかを確認します。

北米版あすけんには ユーザーが食事登録を完了した際に発火するlogged_foodというactionイベントがあります。これを例にデータを抽出してみたいと思います。

SELECT
    *
FROM
    `{dataset_name}.events_*` as e
WHERE
    -- 生ログの確認がしたい時などは_TABLE_SUFFIXにを日付を指定してあげるとクエリのデータ量の節約になる。
    (_TABLE_SUFFIX = '20210101')
    -- イベント名を指定する
    AND event_name = "logged_food"
LIMIT 1

結果は以下です。 f:id:techaskeninc:20211208103203p:plain

Firebaseのイベントログには、デフォルトで記録される情報と独自で記録した情報が含まれます。独自で記録したパラメータについて簡単に紹介したいと思います。 logged_foodの例で言えば、event_params.keytypeに記録されている値(event_params.value.string_value)が独自で記録した情報になります。こちらのパラメータの情報を使うことで、どの機能を経由して食事が登録されたかということを識別することができます。例えば、北米版あすけんにはテキスト検索や履歴や画像検索など、食事を登録する複数の方法があります。これらの機能を識別するための情報がevent_params="type"というパラメータに含まれています。イベントログに独自のパラメータ情報を持たせると、より細かい分析が可能になります。こちらのパラメータの情報を用いて使われた機能を識別する方法は次で詳しく説明したいと思います。

次にユーザーごとにlogged_foodを発火したか(食事登録をしたかどうか)というフラグと、何回発火したか(何回食事を登録したか)という情報を取得したいと思います。

SELECT
    user_id,
    -- ユーザーの1回のアクションに対して複数回イベントが発火する場合が存在する。それらのログを排除するため、event_timestamp(イベントが発火した時間)がユニークなログをカウントするようにしている。
    COUNT(DISTINCT IF (event_name = "logged_food", event_timestamp, null)) as num_logged_food
    -- 一人のユーザーが指定しているイベントを発火しているかどうかを1, 0で表す。この値の平均値をとるとイベントの発火率がわかる。平均値はData Studioの機能で算出する。
    , MAX(IF (event_name = "logged_food", 1, 0)) as flg_logged_food
    , MAX(IF (event_name = "logged_food", ep.key = "type", ep.value.string_value = "text", 1, 0)) as flg_logged_food_text
    , MAX(IF (event_name = "logged_food", ep.key = "type", ep.value.string_value = "history", 1, 0)) as flg_logged_food_history
FROM
    `{dataset_name}.events_*` as e
    , unnest(`event_params`) as ep
WHERE
    -- テスト実行したい時などは、_TABLE_SUFFIXに日付を指定してあげるとクエリのデータ量の節約になる。
    (_TABLE_SUFFIX = "20210101")
GROUP BY
     user_id

結果

f:id:techaskeninc:20211208103219p:plain

ここで注意するべきポイントは、イベント発火率・イベント発火数の出し方です。

イベントの名称の指定はMAX(IF (event_name = "logged_food", 1, 0))のようにIF文の中でしています。こうすることで、他のイベント情報も並列で取得することができるようになります。例えば、delete_foodというイベントがあったとしたら、MAX(IF (event_name = "delete_food", 1, 0))というカラムを増やすだけで他のイベント情報を追加することができます。where文でevent_nameの指定をする必要はありません。

イベントの発火率を出すため、ユーザーごとに発火したかどうかというフラグ情報を持たせます。 この情報をフラグとして持たせる理由は、全てのユーザーの当フラグの平均値をとると、ある機能がどれくらいの割合のユーザーによって利用されたのかを知ることができるからです。平均値はData Studioの機能を使って算出するため、BigQueryでデータを加工する際には、ユーザーごとにフラグを持たせるところまでを行います。Data Studioの機能を使ってフラグの平均値をとる方法は後ほど詳しく説明します。

イベント発火数は、ユーザーに紐づくイベントの数を合計することで取得することができます。ただし、ここで注意点がひとつあります。イベントが一度発火すると、複数のログが残るケースが見られます。それらの重複ログを排除するため、event_timestamp(=イベントが発火した時間)がユニークなものだけをカウントします。そうすることでより正確なイベント発火数を取得することができます。

どの機能を経由して食事が登録されたかという情報を識別したい場合どうすれば良いかもここで説明します。上述したように、event_params.keytypeに記録されている情報を使うことで、どの機能を経由して食事が登録されたかということを識別することができます。

Firebaseのイベントは情報がネスト化して記録されているので、必要な情報を引っ張ってくる際には、ネストを解除してあげる必要があります。ネストの解除はunnest()を使って行います。

event_paramsをunnestした情報を使ったのが、select文の6行目と7行目になります。 このように、IF文の中でkeyとvalueを指定することができます。where文で指定することもできますが、そうしない理由としては、where文でunnestしたevent_paramsを指定すると、unnestしたevent_paramsを使いまわすことができなくなるからです。例えば、text経由とhistory経由の情報をそれぞれ取得したとなった場合、where文でevent_paramsの値にtextを指定してしまうとhistoryの情報は取得できなくなってしまいます。しかし、IF文で条件指定をしてあげればunnestしたevent_paramsの値を固定することなく、使いまわすことができます。

最後に、上記データに対してABテストの情報を付与したデータを抽出します。 北米版あすけんでは、ABテストの振り分けはモバイル側で行い、その振り分けの結果をサーバーに保存しています。したがって、DBに保存されている振り分け結果を先ほど抽出した結果に付け加えます。ABテストの情報を付与する理由としては、Data Studioのフィルタリング機能を使ってABテストごとの結果を見れるようにするためです。

ABテストのテーブルの構成は以下のようになっています。

id       INTEGER
user_id  INTEGER
name     STRING
pattern  STRING
created  STRING
modified STRING
SELECT
    e.user_id
    , ab.name
    , pattern
    -- ユーザーの1回のアクションに対して複数回イベントが発火する場合が存在する。それらのログを排除するため、event_timestamp(イベントが発火した時間)がユニークなログをカウントするようにしている。
    , COUNT(DISTINCT IF (event_name = "logged_food", event_timestamp, null)) as num_logged_food
    -- 一人のユーザーが指定しているイベントを発火しているかどうかを1, 0で表す。この値の平均値をとるとイベントの発火率がわかる。平均値はData Studioの機能で算出する。
    , MAX(IF (event_name = "logged_food", 1, 0)) as flg_logged_food
    , MAX(IF (event_name = "logged_food" and ep.key = "type" and ep.value.string_value = "text", 1, 0)) as flg_logged_food_text
    , MAX(IF (event_name = "logged_food" and ep.key = "type" and ep.value.string_value = "history", 1, 0)) as flg_logged_food_history
FROM
    `{dataset_name}.events_*` as e
    , unnest(`event_params`) as ep
LEFT JOIN a_b_testing as ab USING(user_id)
WHERE
    (_TABLE_SUFFIX = "20210101")
GROUP BY
     user_id
     , ab.name
     , pattern

結果

f:id:techaskeninc:20211208103236p:plain

ユーザーによっては複数のABテストに割り当てられているため、ユーザーのイベント発火率・発火数の情報は割り当てられたABテストの数だけ複製されることになります。こうすることでData Studioのフィルタリング機能を使って、分析したABテストを選択し、そのABテストの結果のみを見れるようになります。ABテストをレポート化することで、クエリを書けないメンバーであっても誰でもABテストの結果にアクセスし分析することが可能になるというメリットがあります。

それではこの結果を使って、Data Studioで結果を可視化したいと思います。

Data StudioでABテストの結果を可視化する

以下の手順でABテストの可視化を行います。

  1. レポートを作成し、データソースを追加する。
  2. レポートにABテストのフィルタリング機能を追加する。
  3. グラフを追加し、イベントの発火率・発火数を確認できるようにする。

まず、新規のレポートを作成し、作成したクエリを利用してデータソースを作成します。データを追加/カスタムクエリページに遷移し、カスタムクエリ入力欄に、BigQueryで作成したクエリを入力し、追加します。

f:id:techaskeninc:20211208103256p:plain

次に、ABテストの名前でフィルタリングをかけるためのコントロールを追加します。コントロールを追加/プルダウンリストを選択し、コントロールフィールドにABテスト名を値としてもつカラムを選択します 。今回の例では、nameカラムがそれに該当します。こちらのコントローラーを利用することで、ABテストごとにフィルターをかけることができるようになります。

f:id:techaskeninc:20211208103316p:plain f:id:techaskeninc:20211208103321p:plain

最後に、発火率・発火数が確認できるグラフを追加し、ABテストのバリアント(テストパターン)間の比較ができるようにします。

棒グラフを追加し、ディメンションにバリアント情報を持つカラムを指定します。今回の例でいうと、patternがそれに該当します。そして、指標にイベントを発火したかどうかを表すフラグを値として持つカラムを指定します。今回はflg_logged_foodがそれに該当します。

グラフが追加できたら、どれくらいのユーザーがそのイベントを発火したのかを知るための数値を算出・表示します。デフォルト設定では、指定した指標の値が"合計"になっているので、これを"平均値"に変更します。平均値に変更すると0.5などの数値を取得することができます。これをパーセント表記にするため、指標のタイプを数値/%に変更します。パーセント表記にすることで直感的な比較が可能になります。

f:id:techaskeninc:20211208103341p:plain

今のままだと割合がグラフ上に表示されないため、グラフにカーソルを持っていく必要があり手間がかかります。そのため、グラフ上に数値を表示させるための修正をします。グラフのスタイルページにて、"データラベルを表示"にチェックをつけます。すると、バリアントごとのコンバージョン率がそれぞれグラフ上に表示されるようになります。

f:id:techaskeninc:20211208103402p:plain

他の指標もそれぞれ同じ要領で追加します。発火回数も同じように平均値で表示します。

f:id:techaskeninc:20211208103416p:plain

以上でABテストの結果の可視化ができました。あとはフィルターの値を確認したいABテスト名に変更し、バリアント間の数値を比較します。

まとめ

今回は、北米版askenで取り組んでいるABテストの結果を可視化する方法についてご紹介しました。

今回は、一部のイベントログのみを利用しましたが、出来るだけ多くのログをData Studioのレポートに取り込むことで、ABテストの結果をもれなく分析することができるようになります。北米版あすけんでは、アプリのページごとのレポートを作成し様々な指標を盛り込んで、細かくABテストの結果が分析できるようにしています。

基本的な作り方はこの記事の通りですので、皆さんもぜひ作ってみてください。

積極採用中です!

askenでは、一緒に働いてくれるエンジニアを募集しています。少しでも興味を持っていただけたら、ぜひ採用情報をご確認ください。 www.wantedly.com