青の統計学-DS Playground-

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_dailyfct_membership_conversion_daily を読み解きます。

この章を終えると、こんなことができるようになります:

  • terms 同意 proxy を選んだ理由と限界を説明できる
  • date spine + 累積和で日次会員推移 mart を読める
  • paid_member_conversion_ratesame_day_paid_conversion_rate の違いを説明できる
  • safe_ratio マクロが比率列で使われていることを認識できる

なぜ proxy が必要か

KPI の母集団は、必ずしも OLTP の「正本テーブル」と一致しません。

よくある状況 問題
Auth / ユーザー管理テーブル analytics から直接読むと PII・権限境界を越える
初回ログイン パッシブ行動であり、activity SSOT と定義が不一致
明示的な同意・登録イベント アプリ設計上 signup と一体。監査可能

指標定義 で学ぶ「率の分子分母を会議前に固定する」原則は、何を会員と呼ぶか の選択にもそのまま当てはまります。 データマネジメント入門 で学んだ「必要最小の参照」も、モデル設計に現れます。

proxy を選ぶときは 3 点を YAML に書きます。

  1. なぜこの列か(他の選択肢を却下した理由)
  2. 含まれないユーザーは誰か(同意記録欠落など)
  3. ソース側の削除・修正で数字がどう変わるか

日次会員推移 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 つです。

  1. date spine … 活動ゼロの日も行が存在する。期間比較 と同様、欠損日を埋める設計
  2. 累積列total_members は日次 MAX(累積のスナップショット)として扱う
flowchart LR STG["staging
同意・登録イベント"] 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 が必ず付きます。

  1. 分母が小さい日はブレやすい … 新規 3 人で 1 人課金 → 33%
  2. 「同日」は calendar date ベース … 23:50 登録・翌 00:10 課金は同日転換に含まれない
  3. 後日課金は別日に反映 … 7 日後課金は new_paid_members の別日に載る
  4. 分母 0 は NULLsafe_ratio 等で 0 除算を防ぐ

率と KPI で学んだ率の注意点が、そのまま列名と caveats に現れます。


terms 同意 proxy

DS Playground では、Supabase Auth を analytics から直接読まず、terms 同意を会員数 proxy として使います。

stg_supabase__consentsconsent_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_datefirst_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;
          
flowchart TD A["stg_supabase__consents
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 で限界を明示することで、会議での誤解を防いでいます。


関連教材


確認問題

問題 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 定義の限界・但し書き