用pandas做漏斗分析的核心是确保每步用户集为前序步骤的子集,否则转化率虚高;需按业务路径顺序定义funnel_steps,通过集合交集逐步缩小用户群,并以留存用户为分母计算转化率。

直接结论:用 pandas 做漏斗分析,核心不是“写多复杂”,而是确保每一步的用户集合是前序步骤的子集——否则转化率会虚高。
为什么 funnel_steps 顺序必须严格对应业务逻辑
漏斗不是按事件发生时间排的,而是按用户完成路径的「预期顺序」定义的。比如电商场景必须是 ['impression', 'click', 'add_to_cart', 'order', 'pay'],不能把 'pay' 放前面,也不能跳步(如直接从 'impression' 到 'order' 不算有效路径)。
常见错误现象:funnel_result['order'] 比 funnel_result['click'] 还大——说明你没做交集,只是分别统计了各事件的独立用户数。
- 正确做法:从第一步开始,用
&操作符持续缩小用户集合 - 错误做法:对每个
event单独df[df['event']==step]['user_id'].nunique() - 性能影响:用
set交集比多次groupby+isin快 3–5 倍(尤其在百万级用户时)
groupby('user_id').apply() 是还原路径的前提
原始日志是扁平的事件流,一个用户可能有几十条记录。不做路径还原就直接漏斗,等于默认“只要用户干过某件事,就算走完了前面所有环节”——这显然不对。
使用场景:你要判断“U123 是否完成了 click → add_to_cart → order”,就必须先确认这三件事是同一个用户、且时间上有序发生。
- 先排序:
df.sort_values(['user_id', 'event_time']),否则apply里无法判断先后 - 再聚合:
df.groupby('user_id')['event'].apply(list)得到每个用户的完整行为序列 - 关键点:不是所有用户都按顺序走完,得用
next((i for i, x in enumerate(seq) if x == step), None)找首次出现位置,再比对索引关系
转化率计算必须用「上一步留存用户」作分母
很多初学者直接用原始总用户数(比如 10000)当所有步骤的分母,结果得到的“支付转化率”是 4.86%,而实际应是 486 / 608 ≈ 80%——因为只有下单用户才可能去支付。
参数差异:funnel_steps[0] 的分母是全部参与该步的用户;从 i=1 开始,分母必须是 funnel_result[funnel_steps[i-1]]。
- 示例代码片段:
for i, step in enumerate(funnel_steps): if i == 0: current_users = set(df[df['event'] == step]['user_id']) else: next_step_users = set(df[df['event'] == step]['user_id']) current_users = current_users & next_step_users funnel_result[step] = len(current_users) if i > 0: prev_step = funnel_steps[i-1] rate = round(len(current_users) / funnel_result[prev_step] * 100, 2) print(f"{prev_step} → {step}: {rate}%") - 容易踩的坑:没处理
current_users为空的情况,导致除零错误 - 兼容性注意:Pandas 2.0+ 中
set.intersection()比&更明确,但语法稍长
真正难的不是写这几行代码,而是确认你的 user_id 是稳定标识——如果登录态丢失、设备 ID 混用、或埋点未打全,漏斗数字再好看也没意义。别急着画图,先花 20 分钟检查三条日志:同一个 user_id 是否真能串起完整路径。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











