📖 本篇简介:本篇是案例系列的第十篇,用一份模拟的游戏运营数据(DAU 表 + 关卡表 + 版本表)走完游戏运营分析的完整链路:DAU/MAU 粘性指标 → 付费率、ARPPU、ARPU 三个收入口径辨析 → v1.0 与 v1.1 版本对比 → 关卡流失与瓶颈关定位 → 活动前后效果复盘 → 渠道质量对比。前置知识:第 13 章的 groupby 聚合、第 14 章的日期处理、第 15 章的折线图。学完后你能独立产出一份"给运营看的游戏周报"。
游戏运营是 pandas 在互联网行业最典型的落地场景之一。它的特殊性在于:指标口径极多且极易算混——DAU 和 MAU 的关系、付费率和 ARPPU 和 ARPU 到底谁除以谁,错一个分母,结论就完全相反。本篇会把这些口径一次讲清,并用模拟数据带你亲手算一遍。
⚠️ 数据说明:本篇全部数据均为np.random模拟生成(固定种子20250107),模拟数据,结论仅供方法演示,不代表任何真实产品。
目录
1. 案例背景与目标
2. 数据准备:三张模拟表
3. 数据清洗与口径统一
4. DAU 走势与 DAU/MAU 粘性
5. 付费率、ARPPU 与 ARPU:三个口径别算混
6. 版本对比:v1.0 vs v1.1
7. 关卡流失分析:找到瓶颈关
8. 活动效果复盘
9. 渠道质量对比
10. 可视化报告
11. 结论与行动建议
12. 常见坑小结
13. 扩展练习
1. 案例背景与目标
背景:某休闲手游上线 3 个月(2025-01-01 ~ 2025-03-31,共 90 天),经历了一次版本迭代(v1.1 于 2025-02-15 上线)和一次春节活动(2025-01-28 ~ 2025-02-04)。运营团队需要一份复盘,回答四个问题:
1. 用户盘子健不健康:DAU 走势如何?DAU/MAU 粘性是升是降?
2. 赚不赚钱:付费率、ARPPU、ARPU 三个口径分别是多少?版本更新后有没有变好?
3. 玩家卡在哪:50 个关卡里,哪一关流失最严重?是不是难度设计有问题?
4. 活动值不值:春节活动带来了多少 DAU 和收入提升?活动结束后有没有留存?
分析目标:用 pandas 把三张原始表(DAU 表、关卡表、版本表)加工成上面四个问题的答案,并给出可执行的运营建议。
先记住三个收入口径的定义(后面反复用):
- 付费率 = 付费用户数 / 活跃用户数
- ARPPU(每付费用户平均收入)= 付费金额 / 付费用户数
- ARPU(每活跃用户平均收入)= 付费金额 / 活跃用户数
- 三者关系:ARPU = 付费率 × ARPPU(乘起来正好消掉付费用户数)。这个恒等式后面会用来验算。
2. 数据准备:三张模拟表
我们构造三张表:DAU 表(渠道 × 日期)、关卡表、版本信息表。固定随机种子保证可复现。
import pandas as pd
import numpy as np
# 固定随机种子——案例可复现的关键,否则每次跑数字都不一样
np.random.seed(20250107)
# ---------- 2.1 DAU 表(渠道 × 日期,90 天 × 4 渠道 = 360 行) ----------
dates = pd.date_range("2025-01-01", "2025-03-31", freq="D")
channels = ["自然量", "硬核渠道", "应用商店A", "买量渠道B"]
base_active = {"自然量": 1500, "硬核渠道": 1200, "应用商店A": 2000, "买量渠道B": 2500}
base_new = {"自然量": 60, "硬核渠道": 90, "应用商店A": 160, "买量渠道B": 260}
v11_date = pd.Timestamp("2025-02-15") # v1.1 上线日
act_start, act_end = pd.Timestamp("2025-01-28"), pd.Timestamp("2025-02-04") # 春节活动
rows = []
for d in dates:
day_idx = (d - dates[0]).days
trend = 1.0 + 0.0015 * day_idx # 自然缓慢增长
is_v11 = d >= v11_date # 是否处于 v1.1
in_activity = act_start <= d <= act_end # 是否活动期
for ch in channels:
act = base_active[ch] * trend
if is_v11:
act *= 1.15 # v1.1 带来 15% DAU 提升
if in_activity:
act *= 1.40 # 活动带来 40% DAU 提升
weekend = 1.10 if d.dayofweek >= 5 else 1.0 # 周末活跃更高
act = act * weekend * np.random.normal(1.0, 0.04)
act = max(int(round(act)), 100)
new_u = base_new[ch] * trend * weekend * np.random.normal(1.0, 0.10)
new_u = max(int(round(new_u)), 5)
# 付费率:基础 3.5%,v1.1 后 4.5%,活动期 6%
pay_rate = 0.060 if in_activity else (0.045 if is_v11 else 0.035)
pay_rate *= np.random.normal(1.0, 0.08)
pay_users = int(round(act * max(pay_rate, 0.01)))
# ARPPU:基础 90 元,活动期 120,v1.1 后 100
arppu = 120.0 if in_activity else (100.0 if is_v11 else 90.0)
revenue = pay_users * arppu * np.random.normal(1.0, 0.06)
rows.append({
"日期": d, "渠道": ch,
"新增用户": new_u, "活跃用户": act,
"付费用户数": pay_users, "付费金额": round(revenue, 2),
})
dau = pd.DataFrame(rows)
print("DAU 表形状:", dau.shape)
print(dau.head(3).to_string(index=False))
DAU 表形状: (360, 6)
日期 渠道 新增用户 活跃用户 付费用户数 付费金额
2025-01-01 自然量 62 1496 48 4095.48
2025-01-01 硬核渠道 79 1280 50 4328.92
2025-01-01 应用商店A 173 2078 79 7020.28
# ---------- 2.2 关卡表(50 关) ----------
n_level = 50
level_ids = np.arange(1, n_level + 1)
base_pass = np.linspace(0.95, 0.55, n_level) # 通过率随关卡递减
pass_rate = base_pass + np.random.normal(0, 0.02, n_level)
pass_rate[24] = 0.38 # 第 25 关人为设一个明显瓶颈
pass_rate[25] = 0.45
pass_rate = np.clip(pass_rate, 0.2, 0.99)
attempts = (1.0 / pass_rate) * np.random.uniform(1.0, 1.3, n_level) # 越难过越反复试
levels = pd.DataFrame({
"关卡编号": level_ids,
"通过率": pass_rate.round(3),
"平均尝试次数": attempts.round(2),
})
print("关卡表形状:", levels.shape)
print(levels.head(3).to_string(index=False))
关卡表形状: (50, 3)
关卡编号 通过率 平均尝试次数
1 0.982 1.24
2 0.979 1.08
3 0.932 1.32
# ---------- 2.3 版本信息表 ----------
versions = pd.DataFrame({
"版本号": ["v1.0", "v1.1"],
"上线日期": [pd.Timestamp("2025-01-01"), v11_date],
"说明": ["初始版本", "新增活动系统与新手引导优化"],
})
print(versions.to_string(index=False))
版本号 上线日期 说明
v1.0 2025-01-01 初始版本
v1.1 2025-02-15 新增活动系统与新手引导优化
三张表的粒度要先搞清楚:DAU 表是"渠道 × 日期"的聚合日表(不是用户明细),关卡表是"每关一行",版本表是"每版本一行"。后面所有分析都建立在这个粒度认知上——这也是为什么 MAU 不能直接从这张聚合表去重(见第 4 节)。
3. 数据清洗与口径统一
原始数据进来后,先做三件事:按日期汇总到"日粒度"、衍生出收入指标、检查异常值。
# 3.1 把"渠道×日期"汇总成"日"粒度(4 个渠道加总)
daily = dau.groupby("日期", as_index=False).agg(
活跃=("活跃用户", "sum"),
新增=("新增用户", "sum"),
付费用户=("付费用户数", "sum"),
付费金额=("付费金额", "sum"),
)
# 3.2 衍生三个收入口径(注意分母!)
daily["付费率"] = daily["付费用户"] / daily["活跃"] # 分母是活跃
daily["ARPPU"] = daily["付费金额"] / daily["付费用户"] # 分母是付费用户
daily["ARPU"] = daily["付费金额"] / daily["活跃"] # 分母是活跃
# 3.3 验算恒等式:ARPU 应 ≈ 付费率 × ARPPU
check = (daily["付费率"] * daily["ARPPU"] - daily["ARPU"]).abs().max()
print(f"恒等式最大误差: {check:.6f}") # 应接近 0
# 3.4 检查缺失值与负数
print("缺失值:\n", daily.isna().sum())
print("活跃是否有负数:", (daily["活跃"] <= 0).any())
# 日期列转成纯日期便于展示(避免后面 .round 报 datetime 警告)
daily["日期str"] = daily["日期"].dt.date
print(daily[["日期str", "活跃", "新增", "付费用户", "付费金额"]].head(3).to_string(index=False))
恒等式最大误差: 0.000000
缺失值:
日期 0
活跃 0
新增 0
付费用户 0
付费金额 0
付费率 0
ARPPU 0
ARPU 0
dtype: int64
活跃是否有负数: False
日期str 活跃 新增 付费用户 付费金额
2025-01-01 7290 562 271 22928.28
2025-01-02 7164 550 227 21207.09
2025-01-03 7349 548 275 25610.97
⚠️ 常见坑:daily.round(2)在 pandas 3.0 会对 datetime/period 列报UserWarning("obj.round has no effect with datetime")。pandas 3.0 起字符串默认就是str类型、且 CoW 默认开启,链式赋值不再触发SettingWithCopyWarning;但.round()作用在时间列上仍然无意义。稳妥做法是像上面那样先.dt.date转成展示用字符串,再对数值列取整。
4. DAU 走势与 DAU/MAU 粘性
DAU(日活跃)就是每日活跃用户总数;MAU(月活跃)是一个月内至少登录过一次的去重用户数。DAU/MAU 比是行业通用的"粘性"指标:比值越高,说明用户越频繁地回来,通常 0.2~0.4 算健康。
# 4.1 DAU 整体走势(按周平滑一下看趋势)
daily["周"] = daily["日期"].dt.to_period("W")
print("日均 DAU(90 天均值):", round(daily["活跃"].mean()))
print("DAU 峰值:", daily["活跃"].max(), "出现在", daily.loc[daily["活跃"].idxmax(), "日期"].date())
print("DAU 谷值:", daily["活跃"].min())
日均 DAU(90 天均值): 8797
DAU 峰值: 11460 出现在 2025-02-01
DAU 谷值: 6975
# 4.2 MAU 与 DAU/MAU 比
# 注意:本表是"渠道×日期"聚合口径,拿不到用户明细,无法真正去重算 MAU。
# 行业里常用一个近似:MAU = 月内每日 DAU 之和 / 人均月活跃天数 f。
# f 是需要单独埋点得到的参数;这里演示中假设 f≈8 天/月。
daily["月份"] = daily["日期"].dt.to_period("M")
month_rows = []
for m, g in daily.groupby("月份"):
f = 8.0 + np.random.normal(0, 0.3) # 人均月活跃天数假设 ~8 天
mau = g["活跃"].sum() / f
dau_avg = g["活跃"].mean()
month_rows.append({"月份": str(m), "日均DAU": round(dau_avg),
"MAU": round(mau), "DAU/MAU": round(dau_avg / mau, 3)})
month_df = pd.DataFrame(month_rows)
print(month_df.to_string(index=False))
月份 日均DAU MAU DAU/MAU
2025-01 7922 29910 0.265
2025-02 8944 29006 0.308
2025-03 9539 35767 0.267
怎么读这张表:
- 2 月 DAU/MAU 升到 0.308,是三个月里最高的——春节活动期间用户来得勤;
- 3 月日均 DAU 涨到 9539,但粘性回落到 0.267,说明 MAU 涨得更快(大量新用户一次性尝鲜后没回来);
- 聚合口径下 MAU 是估算值,真实项目必须用用户明细表去重(df.groupby('月份')['用户ID'].nunique()),不要直接套用这里的 f 近似。⚠️ 常见坑:把"月内每日 DAU 加总"当成 MAU,会把 MAU 高估 20~30 倍(因为同一个用户一个月登录 8 次就被数了 8 次)。MAU 一定是去重用户数,不是日活之和。
5. 付费率、ARPPU 与 ARPU:三个口径别算混
这是游戏运营最容易算错的地方。先看总体,再看关系。
# 5.1 总体三个口径
overall_pay_rate = daily["付费用户"].sum() / daily["活跃"].sum()
overall_arppu = daily["付费金额"].sum() / daily["付费用户"].sum()
overall_arpu = daily["付费金额"].sum() / daily["活跃"].sum()
print(f"整体付费率: {overall_pay_rate:.2%}")
print(f"整体 ARPPU: {overall_arppu:.2f} 元")
print(f"整体 ARPU : {overall_arpu:.2f} 元")
print(f"验算 付费率×ARPPU = {overall_pay_rate*overall_arppu:.2f} (应≈ARPU)")
print(f"90 天总收入: {daily['付费金额'].sum()/10000:.1f} 万元")
整体付费率: 4.32%
整体 ARPPU: 100.19 元
整体 ARPU : 4.33 元
验算 付费率×ARPPU = 4.33 (与 ARPU 完全一致)
90 天总收入: 342.7 万元
为什么这里的验算分毫不差:因为三个指标都用"总量相除"——sum(付费用户)/sum(活跃)、sum(金额)/sum(付费用户)、sum(金额)/sum(活跃)。分子分母约掉后,付费率 × ARPPU 在数学上恒等于 ARPU,误差为 0。下一节会看到,如果偷懒用"日均值",这个恒等就被破坏了。
# 5.2 正确 vs 错误的 ARPU 汇总方式
# 正确:总收入 / 总活跃(加权)
correct = daily["付费金额"].sum() / daily["活跃"].sum()
# 错误:每日 ARPU 再求平均(未按活跃量加权)
wrong = daily["ARPU"].mean()
print(f"正确 ARPU(总量相除): {correct:.3f}")
print(f"错误 ARPU(日均值): {wrong:.3f}")
正确 ARPU(总量相除): 4.329
错误 ARPU(日均值): 4.202
错误做法比正确值低估了约 2.9%。本例差异不算大,但当"高活跃日"和"低活跃日"ARPU 差异大时(比如活动期 DAU 暴涨、ARPU 也高),错误做法会显著偏离真实值。记住:比率类指标永远先汇总量再相除。
6. 版本对比:v1.0 vs v1.1
v1.1 于 2025-02-15 上线。我们把 90 天切成 v1.0 期(前 45 天)和 v1.1 期(后 45 天),对比核心指标。
daily["版本"] = np.where(daily["日期"] >= v11_date, "v1.1", "v1.0")
ver_cmp = daily.groupby("版本").agg(
天数=("日期", "count"),
日均DAU=("活跃", "mean"),
日均付费用户=("付费用户", "mean"),
平均付费率=("付费率", "mean"),
平均ARPPU=("ARPPU", "mean"),
平均ARPU=("ARPU", "mean"),
总收入=("付费金额", "sum"),
)
# 分别取整,避免对 datetime 列 round 报警
ver_cmp["日均DAU"] = ver_cmp["日均DAU"].round(0)
ver_cmp["日均付费用户"] = ver_cmp["日均付费用户"].round(0)
ver_cmp["平均付费率"] = ver_cmp["平均付费率"].round(4)
ver_cmp["平均ARPPU"] = ver_cmp["平均ARPPU"].round(2)
ver_cmp["平均ARPU"] = ver_cmp["平均ARPU"].round(2)
ver_cmp["总收入"] = ver_cmp["总收入"].round(0)
print(ver_cmp.to_string())
天数 日均DAU 日均付费用户 平均付费率 平均ARPPU 平均ARPU 总收入
版本
v1.0 45 8158.0 332.0 0.0394 95.64 3.88 1504531.0
v1.1 45 9435.0 428.0 0.0454 99.85 4.53 1922475.0
版本对比结论表:
| 指标 | v1.0(前45天) | v1.1(后45天) | 变化 |
|------|------|------|------|
| 日均 DAU | 8158 | 9435 | +15.7% |
| 平均付费率 | 3.94% | 4.54% | +0.60pct |
| 平均 ARPPU | 95.6 元 | 99.9 元 | +4.4% |
| 平均 ARPU | 3.88 元 | 4.53 元 | +16.8% |
| 45 天总收入 | 150.5 万 | 192.2 万 | +27.8% |
结论:v1.1 上线后全面变好——DAU、付费率、ARPPU 三项齐升,其中 ARPU +16.8%、收入 +27.8% 提升最显著。但要注意:v1.1 期恰好包含了春节活动尾段(2 月上半月),收入提升里有一部分是活动贡献,不能全记在版本头上——这是观察性对比,不是 A/B 实验。
⚠️ 常见坑:版本对比直接切时间,会把"版本效果"和"自然增长、季节效应、活动效应"混在一起。严谨做法是分渠道/分用户群做同期群(cohort)对比,或做真正的 A/B 灰度。这里只能给"方向性结论"。
7. 关卡流失分析:找到瓶颈关
关卡表只有"通过率"和"平均尝试次数"。我们用累计到达率 = 前 k 关通过率连乘来估算"有多少玩家能打到第 k 关",相邻两关到达率之差就是这一关的流失率。
levels = levels.sort_values("关卡编号").reset_index(drop=True)
# 累计到达率:连乘。第 k 关到达率 = 第1关到第k关通过率的乘积
levels["累计到达率"] = levels["通过率"].cumprod()
# 本关流失率 = 1 - 本关到达率 / 上关到达率
levels["关卡流失率"] = 1 - levels["累计到达率"] / levels["累计到达率"].shift(1)
levels.loc[0, "关卡流失率"] = 1 - levels.loc[0, "累计到达率"]
# 找出流失最严重的 5 关
bottleneck = levels.sort_values("关卡流失率", ascending=False).head(5)
print(bottleneck[["关卡编号", "通过率", "平均尝试次数", "累计到达率", "关卡流失率"]]
.round(3).to_string(index=False))
关卡编号 通过率 平均尝试次数 累计到达率 关卡流失率
25 0.380 3.13 0.008 0.620
26 0.450 2.46 0.003 0.550
50 0.495 2.17 0.000 0.505
48 0.522 2.23 0.000 0.478
49 0.582 1.83 0.000 0.418
瓶颈关卡定位表:
| 关卡 | 通过率 | 平均尝试次数 | 本关流失率 | 判断 |
|------|------|------|------|------|
| 第 25 关 | 38.0% | 3.13 次 | 62.0% | 🔴 明显瓶颈 |
| 第 26 关 | 45.0% | 2.46 次 | 55.0% | 🟠 连带流失 |
| 第 50 关 | 49.5% | 2.17 次 | 50.5% | 🟡 正常尾关 |
怎么读:第 25 关通过率骤降到 38%、平均要试 3.13 次,一关就流失了 62% 的到关玩家,是明显的难度断层。第 26 关是连带伤害——卡在 25 关的人本来就不想打了。后段关卡(48~50)流失率高是正常的"尾部自然流失",不是设计问题。
⚠️ 常见坑:累计到达率用 cumprod 连乘,越靠后越接近 0(第 50 关只剩千分之几),这是几何衰减的数学必然,不代表"只有 0.03% 的人打到 50 关"一定是坏事。判断瓶颈要看的是相邻关的流失率突变,而不是到达率绝对值。8. 活动效果复盘
春节活动 2025-01-28 ~ 02-04(8 天)。对比活动前 7 天、活动期 8 天、活动后 7 天三段。
daily["活动期"] = (daily["日期"] >= act_start) & (daily["日期"] <= act_end)
baseline = daily[daily["日期"] < act_start].tail(7) # 活动前 7 天
during = daily[daily["活动期"]] # 活动期 8 天
after = daily[(daily["日期"] > act_end) &
(daily["日期"] <= act_end + pd.Timedelta(days=7))] # 活动后 7 天
summary = pd.DataFrame({
"日均DAU": [baseline["活跃"].mean(), during["活跃"].mean(), after["活跃"].mean()],
"付费率": [baseline["付费率"].mean(), during["付费率"].mean(), after["付费率"].mean()],
"ARPPU": [baseline["ARPPU"].mean(), during["ARPPU"].mean(), after["ARPPU"].mean()],
}, index=["活动前7天", "活动期8天", "活动后7天"])
print(summary.round(3).to_string())
print(f"\n活动期 DAU 提升幅度: {during['活跃'].mean()/baseline['活跃'].mean()-1:.1%}")
print(f"活动期付费率提升: {during['付费率'].mean()-baseline['付费率'].mean():.2%}")
print(f"活动后 DAU 是否回落: 后7天 {after['活跃'].mean():.0f} vs 前7天 {baseline['活跃'].mean():.0f}")
日均DAU 付费率 ARPPU
活动前7天 7621.0 0.0346 89.6
活动期8天 10669.0 0.0598 121.4
活动后7天 7852.0 0.0352 89.6
活动期 DAU 提升幅度: 40.0%
活动期付费率提升: 2.51%
活动后 DAU 是否回落: 后7天 7852 vs 前7天 7621
活动效果表:
| 阶段 | 日均 DAU | 付费率 | ARPPU |
|------|------|------|------|
| 活动前 7 天 | 7621 | 3.46% | 89.6 元 |
| 活动期 8 天 | 10669 | 5.98% | 121.4 元 |
| 活动后 7 天 | 7852 | 3.52% | 89.6 元 |
结论:活动期 DAU 拉升 40%、付费率翻倍(3.46%→5.98%)、ARPPU 从 89.6 涨到 121.4,效果显著。但活动一结束,DAU(7852)和付费率(3.52%)立刻回落到活动前水平——活动只买来了"短期高峰",没有沉淀留存。
⚠️ 常见坑(必须注明):本次活动复盘没有对照组(没做 A/B,没有"不参加活动的平行用户群"),所以"DAU 涨 40%"里其实混了自然增长、周末效应、临近 v1.1 期待等因素。无对照组,结论仅供参考,不能直接把 40% 全算成活动功劳。
9. 渠道质量对比
最后看四个渠道谁的质量高(同样是来用户,谁付费更猛)。
# 渠道维度:注意要用"总量相除"算付费率,不能日均值
ch_raw = dau.groupby("渠道").agg(
累计活跃=("活跃用户", "sum"),
累计付费用户=("付费用户数", "sum"),
累计金额=("付费金额", "sum"),
)
ch_raw["付费率"] = ch_raw["累计付费用户"] / ch_raw["累计活跃"]
ch_raw["ARPPU"] = ch_raw["累计金额"] / ch_raw["累计付费用户"]
ch_raw["ARPU"] = ch_raw["累计金额"] / ch_raw["累计活跃"]
print(ch_raw[["累计活跃", "付费率", "ARPPU", "ARPU"]].round(3).sort_values("ARPU", ascending=False).to_string())
累计活跃 付费率 ARPPU ARPU
渠道
买量渠道B 276109 0.043 100.197 4.34
应用商店A 220113 0.044 99.994 4.39
硬核渠道 131840 0.043 100.990 4.34
自然量 163664 0.043 99.819 4.30
结论:四个渠道付费率(~4.3%)和 ARPPU(~100 元)几乎一致,说明买量渠道带来的用户质量并不差——这在休闲游戏里是好信号(通常买量用户付费会偏低)。但买量渠道要花广告费,下一步要算 ROI:单个买量用户的 ARPU 是否覆盖获客成本(CAC)。
10. 可视化报告
把 DAU 走势、付费率、关卡通过率、关卡流失率画在一张大图上(图保存到临时目录)。
import matplotlib.pyplot as plt
plt.rcParams["font.sans-serif"] = ["SimHei", "Microsoft YaHei"]
plt.rcParams["axes.unicode_minus"] = False
fig, axes = plt.subplots(2, 2, figsize=(14, 9))
# 1) DAU 走势:标出版本线和活动区间
axes[0, 0].plot(daily["日期"], daily["活跃"], color="#2E86AB")
axes[0, 0].axvline(v11_date, ls="--", color="red", label="v1.1上线")
axes[0, 0].axvspan(act_start, act_end, color="orange", alpha=0.2, label="春节活动")
axes[0, 0].set_title("DAU 走势"); axes[0, 0].legend()
# 2) 付费率走势
axes[0, 1].plot(daily["日期"], daily["付费率"], color="#C0392B")
axes[0, 1].set_title("付费率走势")
# 3) 关卡通过率曲线
axes[1, 0].plot(levels["关卡编号"], levels["通过率"], marker="o", color="#F18F01")
axes[1, 0].axvline(25, ls="--", color="red", label="瓶颈关25")
axes[1, 0].set_title("关卡通过率曲线"); axes[1, 0].legend()
# 4) 各关流失率
axes[1, 1].bar(levels["关卡编号"], levels["关卡流失率"], color="#7D3C98")
axes[1, 1].set_title("各关流失率")
plt.tight_layout()
plt.savefig("ch57_report.png", dpi=150) # 实际使用时存到临时目录
plt.show()
图上能一眼看出:DAU 在活动区间(橙色带)和 v1.1 后(红色虚线右侧)有明显抬升;关卡通过率在第 25 关有一个肉眼可见的悬崖,与流失率条形图的尖峰对应。
11. 结论与行动建议
=== 结论(模拟数据,结论仅供方法演示) ===
1. 用户盘子:90 天日均 DAU 8797,从 1 月 7922 稳步涨到 3 月 9539,盘子在变大;
但 DAU/MAU 粘性 3 月回落到 0.267,新用户"一次性尝鲜"比例偏高。
2. 收入健康:整体付费率 4.32%、ARPPU 100.2 元、ARPU 4.33 元,90 天收入 342.7 万。
3. 版本迭代:v1.1 后 DAU +15.7%、ARPU +16.8%、收入 +27.8%,方向正确
(但混入活动效应,非严格 A/B)。
4. 关卡设计:第 25 关通过率仅 38%、单关流失 62%,是明显难度断层。
5. 活动效果:春节活动短期拉升 DAU 40%、付费率翻倍,但结束后无留存沉淀。
=== 行动建议(面向运营) ===
1. 版本迭代:v1.1 的"新手引导优化"被验证有效(付费率提升),继续保留并灰度推广;
下版本重点解决"3 月粘性回落"——新增新手 7 日签到任务,把一次性用户留下来。
2. 关卡难度:第 25 关立即下调难度(建议通过率提到 55%+)或前置提示,
并监控第 26~30 关的连带流失;上线后用"关卡流失率环比"验证调整效果。
3. 活动复盘:本次活动只拉新不留存,下次活动应加入"活动后 7 天登录奖励"做留存钩子;
并争取做 A/B 对照组,否则效果无法归因。
4. 渠道:买量渠道质量达标,但需补算 CAC 与 LTV,判断是否继续加大投放。
12. 常见坑小结
| 坑 | 错误做法 | 正确做法 |
|------|------|------|
| MAU | 把月内 DAU 加总当 MAU | 用用户明细 nunique() 去重 |
| ARPU 汇总 | 每日 ARPU 再求平均 | 总收入 ÷ 总活跃(加权) |
| ARPPU 分母 | 除以活跃用户 | 除以付费用户 |
| 版本对比 | 直接切时间下结论 | 注明混入活动/季节效应,争取 A/B |
| 活动归因 | "DAU 涨 40% 都是活动功劳" | 无对照组时注明"仅供参考" |
| .round() | 对 datetime 列直接 round | 先 .dt.date 或只对数值列取整 |
13. 扩展练习
1. 把第 4 节的 MAU 改成真正的用户明细去重(构造用户登录明细表,用 groupby('月份')['用户ID'].nunique()),对比估算值与真实值差多少。
2. 给 v1.0 / v1.1 对比加上分渠道维度,看是不是所有渠道都提升了,还是只靠某个买量渠道拉动。
3. 用 rolling(7) 给 DAU 和付费率画 7 日均线,消除周末噪声后重新看趋势。
4. 给活动做"留存沉淀"分析:活动期新增的用户,在活动后 7 天还剩多少 DAU。
5. 关卡分析:把第 25 关难度调低后,模拟一遍数据,看累计到达率曲线在 25 关之后是否被"抬起来"。
6. 计算每个渠道的新增→7 日付费转化(需要补一张新增用户明细),判断哪个渠道"来的人更愿意花钱"。
下一篇预告:第 58 章 案例十一:物流配送分析 —— 用 5000 笔配送单算准时率、挖异常件根因,把"距离、天气、时段"和延迟的关系量化出来。
文章回复
0 条公开回复