DAU、MAUを継続的に測定する際に気をつけていること
こんにちは、えんがわです。ナビタイムジャパンでスマートフォン向けアプリ『乗換NAVITIME』のサーバーサイド開発を担当しています。
サービス改善や効果測定の指標の一つであるDAU、MAUを継続的に測定する際に気をつけているポイントをご紹介いたします。
DAU、MAUとは?
DAU、MAUのAUとは「アクティブユーザー」の略語で特定の期間内に1回以上サービスを利用したユーザーを指します。
DAUは「デイリーアクティブユーザー」の略語で1日にサービスを1回以上利用したユーザー数を指します。
同一ユーザーが1日にサービスを10回利用しても、1回のみの利用だとしても1ユーザーとしてカウントします。
MAUは「マンスリーアクティブユーザー」の略語で1ヶ月にサービスを1回以上利用したユーザー数を指します。
合わせてDAU / MAU比率 (アクティブ率)を測定することで日常的にサービスを利用しているユーザーの割合を知ることができます。
DAU、MAU、アクティブ率を継続的に測定してサービス改善や施策の指標にします。
DAU、MAUの測定方法
DAU、MAUの測定にはアプリから収集したイベントログを利用して集計します。
イベントログはAWSのAthenaを利用して集計できるようになっていますのでAthena上でクエリを実行し、DAU、MAUを測定します。
DAU、MAUの測定で気にすること
気にしているポイントは2点です。
アクティブユーザーの定義をメンバー間で擦り合わせる
集計時のコスト
DAU、MAUは自分一人だけが確認するのではなく、プロダクトに関連するメンバー全員が継続的に確認する指標です。
初めてDAU、MAU測定するときは具体的にどういった数値を継続的に確認したいか、何を持ってアクティブユーザーとするかを予めメンバー間で擦り合わせておくと良いと思います。
例えば、
・OS別にユーザー数を知りたい
・ウィジェットを登録しているが、アプリを起動していないユーザーはアクティブユーザーとしてカウントしない
などです。
AUの定義が変更になったときもメンバーに共有することで認識齟齬を産ませないことも大切だと考えています。
擦り合わせた定義に沿った条件で絞り込みを行うため、AさんとBさんの集計結果が違ってどちらを信じてよいか分からないといったことも防ぐことができます。
Athenaでは2022年10月現在、東京リージョンの場合スキャンデータ1TBあたり5.00USDのコストが掛かります。
参考:https://aws.amazon.com/jp/athena/pricing/
過去データの推移・比較を行いたい場合は集計期間は広くとる必要があり、サービス規模に応じてイベント量も多くなり、スキャンデータも増えていきます。
継続的に確認したい場合はスキャンの頻度も高くなります。
そのため、できるだけ1回あたりのスキャンデータ量は減らしたいところです。
DAUのみの集計であれば、集計期間を1日にして結果を毎日蓄積すれば1回あたりのスキャンデータを減らすことができますが、DAU以外の集計では利用できません。
他の集計でも利用できるように汎用的なAU集計用のテーブルを新たに作成しました。
AU集計用のテーブルの作成時の注意点
集計用のテーブルを作る目的は、集計クエリをシンプルにすることとスキャンデータの削減が主な目的になります。
集計用のテーブルを作らずに直接イベントログからAU集計を行うと不必要なデータまでスキャンしてしまったり、集計する人の認識齟齬によって数値が変わったり、非起動時のイベントを弾くといった条件をAU集計全てのクエリで考慮を入れる必要があります。
今回はアクティブユーザーとしてカウントする対象はアプリを起動してサービスを利用したユーザーとしました。
そのため、ウィジェットなどのアプリを起動しなくても実行されるイベントは除外しておく必要があります。
AWS S3にテーブルで利用するデータを作成しています。
テーブル作成時に以下の点に注意しました。
不必要な情報は削る
作成するテーブルをある程度汎用的にする
作成するテーブルでやりたいことが実現できることを確認する
結果の妥当性を確認する
汎用的なテーブルにすることで他の集計にも利用することができますが、情報を増やしすぎるとスキャンデータの削減量が少なくなってしまうのでバランスが大切です。
DAUやMAUはサービス改善等の指標となりますので、テーブルを作成する前にクエリ内で仮の中間テーブルを作成しやりたいことが実現できるか、結果が妥当であるかを確認しました。
今回はFirebaseなどの他分析ツール上の数値・傾向が集計結果とほぼ一致していたので、問題ないと判断しました。
テーブル作成
上記の注意点を踏まえてイベントログを元にOS別、会員状態別に集計できるようなシンプルなテーブルを作成しました。
同一ユーザーが1日に10回サービスを利用してもユーザー1人あたりにできるレコードは1つになり、ウィジェットなどのアプリ非起動時のイベントは弾くようにしました。
初めてテーブル作成する際は、過去のイベントログもスキャンする必要がありますが、初回以降は毎日前日分のイベントログのスキャンをして作成したテーブルにデータを入れていきます。
手動で毎日データを入れるのは大変なので、運用がうまく回ってきたところで自動で前日分のデータが投入されるような仕組みを入れました。
実際にDAU、MAUを集計してみる
集計例として下記3パターンの集計を行ってみます。
OS・年別のDAU
会員のOS・年別の積上MAU
日次のアクティブ率
数値はサンプルデータで、作成したテーブル名をau_tとしています。
結果が出たらスプレッドシートなどで見やすい形にして結果を確認します。
OS・年別のDAU
昨年の1/1から前日までのOS_年別のDAUを集計します。
シンプルなクエリで集計ができました。
SELECT OS || '_' || SUBSTR(access_date, 1,4) AS OS_YEAR,
SUBSTR(access_date, 5, 4) AS DATE,
COUNT(DISTINCT user_id) AS DAU
FROM au_t
WHERE access_date BETWEEN DATE_FORMAT(NOW() - INTERVAL '1' YEAR, '%Y') || '0101' AND DATE_FORMAT(NOW() - INTERVAL '1' DAY, '%Y%m%d')
GROUP BY 1,2
ORDER BY 2,1
会員のOS・年別の積上MAU
MAUは月末に数値が確定するため、月の途中で達成率の確認ができません。
毎日達成率を確認するために積上のMAUを見てみます。
1/5であれば、1/1~1/5までのAU数がわかるようなイメージです。
-- 月ごとの初回サービス起動日とuser_idを紐付け
WITH first_access_t AS
(select SUBSTR(access_date, 1,6) AS MONTH,
MIN(DATE_PARSE(access_date, '%Y%m%d')) AS first_open_date,
user_id,
OS
from au_t
WHERE access_date BETWEEN DATE_FORMAT(NOW() - INTERVAL '1' YEAR, '%Y0101') AND DATE_FORMAT(NOW() - INTERVAL '1' DAY, '%Y%m%d')
AND status = '会員'
GROUP BY 1,3,4
)
SELECT
OS || '_' || DATE_FORMAT(date, '%Y') AS OS_YEAR,
DATE_FORMAT(date, '%m%d') AS DATE,
COUNT(DISTINCT user_id) AS MAU
FROM
first_access_t,
-- 初回サービス起動日から月末まで
UNNEST(SEQUENCE(
first_open_date,
LEAST(CAST(NOW() - INTERVAL '1' DAY AS TIMESTAMP), DATE_PARSE(DATE_FORMAT(first_open_date + INTERVAL '1' MONTH, '%Y%m01'), '%Y%m%d') - INTERVAL '1' DAY),
INTERVAL '1' DAY
)) AS t(date)
GROUP BY 1,2
ORDER BY 2,1
月初に数値がリセットされるため、グラフにするとノコギリの刃のような形になります。
日次のアクティブ率
アクティブ率はDAU / MAUで計算しますが、今回はDAU / 直近30日のAU で計算しています。
直近30日の日付一覧を作成する際はsequence関数が便利です。
WITH access_t AS
(SELECT DATE_PARSE(access_date, '%Y%m%d') AS date,
user_id,
OS
FROM au_t
WHERE access_date BETWEEN DATE_FORMAT(NOW() - INTERVAL '1' YEAR, '%Y0101') AND DATE_FORMAT(NOW() - INTERVAL '1' DAY, '%Y%m%d')
)
SELECT
OS_YEAR,
DATE,
ROUND(100 * CAST(DAU AS DOUBLE) / CAST(MAU AS DOUBLE), 4) AS ACTIVE_RATE
FROM (
SELECT
OS || '_' || DATE_FORMAT(mau_date, '%Y') AS OS_YEAR,
DATE_FORMAT(mau_date, '%m%d') AS DATE,
COUNT(DISTINCT IF(date = mau_date, user_id, NULL)) AS DAU,
COUNT(DISTINCT user_id) AS MAU
FROM
access_t,
-- 直近30日
UNNEST(SEQUENCE(
date,
date + INTERVAL '30' DAY,
INTERVAL '1' DAY
)) AS t(mau_date)
WHERE
mau_date >= DATE_PARSE(DATE_FORMAT(NOW() - INTERVAL '1' YEAR, '%Y0131'), '%Y%m%d')
GROUP BY 1,2
)
ORDER BY 1,2
結果
汎用的な集計用テーブルを作成したことにより、1つのテーブルから複数の集計が可能となりました。
テーブル作成時に細かい条件を絞り込んであるので、集計用テーブルからシンプルなクエリで集計できるようになりました。
集計コストもイベントログからのスキャンと比べて1 / 50ほどになりました。
DAUやMAUなど定期的に確認するものはRedashやJenkinsを使ってクエリの定期実行をして毎日最新のデータが反映されるようにしています。
最後に
今回はDAU、MAUを継続的に測定する際に注意しているポイントをご紹介させていただきました。
システム構成やサービスの特徴によって工夫するポイントは異なると思いますが、リテンション率など別の数値にも応用できると思います。
少しでもお役に立てれば幸いです。