KPI proxy:直接取れない指標をどう定義するか
Stage 3 — 第3章 | dbt入門カリキュラム 推定学習時間:40〜50分 | 難易度:★★★☆☆
この章で学ぶこと
第2章 で mart の grain と business question を学びました。 次は 直接取れない指標を proxy で定義する 実践です。
「会員は何人いるか」はシンプルに見えて、実は 定義が最も議論されやすい指標 です。 Auth テーブルの行数をそのまま数えるのではなく、監査可能な proxy 列(明示的な同意日など)を選び、YAML に caveats を書く——これが KPI mart の設計の核心です。
まず dbt の作法を整理し、続けて DS Playground の fct_membership_daily と fct_membership_conversion_daily を読み解きます。
この章を終えると、こんなことができるようになります:
- terms 同意 proxy を選んだ理由と限界を説明できる
- date spine + 累積和で日次会員推移 mart を読める
paid_member_conversion_rateとsame_day_paid_conversion_rateの違いを説明できるsafe_ratioマクロが比率列で使われていることを認識できる
なぜ proxy が必要か
KPI の母集団は、必ずしも OLTP の「正本テーブル」と一致しません。
| よくある状況 | 問題 |
|---|---|
| Auth / ユーザー管理テーブル | analytics から直接読むと PII・権限境界を越える |
| 初回ログイン | パッシブ行動であり、activity SSOT と定義が不一致 |
| 明示的な同意・登録イベント | アプリ設計上 signup と一体。監査可能 |
指標定義 で学ぶ「率の分子分母を会議前に固定する」原則は、何を会員と呼ぶか の選択にもそのまま当てはまります。 データマネジメント入門 で学んだ「必要最小の参照」も、モデル設計に現れます。
proxy を選ぶときは 3 点を YAML に書きます。
- なぜこの列か(他の選択肢を却下した理由)
- 含まれないユーザーは誰か(同意記録欠落など)
- ソース側の削除・修正で数字がどう変わるか
日次会員推移 mart の一般パターン
business question の典型: 「日別に、会員は何人まで増えているのか」 grain: 1 行 = 1 日
-- 概念構造(一般形)
with members as (
select user_id, min(proxy_event_at) as first_member_at
from {{ ref('stg_...') }}
where /* proxy 条件 */
group by 1
),
date_spine as (
select generate_series(min_date, max_date, interval '1 day')::date as date_day
from bounds
),
daily_new as (
select
cast(first_member_at as date) as date_day,
count(distinct user_id) as new_members
from members
group by 1
)
select
date_day,
new_members,
sum(new_members) over (order by date_day) as total_members
from filled;
設計上のポイントは 2 つです。
- date spine … 活動ゼロの日も行が存在する。期間比較 と同様、欠損日を埋める設計
- 累積列 …
total_membersは日次 MAX(累積のスナップショット)として扱う
同意・登録イベント"] SPINE["date spine
全日付"] DAILY["日次新規 COUNT"] CUM["累積 SUM
total_members"] MART["fct_membership_daily"] STG --> DAILY SPINE --> MART DAILY --> CUM --> MART
転換率 mart の一般パターン
会員数に加え 有料化率 を見る mart では、中間モデルから 日付 2 列(signup 日・初回課金日)を橋渡しに使います。
| 列の種類 | 典型定義 | 解釈 |
|---|---|---|
| ストック型転換率 | 累計有料 / 累計会員 | 全体 の有料化率 |
| フロー型転換率 | 同日有料化 / 同日新規 | その日 signup した人のうち同日課金した割合 |
フロー型は便利ですが、解釈範囲が狭い caveats が必ず付きます。
- 分母が小さい日はブレやすい … 新規 3 人で 1 人課金 → 33%
- 「同日」は calendar date ベース … 23:50 登録・翌 00:10 課金は同日転換に含まれない
- 後日課金は別日に反映 … 7 日後課金は
new_paid_membersの別日に載る - 分母 0 は NULL …
safe_ratio等で 0 除算を防ぐ
率と KPI で学んだ率の注意点が、そのまま列名と caveats に現れます。
terms 同意 proxy
DS Playground では、Supabase Auth を analytics から直接読まず、terms 同意を会員数 proxy として使います。
stg_supabase__consents の consent_type = 'terms' and is_granted = true を会員の正本 proxy とします。
| 選択肢 | DS Playground の判断 |
|---|---|
auth.users 行数 |
analytics から 直接読まない(境界・権限・PII) |
| 初回ログイン | パッシブ行動であり Learning Action SSOT と不一致 |
| terms 同意 | アプリ設計上 signup と一体。監査可能な 明示的同意 |
YAML の caveats として明示されている限界:
- Auth 作成後に同意記録が失敗したユーザーは 会員数に含まれない
- 退会時に consents が削除される運用では、次回 build 後 累計から外れる
- これは「バグ」ではなく proxy 定義の結果 として説明する
fct_membership_daily
business question: 「日別に、規約同意済み会員は何人まで増えているのか」
grain: 1 行 = 1 日
-- 概念構造(簡略)
with terms_members as (
select user_id, min(captured_at) as first_terms_granted_at
from {{ ref('stg_supabase__consents') }}
where consent_type = 'terms' and is_granted = true
group by 1
),
date_spine as (
select generate_series(min_date, max_date, interval '1 day')::date as date_day
from bounds
),
daily_new as (
select
cast(first_terms_granted_at as date) as date_day,
count(distinct user_id) as new_terms_members
from terms_members
group by 1
)
select
date_day,
new_terms_members,
sum(new_terms_members) over (order by date_day) as total_terms_members
from filled;
主要メトリクス
| 列 | 意味 | 集計の仕方 |
|---|---|---|
new_terms_members |
その日に 初めて terms 同意した人数 | 日次 SUM |
total_terms_members |
累計 terms 同意会員数 | 日次 MAX(累積のスナップショット) |
total_terms_members_pct_vs_previous_day |
累計の前日比 | 日次で 1 値 |
date spine により 活動ゼロの日も行が存在 します。
fct_membership_conversion_daily
business question: 「無料会員から Premium 有料会員への転換はどう進んでいるか」
入力は int_member_status_current から signed_up_date と first_paid_date を取ります。
select
date_day,
new_terms_members,
new_paid_members,
total_terms_members,
total_paid_members,
safe_ratio(total_paid_members, total_terms_members) as paid_member_conversion_rate,
safe_ratio(new_paid_members, new_terms_members) as same_day_paid_conversion_rate
from cumulative;
terms 同意"] --> B["int_member_status_current
signed_up / first_paid"] C["stg_supabase__entitlements"] --> B D["stg_supabase__premium_purchases"] --> B B --> E["fct_membership_conversion_daily"] A --> F["fct_membership_daily"]
2 つの転換率の違い
| 列 | 分子 / 分母 | 解釈 |
|---|---|---|
paid_member_conversion_rate |
累計有料 / 累計 terms 会員 | 全体 の有料化率(ストック) |
same_day_paid_conversion_rate |
同日有料化 / 同日新規 terms | その日 signup した人のうち同日課金した割合 |
same_day_paid_conversion_rate は会議で使いやすい一方、上記の caveats(分母の小ささ、calendar date 基準、後日課金の非反映、safe_ratio による NULL)を必ず添えて説明します。
int_member_status_current との関係
Membership 日次 mart は consents 起点でも、転換 mart は 中間の会員ステータス を参照します。
Stage 4 で member_status(free / premium / former_premium)を詳述しますが、ここでは 日付 2 列だけ が橋渡し役です。
| 列 | 由来 |
|---|---|
signed_up_date |
terms 同意の初日 |
first_paid_date |
premium_member の初回 paid 購入日 |
よくある誤解
| 誤解 | 実際 |
|---|---|
| 「Auth 行数 = 会員数」 | analytics 境界・PII のため proxy を選ぶ |
| 「累計は永久に増え続ける」 | ソース側削除で次回 build 後に累計から外れうる |
| 「同日転換率 = 全体有料化率」 | 分子分母の定義が異なる別指標 |
体系コラム:カリキュラム上の位置づけ
| 項目 | 内容 |
|---|---|
| Stage / 章 | Stage 3 — 第3章 |
| 今回の論点 | terms proxy、date spine、累積転換率、同日転換の限界 |
| 前章との接続 | Mart 設計 |
| 次章への伏線 | Active User mart — 会員フィルタしない DAU |
まとめ
会員数は「数える」前に 何を会員と呼ぶか が決まります。 監査可能な proxy 列を選び、date spine で日次推移を埋め、累積列と転換率列を分けて定義します。
DS Playground では terms 同意を proxy とし、YAML と overview の caveats で限界を明示することで、会議での誤解を防いでいます。
関連教材
- 前章: Mart 設計
- 次章: Active User mart
- 関連(DM): ER と SSOT マスタ
- 関連(Analytics): 率と KPI
確認問題
問題 1
累計会員数のような指標で、監査可能な proxy 列(例: 明示的な登録日・同意日)を使う主な理由はどれですか。
A. SQL が短くなるから
B. 指標の母集団境界を説明可能にし、定義のブレを減らすため
C. 有料会員だけを数えたいから
D. BI ツールが Auth テーブルを読めないから
正解: B
問題 2
「その日新規登録者のうち、同日に初回購入した割合」のような指標について 正しい 説明はどれですか。
A. 累計購入者 ÷ 累計登録者
B. その日新規登録者を分母に、同日初回購入者の割合
C. 全期間の購入回数 ÷ DAU
D. 常に 100% を超えうる
正解: B
問題 3
source 側で行が物理削除される運用のとき、累計 mart への影響として最も近いのはどれですか。
A. 累計は永久に増え続ける
B. 次回 dbt build 後、累計からその行が外れうる
C. 日次新規のみ変わり累計は不変
D. mart が自動的に OLTP と同期する
正解: B
用語メモ(この章)
| 用語 | 意味(この章での使い方) |
|---|---|
| proxy | 直接取れない指標の代わりに使う監査可能な列 |
| date spine | 全日付を生成し欠損日を埋めるテーブル |
| ストック型転換率 | 累計有料 ÷ 累計会員 |
| フロー型転換率 | 同日有料化 ÷ 同日新規 |
safe_ratio |
分母 0 を NULL にする比率マクロ |
| caveats | proxy 定義の限界・但し書き |