青の統計学-DS Playground-

semi-structured データ:multi-select を mart に載せる

Stage 4 — 第3章 | dbt入門カリキュラム 推定学習時間:35〜45分 | 難易度:★★★☆☆


この章で学ぶこと

前章 では、Engagement mart で primary_goal をセグメント軸として使いました。 有料化前のプロダクトでは、「ユーザーは何を学びたいか」「Premium に何を期待するか」 を定性的に把握することが重要です。

Demand 分析には 2 種類のデータが混在します。

現在値(current-state) … ユーザー設定や preferences。1 ユーザー 1 値。
イベント(event) … アンケート回答。1 回答に複数選択肢(multi-select)があり得る。

これらを同じ grain で JOIN すると、分母がずれ、share の解釈が壊れます。 dbt では 問いごとに mart を分け、multi-select は UNNEST で行に広げてから集計するのが典型パターンです。

まず dbt の作法を整理し、続けて DS Playground の fct_learning_goal_sharefct_member_survey_option_share を読み解きます。

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

  • current-state preferences と survey 回答の データモデル差 を説明できる
  • UNNEST(selected_options) パターンを読める
  • response_shareselected_option_share の分母の違いを説明できる
  • pre-monetization research exposure との接続を理解できる

Demand 分析

Demand ファミリーは 「何が欲しいか」 に答える mart 群です。 Membership / Activity / Engagement とは grain が異なります。

問いの型 データ性質 典型 grain
今の設定・嗜好は current-state 1 行 = 1 カテゴリ値(集計済み)
調査への回答は イベント(multi-select 可) 1 行 = survey × 選択肢
行動から推測 別 mart(本章外)

同じ「学習目的」という単語でも、Learning Goal(現在の preferences)Survey Response(イベント) はエンティティが違います。 オントロジー で学んだ「同じ単語でもエンティティが違う」典型例です。

flowchart LR TERM["同じ用語
学習目的"] CURRENT["Current-state entity
現在の設定・嗜好"] EVENT["Survey Response entity
回答イベント"] PREF["user preferences"] G["goal share mart"] SUR["survey responses"] U["UNNEST
option share mart"] TERM -.->|"現在値として扱う"| CURRENT --> PREF --> G TERM -.->|"イベントとして扱う"| EVENT --> SUR --> U classDef term fill:#fff7ed,stroke:#ea580c,color:#1f2937 classDef current fill:#eff6ff,stroke:#2563eb,color:#1f2937 classDef event fill:#f0fdf4,stroke:#16a34a,color:#1f2937 class TERM term class CURRENT,PREF,G current class EVENT,SUR,U event

share 列を出すとき、分母を列名で固定 することが重要です。 率と KPI で学んだ「分母の固定」を、mart 列レベルで実装します。


multi-select の展開パターン

アンケートで複数選択可の場合、1 回答行に配列(例: selected_options)が載ります。 集計するには 1 選択肢 1 行 に広げる必要があります。

PostgreSQL では unnest(selected_options) を使います。

with responses as (
            select
              response_id,
              user_id,
              survey_key,
              unnest(selected_options) as selected_option
            from stg_survey_responses
          ),

          option_counts as (
            select
              survey_key,
              selected_option,
              count(distinct response_id) as responses
            from responses
            group by 1, 2
          )
          

1 回答で A, B を選択すると 2 行に広がります。 このとき share には 2 種類 定義できます。

分母 解釈
response_share その survey の 回答数(response_id) 「回答の何 % がこの選択肢を含むか」
selected_option_share その survey の 選択肢総数 multi-select 全体における選択肢の割合

分母が違えば数字の意味も違います。YAML で business question と列定義を固定してください。


current-state goal share の caveats

preferences から goal 構成比を出す mart は current-state のみ です。 過去に goal を変更した履歴はこの mart では見えません。履歴化が必要なら snapshot(Stage 5)が教学例です。

preferences 未設定ユーザーは staging の NULL 処理に依存し、分布 mart では unset と対応します。

survey を preferences と単純 JOIN しない理由も同じです。 grain が違う 別問い として扱います。


Survey mart ファミリー

DS Playground では Demand ファミリーに 2 モデルがあります。

  • fct_learning_goal_share — 現在の primary_goal 構成
  • fct_member_survey_option_share — 調査ごとの選択肢シェア
flowchart LR LP["stg_supabase__user_learning_preferences"] --> G["fct_learning_goal_share"] MS["stg_supabase__member_survey_responses"] --> S["fct_member_survey_option_share"] G --> EXP["metabase_pre_monetization_research_dashboard"] S --> EXP

fct_learning_goal_share

business question: 「会員の学習目的構成比はどうなっているか」

grain: 1 行 = 1 primary_goal 値

with goal_counts as (
            select primary_goal, count(*) as users
            from stg_supabase__user_learning_preferences
            group by 1
          )
          select
            primary_goal,
            users,
            safe_ratio(users, total_users) as user_share
          from goal_counts;
          

fct_member_survey_option_share

business question: 「各 survey で、選択肢はどれだけ選ばれているか」

grain: 1 行 = survey_key × selected_option

DS Playground では unnest(selected_options) で展開し、singular test assert_member_survey_selected_options_non_empty が空配列を防ぎます。

exposure との接続

metabase_pre_monetization_research_dashboard depends_on:

  • fct_learning_goal_share
  • fct_member_survey_option_share
  • fct_problem_quality(次章)
  • fct_problem_category_quality

Survey mart 単体では 教材品質 まで見ません。 exposure が 研究ダッシュボードの SSOT セット を定義します。

分析上の注意

落とし穴 対策
goal の履歴変化を見たい snapshot または将来の event 履歴
survey を preferences と単純 JOIN grain が違う — 別問いとして扱う
選択肢 share を CVR と混同 business question を YAML で固定

体系コラム:カリキュラム上の位置づけ

項目 内容
Stage / 章 Stage 4 — 第3章
今回の論点 current goal vs multi-select、unnest、2 種 share
前章との接続 Engagement martprimary_goal
次章への伏線 Content quality mart

まとめ

Demand 分析は「アンケートを読む」ことではなく、定義済み grain の share 表 として SSOT 化することです。 DS Playground では current-state の goal share と、multi-select を unnest した survey share を分け、safe_ratio で分母を列名に固定しています。


関連教材


確認問題

問題 1

カテゴリ別シェア mart(例: 1 行 = 1 カテゴリ値)の grain として正しいのはどれですか。

A. 1 行 = 1 ユーザー
B. 1 行 = 1 カテゴリ値(集計済み)
C. 1 行 = 1 アンケート回答
D. 1 行 = 1 日

正解: B


問題 2

multi-select アンケートで unnest(selected_options) を使う理由はどれですか。

A. PII を削除するため
B. 1 回答の複数選択肢を集計可能な行に広げるため
C. incremental build するため
D. OLTP と JOIN するため

正解: B


問題 3

選択肢シェア(option share)の分母として 一般的に正しい のはどれですか。

A. 全登録ユーザー数
B. そのアンケート回答における選択肢総数(multi-select 合計)
C. DAU
D. seed 行数

正解: B


用語メモ(この章)

用語 意味(この章での使い方)
current-state 最新の preferences 値。履歴は見えない
multi-select 1 回答で複数選択肢を選べる形式
unnest 配列を行に広げる PostgreSQL 関数
response_share 回答数を分母にした share
selected_option_share 選択肢総数を分母にした share