🎓 レベル:標準 | 重要度:A(必須)
📎 関連:コホート分析 | 前提:顧客生涯価値(LTV)
要点(BLUF)
- リテンション率(継続率) は「次の期も取引を続ける確率」、チャーン率(解約率) は「抜ける確率」で、同じ期間なら 。期間を揃えないと取り違えます。
- 生存関数 (獲得時 )から、期待生涯は 。継続率一定なら で、顧客生涯価値(LTV) の前提そのものです。
- 現実の継続率は一定ではなく、集団で観測すると時間とともに上昇します。個々の churn が一定でも、高 churn 層が先に抜けて生き残りが低 churn 層に偏る(生存者バイアス)ためです。一定 churn とみなすと生涯を大きく誤ります。
1. リテンションとチャーン:定義を揃える
リテンション率(継続率) は、ある期に取引していた顧客が次の期も続ける確率です。チャーン率(解約率) はその逆で、抜ける確率。同じ期間で定義すれば、両者は表裏一体です。
ここで肝心なのは「同じ期間」で揃えること。月次の継続率と年次のチャーン率を引き算しても意味がありません(月次 churn 5% は年次では )。
獲得直後を第0期として、顧客が第 期まで生き残っている確率を生存関数 と呼びます。各期の継続率 を掛け合わせたものです。
顧客生涯価値(LTV) で使った「期待継続期間」は、この生存関数を足し上げたものでした(獲得した第0期から数えます)。
継続率が一定 なら となり、。これが 02-01 の の正体です。問題は「継続率一定」が現実には成り立たないこと——次節でその理由を見ます。
2. なぜ継続率は一定でないのか:顧客の異質性
実データのリテンション率は、たいてい時間とともに上昇します(リテンション曲線が寝てくる、逓減がゆるくなる)。これは「だんだん顧客が定着した」とは限りません。顧客の異質性(heterogeneity)= churn の高い人と低い人が混在しているだけで、自然に起きます。
直感はこうです。獲得した集団に、すぐ離れる気まぐれ層(高 churn)と、長く続く忠実層(低 churn)が混ざっているとします。時間が経つと、高 churn の気まぐれ層は先にごっそり抜け、残るのは低 churn の忠実層に偏っていきます。すると、生き残った集団で測ったリテンション率は、忠実層の高い継続率へと近づいていく——これが生存者バイアスです。個々人の churn は一定でも、集団の観測値は上昇します。
flowchart TD Start["獲得時:忠実層60% + 気まぐれ層40%"] --> T1["時間が経つ"] T1 --> Leave["気まぐれ層(高churn)が先に抜ける"] T1 --> Stay["忠実層(低churn)が生き残る"] Leave --> Mix["生存者は低churn層に偏る"] Stay --> Mix Mix --> Rise["集団の観測リテンション率は上昇(曲線が寝る)"]
これは 顧客生涯価値(LTV) の「継続率一定」仮定の限界そのものです。観測された初期リテンションをそのまま一定とみなすと、生涯を大きく見誤ります。次節で数値にして確かめます。
3. 2セグメント混合で確かめる(コード)
概念:忠実層 A と気まぐれ層 B の2セグメント混合を考えます。各セグメントの churn は一定ですが、混ざると集団リテンションは一定になりません。
数式:重み ()、各セグメントの継続率 とすると、集団の生存関数は2つの幾何級数の重み付き和です。
年齢 の集団で観測されるリテンション率は、隣り合う生存確率の比です。
では高 churn 層 B が消え、 は忠実層の へ近づきます。一方、真の期待生涯はセグメントごとの を重みで足したものです。
次のコードで、、 として、観測リテンション率の表・真の期待生涯・素朴推定を出します。
import numpy as np
import pandas as pd
# 2セグメント混合:忠実層A(継続率0.95)と気まぐれ層B(継続率0.60)
w_A, r_A = 0.6, 0.95 # 忠実層:重み0.6、継続率0.95(churn 0.05)
w_B, r_B = 0.4, 0.60 # 気まぐれ層:重み0.4、継続率0.60(churn 0.40)
def S(t):
"""獲得時を母数とした年齢tの集団生存率 S(t)=w_A r_A^t + w_B r_B^t、S(0)=1"""
return w_A * r_A**t + w_B * r_B**t
# age=0..9 で観測される集団リテンション率 = S(t+1)/S(t)
ages = np.arange(0, 10)
rows = []
for t in ages:
rows.append({"年齢age": int(t),
"生存率S(t)": S(t),
"観測リテンション率": S(t + 1) / S(t)})
tbl = pd.DataFrame(rows)
print("=== 集団で観測されるリテンション率(年齢ageごと)===")
print(tbl.to_string(index=False, formatters={
"生存率S(t)": "{:.4f}".format,
"観測リテンション率": "{:.4f}".format}))
# 真の期待生涯 = Σ S(t) = w_A/(1-r_A) + w_B/(1-r_B)
true_life = w_A / (1 - r_A) + w_B / (1 - r_B)
T = 2000
life_num = np.sum(S(np.arange(T + 1))) # 数値和でも確認
# 素朴推定:初期(age=0)の観測リテンション r0=0.81 を一定とみなす → 1/(1-r0)
r0 = S(1) / S(0)
naive_life = 1 / (1 - r0)
print(f"\n初期age=0の観測リテンション率 r0 = {r0:.4f}")
print(f"真の期待生涯 Σ S(t)(閉形式) = {true_life:.4f} 期")
print(f"真の期待生涯 Σ S(t)(数値和) = {life_num:.4f} 期")
print(f"素朴推定 1/(1-r0)(churn一定) = {naive_life:.4f} 期")
print(f"→ 一定churn仮定は期待生涯を {true_life - naive_life:.2f} 期ぶん過小評価")
出力:
=== 集団で観測されるリテンション率(年齢ageごと)===
年齢age 生存率S(t) 観測リテンション率
0 1.0000 0.8100
1 0.8100 0.8463
2 0.6855 0.8765
3 0.6008 0.8997
4 0.5405 0.9164
5 0.4954 0.9280
6 0.4597 0.9358
7 0.4302 0.9409
8 0.4048 0.9442
9 0.3822 0.9463
初期age=0の観測リテンション率 r0 = 0.8100
真の期待生涯 Σ S(t)(閉形式) = 13.0000 期
真の期待生涯 Σ S(t)(数値和) = 13.0000 期
素朴推定 1/(1-r0)(churn一定) = 5.2632 期
→ 一定churn仮定は期待生涯を 7.74 期ぶん過小評価
出力の意味:観測リテンション率は、年齢0の 0.8100 から、年齢9で 0.9463 へと単調に上昇しています。施策で定着率が上がったわけではありません——コードの churn はセグメントごとに一定です。高 churn の気まぐれ層が先に抜け、生存者が忠実層()へ偏るので、観測値が へ近づくだけ(生存者バイアス)。一方、真の期待生涯は 13.00 期(閉形式・数値和とも一致)。ところが、観測された初期リテンション 0.81 を「ずっと一定」とみなして で見積もると、わずか 5.26 期——真値を 7.74 期も過小評価します。02-01 の LTV はこの期待生涯に粗利 を掛けて出すので、生涯を半分以下に見誤れば LTV も半分以下に潰れる。「観測した1点のリテンションを一定とみなす」のは危険だと数字が示しています。
リテンション曲線が寝ていく様子と、生存が2層の和であることを図にすると、生存者バイアスの直感がつかめます。
import numpy as np
import matplotlib.pyplot as plt
import japanize_matplotlib # noqa: F401 日本語ラベル
w_A, r_A = 0.6, 0.95
w_B, r_B = 0.4, 0.60
def S(t):
return w_A * r_A**t + w_B * r_B**t
ages = np.arange(0, 13)
surv = S(ages)
comp_A = w_A * r_A**ages # 忠実層の寄与
comp_B = w_B * r_B**ages # 気まぐれ層の寄与
obs_ret = S(ages + 1) / S(ages) # 各ageで観測される集団リテンション率
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(11, 4.2))
# 左:集団生存 S(t) を2セグメントに分解
ax1.plot(ages, surv, "o-", color="black", label="集団生存 S(t)")
ax1.plot(ages, comp_A, "s--", color="tab:blue", label="忠実層の寄与 0.6·0.95^t")
ax1.plot(ages, comp_B, "^--", color="tab:red", label="気まぐれ層の寄与 0.4·0.60^t")
ax1.set_xlabel("年齢 age(経過期間)")
ax1.set_ylabel("生存率")
ax1.set_title("生存は2層の和:気まぐれ層が先に消える")
ax1.legend()
ax1.grid(alpha=0.3)
# 右:観測される集団リテンション率は上昇していく
ax2.plot(ages, obs_ret, "o-", color="tab:green", label="観測リテンション率 S(t+1)/S(t)")
ax2.axhline(r_A, ls=":", color="tab:blue", label=f"忠実層の継続率 {r_A}")
ax2.axhline(obs_ret[0], ls=":", color="gray", label=f"初期リテンション {obs_ret[0]:.2f}(素朴に一定とみなす)")
ax2.set_xlabel("年齢 age(経過期間)")
ax2.set_ylabel("観測リテンション率")
ax2.set_title("生存者バイアスでリテンション率は上昇")
ax2.set_ylim(0.5, 1.0)
ax2.legend()
ax2.grid(alpha=0.3)
fig.tight_layout()
plt.show()
左図は、集団生存 が忠実層(青)と気まぐれ層(赤)の和であること、そして気まぐれ層が早々にゼロへ消えることを示します。右図は、観測リテンション率が初期 0.81 から忠実層の 0.95 へ向かって寝ていく様子。初期リテンション 0.81 を水平に伸ばす素朴な見方(灰点線)が、いかに集団の実態とズレるかが一目で分かります。
⚠️ よくある誤解
- リテンションとチャーンの取り違え: は同じ期間で揃えたときだけ成り立ちます。月次継続率と年次チャーンを混ぜない。期間を変換するなら を経由します。
- リテンション率の上昇=施策の成功、とは限らない:本ノートのとおり、churn が一定でも**生存者バイアス(顧客の異質性)**だけで集団リテンションは上昇します。改善を主張するなら、同一コホートで時系列を追う(コホート分析)か、実験で因果を切り分けます。
- 一定 churn 仮定は LTV を誤る:初期リテンションを一定とみなすと、期待生涯(13 対 5.26)を通じて LTV を大きく外します。継続率は age とともに動くものとして、リテンション曲線で見ましょう。
- churn 率は分母の取り方で変わる:期初顧客数を分母にするか、期中平均を分母にするか、新規を含めるか除くかで、同じ事象でもチャーン率の数字は動きます。社内・社外で比べるときは定義を明示すること。
関連ノート
- 顧客生涯価値(LTV)(一定継続率 の仮定を、本ノートで精緻化する)
- コホート分析(同じバッチで作成・リテンションを獲得時期ごとに観察する)
- 第2章 顧客価値の測定 目次
- churn の異質性を確率分布で扱う階層ベイズはベイズテキストへ/打ち切りを含む生存時間解析(カプラン・マイヤー法など)は統計テキストへ
- マーケティング・サイエンス 全体目次