定義をコードで固定し指標を数え直す
目標
「モアノート」の16週間分の製品記録から、DAU・WAU・MAU・スティッキネス・週次共有ユーザー・7日アクティベーション率を、定義どおりに計算する関数を作り、定義が変わるときに数字がどれだけ揺れるかを測ります。
なぜ重要なのか
指標は、定義が書かれているときだけ数字です。社内アカウント・タイムゾーン・重複・アクティブの基準のうち、1つだけが違っても、同じ記録からまったく違うトレンドが出ます。起業の初期には、この数字で投資と採用を決めるので、定義をコードで固定し、計算し直して同じ値が出るようにする習慣が、製品判断の土台です。
用意するものと定義
/opt/fixtures/founder/product/users.csv:user_id,signup_at,is_internal(時刻はUTCで、末尾にZ)/opt/fixtures/founder/product/events.csv:user_id,ts,event(event: session_start・create_doc・share_doc・invite_sent・upgrade)- 事業のタイムゾーンはAsia/Seoul(UTC+9)です。日付はソウルの日付、週はソウル時刻の月曜00:00開始で、0週目は2026-03-02です(0–15週目)。
- 社内アカウント(is_internal=1)は、すべての指標から除きます。 アクティブ = その期間に
session_startが1回以上。人数で数えます。 - すべての関数は、最初の引数としてデータのディレクトリへのパスを受け取ります(採点ツールが別のデータでも呼び出します)。日付の引数は、
"2026-06-14"のような文字列です。
ステップ
/root/founder/metrics/summary.jsonにデータの概要を書く:users(全ユーザー数)、internal_users、events(全イベントの行数)、event_counts(イベント種別ごとの行数のオブジェクト)。/root/founder/metrics/metrics.pyに、dau(fix, day)を作る: ソウルの日付dayにsession_startがある外部ユーザー数(整数)。/root/founder/metrics/internal.jsonに、2026-06-14のdau_all(社内アカウントを含む)、dau_external(除く)、inflation_pct(= (dau_all − dau_external) / dau_external × 100、小数第2位で四捨五入)を書く。metrics.pyに、wau(fix, day)・mau(fix, day)を加える:dayを含めてさかのぼった7日・28日の間の、アクティブな外部ユーザー数。metrics.pyに、stickiness(fix, day)を加える:dayまでの28日間のDAUの平均 ÷mau(fix, day)、小数第4位で四捨五入。/root/founder/metrics/definitions.jsonに、2026-06-14を基準にしたWAUを、3つの定義で書く:session(session_start)、any_event(イベントの種類を問わない)、core(share_doc)。すべて外部ユーザー、人数。metrics.pyに、weekly_sharers(fix)を加える: 0–15週目それぞれの「その週にshare_docを行った外部ユーザー数」を、{"0": n, "1": n, ...}のように、文字列の週キーの辞書で返す。metrics.pyにactivation(fix)を加え(登録週が0–11の外部ユーザーのうち、登録時刻から7×24時間以内にcreate_docを行った割合 →{"signups": n, "activated": m, "rate": 0.0000})、/root/founder/metrics/report.jsonに、2026-06-14のdau・wau・mau・stickiness、activation_rate、north_star_peak_week(週次共有ユーザーが最も多い週、整数)、north_star_week15(15週目の値)をまとめて書く。
参考
- ソウルの日付:
datetime.strptime(ts, "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=timezone.utc).astimezone(ZoneInfo("Asia/Seoul")).date() - 週:
(서울시각 - datetime(2026, 3, 2, tzinfo=서울)).days // 7(プレースホルダーはソウル時刻と、ソウルのタイムゾーンです) - よくある間違い: UTCの日付でまとめること、イベント数を人数として書くこと、社内アカウントを除かないこと、30日やカレンダーの月でMAUを数えること、スティッキネスを1日分のDAUで割ることです。
- 標準ライブラリ(csv・datetime・zoneinfo・collections)だけで十分です。sqlite3 CLIで確認してみてもよいでしょう。
データを先に数える
/root/founder/metrics/summary.jsonに、users・internal_users・events・event_countsを書く。
csv.DictReaderで2つのファイルを読み、collections.Counterでイベントの種類を数えれば済みます。is_internalは、文字列の"1"です。
DAU: ソウルの日付、外部ユーザー、人数
/root/founder/metrics/metrics.pyに、dau(fix, day)を作る。ソウルの日付dayにsession_startがある外部ユーザー数。
UTC時刻をAsia/Seoulに変えてから、.date()でまとめ、ユーザーidをsetに集めて、サイズを数えます。社内アカウントは、users.csvのis_internalで除きます。
社内アカウントが指標をどれだけ膨らませるか
/root/founder/metrics/internal.jsonに、2026-06-14のdau_all・dau_external・inflation_pctを書く。
同じ日・同じ定義(session_start)で、社内アカウントを含むかどうかだけを変えて、2回数えます。比率の分母は、外部ユーザーのDAUです。
WAU・MAU: 基準日を含む7日・28日
metrics.pyに、wau(fix, day)・mau(fix, day)を加える。dayを含めてさかのぼった7日・28日の間の、アクティブな外部ユーザー数。
日付ごとのアクティブな集合を作っておき、day、day-1、…、day-6(または-27)の和集合のサイズを数えます。カレンダーの月や30日ではありません。
スティッキネス: 28日平均のDAU ÷ MAU
metrics.pyに、stickiness(fix, day)を加える。dayまでの28日間のDAUの平均を、mau(fix, day)で割り、小数第4位で四捨五入。
その日1日のDAUで割ると、曜日によって揺れます。28日それぞれのDAUを足して28で割った平均を使います。
アクティブの定義だけを変えて、同じ窓を数える
/root/founder/metrics/definitions.jsonに、2026-06-14を基準にした7日の窓の外部ユーザー数を、session・any_event・core(share_doc)の3つの定義で書く。
窓とユーザーのフィルターはそのままにして、数えるイベントの種類だけを変えます。any_eventは、イベントの種類を問いません。
North Starの候補: 週次共有ユーザー
metrics.pyに、weekly_sharers(fix)を加える。0–15週目それぞれの、share_docの外部ユーザー数を、文字列の週キーの辞書で返す。
週は、(ソウル時刻 − 2026-03-02 00:00ソウル).days // 7です。イベントがない週も0として、16個のキーをすべて埋めます。
アクティベーション率と、1枚のレポート
metrics.pyにactivation(fix)を加え、/root/founder/metrics/report.jsonに、dau・wau・mau・stickiness・activation_rate・north_star_peak_week・north_star_week15をまとめる。
アクティベーションは、カレンダーの日付ではなく、登録時刻から7×24時間(timedelta(days=7))以内の最初のcreate_docです。対象は、登録週が0–11の外部ユーザーです。最も多い週が複数なら、前の週を選びます。