継続率(Retention)はプロダクトの健康度を最も雄弁に語る指標です。「新規 1000 人のうち翌日 500 人、7 日後 200 人、30 日後 50 人」── この数字が改善できるかどうかが事業の勝敗を分けます。本記事では、Cohort(同期入会群) で見る作法と、/ での実装を扱います。
Cohort 分析とは
「同じ時期に入ったユーザー群」を一塊(cohort)として追跡する分析。横軸を経過日数、縦軸を入会日にすると、改善打ち手が「どの cohort から効き始めたか」が一目で見えます。
BigQuery での Cohort 計算
WITH first_active AS ( -- 各ユーザーの初回 active 日(cohort 定義) SELECT user_id, DATE_TRUNC(MIN(active_date), WEEK(MONDAY)) AS cohort_week, MIN(active_date) AS first_date FROM `project.dataset.active_event` GROUP BY user_id),activity AS ( SELECT user_id, active_date FROM `project.dataset.active_event`)SELECT fa.cohort_week, DATE_DIFF(a.active_date, fa.first_date, DAY) AS days_since_join, COUNT(DISTINCT a.user_id) AS active_users, COUNT(DISTINCT a.user_id) * 1.0 / (SELECT COUNT(DISTINCT user_id) FROM first_active WHERE cohort_week = fa.cohort_week) AS retention_rateFROM first_active faJOIN activity a USING(user_id)WHERE DATE_DIFF(a.active_date, fa.first_date, DAY) BETWEEN 0 AND 30GROUP BY 1, 2ORDER BY 1, 2;業界別ベンチマーク(D1/D7/D30)
| 業種 | D1 | D7 | D30 | コメント |
|---|---|---|---|---|
| 消費系アプリ | 40% | 20% | 10% | Sequoia の有名な目安 |
| メッセージング (LINE 等) | 70% | 55% | 45% | 高頻度利用が前提 |
| B2B | 60%(営業日) | 50% | 40% | 土日除外で見ること |
| ソシャゲ | 30〜50% | 10〜25% | 3〜10% | ジャンル差大 |
| EC | 10% | 5% | 3% | 頻度が低いので低く見える |
残存曲線(Survival Curve)
Cohort ヒートマップは詳細を見るのに良いが、経営層への説明は曲線 1 本が刺さる。横軸=経過日数、縦軸=継続率で、複数 cohort を重ねて表示。改善前 cohort と改善後 cohort の曲線が分離していれば打ち手成功。
ありがちな読み間違い
- 全期間平均で見る:直近の改善が古い cohort に薄まって見えなくなる → 必ず cohort 別で見る
- cohort 期間が短すぎる:D1 は出るが D30 は cohort が育っていない期間がある → 30 日経過した cohort のみで比較
- 「全 cohort の D30 平均」を出す:意味なし。最新 4 cohort の D30 中央値等、固定窓で比較
Pandas での Cohort 分析
BigQuery でなくても、 と pandas で Cohort 分析は可能。100 万行までは pandas で十分速い。
import pandas as pdimport numpy as npimport matplotlib.pyplot as pltimport seaborn as snsimport japanize_matplotlib # noqa: F401
# 1. データ読み込み(user_id, active_date の 2 列)df = pd.read_csv("active_events.csv", parse_dates=["active_date"])
# 2. 各ユーザーの初回 active 日を計算 → cohort 週df["first_date"] = df.groupby("user_id")["active_date"].transform("min")df["cohort_week"] = df["first_date"].dt.to_period("W").dt.start_timedf["days_since_join"] = (df["active_date"] - df["first_date"]).dt.days
# 3. cohort × 経過日数の継続率テーブルcohort_size = df.groupby("cohort_week")["user_id"].nunique()retention = ( df.groupby(["cohort_week", "days_since_join"])["user_id"] .nunique() .reset_index() .pivot(index="cohort_week", columns="days_since_join", values="user_id"))retention_pct = retention.div(cohort_size, axis=0)
print(retention_pct.iloc[:5, :8]) # 上位 5 cohort × 0〜7 日# 4. ヒートマップfig, ax = plt.subplots(figsize=(14, 6))sns.heatmap( retention_pct.iloc[:, :30], # 30 日分 annot=True, fmt=".0%", cmap="YlGnBu", vmin=0, vmax=0.5, cbar_kws={"label": "継続率"}, ax=ax,)ax.set_title("週次 cohort × 経過日数の継続率")ax.set_xlabel("経過日数")ax.set_ylabel("入会週")plt.tight_layout()plt.savefig("cohort_heatmap.png", dpi=120)# 5. 残存曲線:改善前後 cohort を重ねて表示before_cohorts = retention_pct.loc[ (retention_pct.index >= "2026-01-01") & (retention_pct.index < "2026-04-01")].mean()after_cohorts = retention_pct.loc[retention_pct.index >= "2026-04-01"].mean()
fig, ax = plt.subplots(figsize=(11, 5))ax.plot(before_cohorts.index, before_cohorts.values, label="改善前 (Q1)", linewidth=2)ax.plot(after_cohorts.index, after_cohorts.values, label="改善後 (Q2)", linewidth=2, color="#eb5d32")ax.set_xlabel("経過日数"); ax.set_ylabel("継続率")ax.set_title("プロダクト改善打ち手の効果(残存曲線)")ax.legend(); ax.grid(alpha=0.3)ax.set_ylim(0, 0.5)plt.tight_layout(); plt.show()ふくふくの進め方
「継続率の改善打ち手の効果が見えない」というご相談には、Cohort 分析の 実装(1 週間)→ ダッシュボード化 → 改善 運用を 1 ヶ月で。 EP.16 の PDCA サイクルとセットで運用すると、施策の効果がはっきり見えます。
次回予告
EP.05 は N+1 日継続率 vs ローリング継続率。「翌日継続」と「過去 7 日のうち 1 日でも戻ってきた率」では数字が大きく変わる罠。
この記事の感想を教えてください
あなたの 1 クリックで、本当にこの記事は更新されます。「もっと詳しく」「続編希望」が一定数集まった記事は、 ふくふくが 実際に内容を拡充したり続編記事を公開 します。 送信したリアクションはお使いのブラウザに記録され、再カウントされません。