做一个电商用户行为分析项目,是很多数据分析师入行时的必经之路。最近我把某电商平台的公开用户行为数据集完整跑了一遍,从数据清洗、指标体系搭建到漏斗转化、用户分层,最后沉淀出一套可以直接复用的分析模板。这篇文章把整个项目的技术细节、关键结论和踩过的坑记录下来,适合刚接触电商数据、想拿真实数据练手的人,也适合产品运营参考里面的分析思路。
先说清楚这个项目解决什么问题。市面上能拿到的电商用户行为数据并不少,但真正干净、字段完整、能支撑从浏览到购买全链路分析的公开数据集不多。这个数据集覆盖了约百万级用户在整整一个月内的行为日志,记录量过亿,包含浏览、加购、收藏、购买四种行为类型。通过对这批数据做系统性分析,可以回答几个核心问题:用户从看到商品到最终下单,每一步折损多少?什么样的用户是高价值用户?一天中哪个时段最容易促成转化?哪些品类的购买意愿更强?围绕这些问题,我拆解了完整的分析流程。
1. 项目目标与数据集认知
1.1 分析目标与适用场景
做用户行为分析最忌讳一上来就埋头跑数,先得把分析目标框定清楚。我这次的核心目标是还原用户从“看到商品”到“最终购买”的完整行为路径,找出转化链路中的关键瓶颈,并基于用户行为特征做价值分层。
这套分析思路不只适用于电商。任何有“访问-互动-转化”链条的业务,比如内容平台、在线教育、SaaS产品,都可以把同样的框架套进去。区别只在于行为类型字段的重新定义。比如内容平台可以把“浏览”换成“阅读”,“购买”换成“付费订阅”,漏斗的每一层含义变了,但分析逻辑是通用的。
适合谁来参考?如果你是刚入门的数据分析师,这份项目可以作为完整的练习范本;如果你是做用户增长或电商运营的同学,里面的漏斗数据和分层方法可以直接指导活动设计。我自己做这个项目时最大的感受是,数据量越大,越考验对业务口径的理解,这一点后面会反复强调。
1.2 数据表结构逐字段拆解
先看原始数据的字段结构,这是整个分析的地基。数据集由五个字段组成,每一行代表用户的一次行为记录,字段含义如下:
- user_id:用户唯一标识,脱敏后的数字ID。
- item_id:商品唯一标识。
- behavior_type:行为类型,枚举值为pv(点击详情页)、fav(收藏)、cart(加购)、buy(购买)。
- user_geohash:用户地理位置哈希值,很多行是空值,只能做辅助分析。
- item_category:商品类目ID,脱敏后的类目编号。
- time:秒级时间戳,记录行为发生的时刻。
这个字段设计在电商日志里很典型。behavior_type 是关键中的关键,它定义的四种行为其实对应了用户决策漏斗的不同阶段:pv是“看见了”,fav和cart是“感兴趣了”,buy是“下定决心了”。需要注意的是,fav和cart在漏斗中的位置并不完全先后,有人先收藏后加购,也有人直接加购,分析时要按业务逻辑灵活处理。
user_geohash 这个字段我建议谨慎使用。它缺失率很高,而且哈希后的精度只能定位到大致区域,做不了细粒度城市分析。我这次用它观察了“有地理标签用户”的行为差异,发现信息增益有限,后续的分析中干脆把它排除在主模型之外。
1.3 数据体量与分布特征观察
数据集覆盖一个月,总记录数据说过亿,用户数约百万级,商品数也有数十万。这个体量对单机Pandas来说是临界点,处理不当很容易内存溢出,所以提前做好数据类型的优化非常关键。
从行为分布上看,pv类行为占了绝大多数,购买行为占比通常只有百分之几。这是电商数据的常态,也是漏斗分析的价值所在。如果购买占比过高反而要警惕,可能数据采集环节出了问题。
另一个值得注意的分布特征是用户活跃度极端不均衡。少量头部用户贡献了大部分浏览行为,尾部大量用户一个月只有几次点击。这种“长尾分布”决定了后续做用户分层时,不能简单用平均值来描述用户特征,而要用分位数或分层统计。我后面会用具体数据展示这种偏态有多明显。
2. 数据预处理与口径统一
2.1 时间戳清洗:时区偏移与小时维度构建
原始数据里time字段是Unix秒级时间戳,直接转成日期时间格式会得到UTC时间。我这边习惯先统一转成北京时间再分析,否则按小时统计活跃度时,结果会整体偏移8小时,白天和夜晚的曲线完全错位。
转换代码很简单:
import pandas as pd # 读取数据时指定列名 df = pd.read_csv('user_behavior.csv', names=['user_id', 'item_id', 'behavior_type', 'user_geohash', 'item_category', 'time'], dtype={'user_id': 'int32', 'item_id': 'int32', 'behavior_type': 'category', 'item_category': 'int32'}) # 时间戳转北京时间并拆出时间维度 df['time'] = pd.to_datetime(df['time'], unit='s') + pd.Timedelta(hours=8) df['date'] = df['time'].dt.date df['hour'] = df['time'].dt.hour这里有个细节:read_csv时指定dtype而不是让Pandas自动推断,能省下大量内存。behavior_type用category类型存储效果显著,因为只有四个枚举值。我实测过,同样的数据不做类型优化和做优化,内存占用能差到三倍以上。
构建date和hour两个维度字段是必须的。date用于按天计算活跃用户数、日转化率,hour用于观察一天内的行为规律。如果原始时间戳没有做时区转换,后面所有按时间维度的统计都会出事。这个坑我在早期练习时踩过,结论是:时间字段永远先验证再使用。
2.2 去重策略:机械行为与真实行为的分界线
数据集里存在完全重复的行,也就是同一user_id、同一item_id、同一行为类型、同一时间戳出现多次。这种重复大概率是用户连点按钮或前端重复上报造成的。如果不做去重,pv量会被虚增,漏斗第一层的基数就不准。
去重时我分了两个层级。第一层是“整行去重”,逻辑上完全相同的记录直接合并;第二层是“业务去重”,同一用户对同一商品在很短时间内(比如几秒内)连续触发同一种行为,只保留第一条。第二种情况需要结合场景判断,因为用户可能在详情页和购物车之间反复横跳,这种“反复横跳”本身有业务意义,不能简单抹掉。
# 整行去重 df = df.drop_duplicates() # 时间上高度集中的重复行为,按用户+商品+行为分组后做时间差过滤 df = df.sort_values(['user_id', 'item_id', 'behavior_type', 'time']) df['time_diff'] = df.groupby(['user_id', 'item_id', 'behavior_type'])['time'].diff() df = df[(df['time_diff'].isna()) | (df['time_diff'].dt.total_seconds() > 5)]这段代码的意思是把同一用户对同一商品的同一行为按时间排序,计算相邻行为的时间差,间隔小于等于5秒的视为机械重复,只留下第一条。阈值5秒不是拍脑袋定的,我观察了时间差分布,发现绝大多数重复行为集中在5秒之内,超过5秒的操作基本是用户重新进入页面产生的真实浏览。
2.3 漏斗口径:时间窗怎么定决定结论怎么变
用户从浏览到购买往往不是一个连续动作,今天看商品,明天比价,后天再下单,这种情况非常普遍。所以在算转化漏斗时,必须先定义清楚:pv到buy的转化是看“同一天内的行为流”,还是放宽到“整个数据集周期内的全局转化”。
这两种口径的差异很大。按“当天”算漏斗,数据更保守,但能反映出页面当天的即时转化效率;按“全周期”算,更能体现用户的整体消费决策周期。我这次两个口径都算了,最后主报告用的是“全周期”口径,运营活动分析用的是“当天”口径。
这里要特别提醒:数据分析最重要的不是算出一个数字,而是让业务方能理解你这个数字代表什么。同一份数据,口径不同可能让转化率从1%变成8%,如果连分析者自己都没想清楚,后面的决策就无从谈起。
3. 核心指标体系与用户分层
3.1 从流量到转化的漏斗拆解
漏斗四层我把pv作为第一层,fav和cart合并成“兴趣层”,buy作为最终转化层。这样从“点击”到“购买”,中间隔着两个可以量化的兴趣信号。
实际计算时,我把每个用户去重后分别在四种行为上的行为人数作为分子分母。结合数据,各层之间的情况大概是这样的:
| 行为阶段 | 行为人数占比 | 说明 |
|---|---|---|
| 点击详情(pv) | 100% | 基础流量池 |
| 收藏(fav) | 6% - 9% | 表达兴趣但未下定决心 |
| 加购(cart) | 8% - 12% | 购买意愿更强 |
| 购买(buy) | 1% - 3% | 最终转化 |
加购占比通常略高于收藏,说明“加购”是比“收藏”更强烈的意向信号。从业务干预的角度看,收藏用户需要靠复购提醒、降价通知来唤醒,加购用户更适合用限时优惠直接催单。这是用户分层在运营动作上的直接映射。
还有一个值得深挖的指标:直接购买占比。有一部分用户根本不经过收藏和加购,从点击直接到下单。这类用户往往是老客,对商品熟悉,决策路径极短。把这一部分单独拆出来看复购表现,能侧面验证平台的用户忠诚度。
3.2 用户价值分层:RFM模型的行为变量改造
经典RFM模型用最近一次购买时间(Recency)、购买频率(Frequency)、购买金额(Monetary)三个维度给用户分层。但当前数据集没有金额字段,只有行为日志。所以我做了一次改造:用“最近一次活跃时间”替代Recency,用“活跃天数”替代Frequency,用“购买次数”替代Monetary,同时把浏览、加购等行为强度作为辅助维度。
# 构建用户行为特征 user_features = df.groupby('user_id').agg( last_active=('time', 'max'), active_days=('date', 'nunique'), pv_count=('behavior_type', lambda x: (x == 'pv').sum()), cart_count=('behavior_type', lambda x: (x == 'cart').sum()), fav_count=('behavior_type', lambda x: (x == 'fav').sum()), buy_count=('behavior_type', lambda x: (x == 'buy').sum()) ).reset_index()有了这些特征,按购买次数分为“未购买用户”“一次性用户”“复购用户”,再结合活跃天数做交叉分析,能得到典型的用户分层图谱。我这次的分层结果大致是:
- 高活跃高购买:占比极小的核心用户,贡献了很大比例的购买行为,是运营必须重点维护的群体。
- 高活跃低购买:典型的“逛客”,每天花大量时间浏览、加购,但迟迟不下单。这批用户更适合用优惠券或推荐算法去刺激转化。
- 低活跃高购买:购买决策果断,但来访频率低。可以认为是“目的性购买用户”,搜索直达下单,对推送敏感度低。
- 低活跃低购买:沉睡用户,需要警惕流失。对这部分用户的触达成本要控制,盲目召回性价比很低。
这种分层的业务含义远比单纯看整体转化率丰富。给运营同学的结论不再是一句“转化率偏低”,而是具体指出“哪一层用户”、“用什么手段”、“预计能改善哪个指标”。
3.3 忠诚度指标:复购率与行为间隔分析
复购率是电商健康度的另一个核心指标。我计算了用户在一个月内的购买次数分布,发现绝大多数购买用户只买了1单,购买2单及以上的用户占比明显下降。这个结果提示平台目前的用户黏性主要靠“拉新”而非“复购”驱动。
为了深入看复购间隔,我取每个用户相邻两次购买行为的时间间隔。间隔越短,说明用户对平台的依赖越强。分析发现,间隔在3天内的复购是主体,超过7天的复购占比大幅下降。这个“7天窗口”对运营很有参考价值:一次购买后的7天内是推送复购的最佳时机,过了这个窗口触达效率会衰减。
我自己在复盘这个分析时觉得,复购间隔的价值被很多人低估了。它比单纯的复购率更能指导“什么时候该给用户发消息”。比如针对7天内复购的用户,可以在第5天推同类目商品,效果通常比购买次日就推要好。
4. 实操环节:完整代码与业务解读
4.1 数据加载与清洗的完整脚本
前面几节已经穿插了一些代码,这里把从加载到清洗的完整流程串起来,方便直接复用。
import pandas as pd import numpy as np # 1. 指定列名、优化类型读取 df = pd.read_csv('user_behavior.csv', names=['user_id', 'item_id', 'behavior_type', 'user_geohash', 'item_category', 'time'], dtype={'user_id': 'int32', 'item_id': 'int32', 'behavior_type': 'category', 'item_category': 'int32'}) # 2. 时间戳清洗 df['time'] = pd.to_datetime(df['time'], unit='s') + pd.Timedelta(hours=8) df['date'] = df['time'].dt.date df['hour'] = df['time'].dt.hour # 3. 整行去重 df = df.drop_duplicates() # 4. 机械重复行为过滤 df = df.sort_values(['user_id', 'item_id', 'behavior_type', 'time']) df['time_diff'] = df.groupby(['user_id', 'item_id', 'behavior_type'])['time'].diff() df = df[(df['time_diff'].isna()) | (df['time_diff'].dt.total_seconds() > 5)] df = df.drop(columns=['time_diff']) # 5. 基础信息输出 print(f"记录总数: {len(df):,}") print(f"用户数: {df['user_id'].nunique():,}") print(f"商品数: {df['item_id'].nunique():,}") print(f"类目数: {df['item_category'].nunique():,}")这段脚本跑完,内存占用和记录数都会明显下降。我习惯先打印基础统计信息确认数据规模,再进入后续分析。如果记录数和原始数据差异过大,就要回头检查是不是过滤逻辑过激,把正常行为也删掉了。
4.2 漏斗转化率与用户路径计算
漏斗计算的核心是“行为人数去重”。每一层统计的是曾经发生过该行为的独立用户数,而不是行为次数。理由很简单:转化率描述的是“有多少人走到了下一步”,重复行为会让同一用户被重复计数。
funnel = df.groupby('behavior_type')['user_id'].nunique() funnel_pct = (funnel / funnel.loc['pv'] * 100).round(2) print(funnel_pct)输出大概长这样:
- pv: 100%
- fav: 7.8%
- cart: 9.5%
- buy: 2.1%
这里能明显看到加购到购买的转化只有两成左右,而点击到加购的转化也不到10%。电商平台上“逛得多买得少”是普遍现象,关键是通过这个漏斗找到最值得优化的环节。这个数据集的薄弱环节在“兴趣到行动”这一层:用户愿意点详情,但不愿意加购。运营侧可以做商品详情页的卖点强化,产品侧可以做加购按钮的引导优化。
更进一步,我算了同时发生过“收藏+加购+购买”“直接购买”“只加购未购买”等路径的用户占比,发现“直接购买”的用户往往复购率更高。这个洞察说明,老客运营和新人引导策略应该完全不同。
4.3 时段活跃度与品类偏好分析
按小时聚合行为可以画出一条全天活跃曲线。我观察到活跃度从早上开始爬升,午间出现小高峰,晚间达到峰值。购买行为的时段分布比浏览更集中,晚上是下单密集时段。
hour_stats = df.groupby(['hour', 'behavior_type']).size().unstack(fill_value=0) hour_stats['buy_pv_ratio'] = (hour_stats['buy'] / hour_stats['pv'] * 100).round(3)我按小时算了buy/pv比值,也就是每个时段“多少次点击能产生一次购买”。这个比值比单纯的购买量更能反映转化效率。实测发现,晚上的购买量虽然高,但点击量基数也大,转化效率反而不如工作日的上午时段。这个结论对广告投放时段选择有直接参考价值:预算不能只看流量高峰,要看转化性价比。
品类偏好相对简单,按item_category分组统计pv和buy占比。pv排名靠前的类目往往是热门品类,但buy占比最高的类目不一定是pv最高的。用“购买占比/点击占比”算偏好系数,能够筛选出“点击少但购买意愿强”的潜力品类,这类目可能流量被低估了,存在投放洼地。
4.4 用户分层落地与画像描述
用户特征表完成后,我给每个用户打上分层标签。分层规则用分位数和业务常识共同确定,不需要复杂算法,简单清晰最重要。
bins = [0, 1, 2, 5, 100] labels = ['1单新手', '2单成长', '3-5单成熟', '5单以上高价值'] user_features['buy_tier'] = pd.cut(user_features['buy_count'], bins=bins, labels=labels) # 叠加活跃度分层 user_features['active_tier'] = np.where( user_features['active_days'] >= user_features['active_days'].median(), '高活跃', '低活跃') user_features['final_segment'] = ( user_features['active_tier'] + '·' + user_features['buy_tier'].astype(str) )这样得到的分层结果,每层用户规模和核心行为特征都可以直接透视出来。我在报告中重点用了两个交叉维度:活跃度去乘购买深度。比如“高活跃·1单新手”占比不低,他们逛得很多但没有完成重复购买,这是流失高危人群,需要结合浏览记录做个性化推荐。而“低活跃·5单以上高价值”用户平时不怎么来,一旦到店就买不少,推送策略应该走“精品推荐”路线而不是“促销轰炸”。
分层不是目的,指导动作才是。分层标签如果不能落地到运营策略,分析就只是自嗨。
5. 常见问题与排查技巧实录
5.1 内存不足:一个数据类型优化就能解决
处理上亿条数据时,最常见的第一个拦路虎就是内存。直接read_csv后Pandas默认用int64存所有整数列,内存占用非常夸张。我建议读取时就用dtype指定int32甚至更小范围的类型,behavior_type这种枚举字段用category。
数值型字段能缩小就缩小。拿user_id来说,百万级用户完全可以用int32存,内存直接减半。如果还扛不住,可以考虑分块读取:
chunk_iter = pd.read_csv('user_behavior.csv', chunksize=500000, names=[...], dtype={...}) df = pd.concat(chunk_iter, ignore_index=True)不过真正稳妥的方案是先做一轮“预清洗”:只保留需要的列,把user_geohash这种高缺失、低价值的字段提前扔掉,可以在加载阶段就节约大量内存。
5.2 时间戳时区错误导致日活跃用户统计错乱
这是一个典型的“数据是对的,口径是错的”问题。Unix时间戳本身是绝对的,问题出在展示层。如果直接把时间戳转换成UTC时间,北京时间下午的活跃高峰会变成凌晨,按天分组的活跃用户数也会错位到前一天。
排查技巧很简单:先取一条已知时间戳,用工具换算成北京时间核对。对应到这个数据,我记得有个时间戳经转换后是某个工作日的晚上9点,结果在UTC体系里成了下午1点,看起来完全合理但完全错误。所以任何时间序列分析前,先验证一条记录的真实时间,再往下走。
5.3 重复行为造成的漏斗虚高
如果不去重,pv层的独立用户数不会有太大变化,因为一个用户重复点击不影响去重统计,但行为次数会被严重放大。我对比过不同去重策略下的pv总量,差异能到百万级别。
有一个更容易被忽略的点:去重不能只看“一模一样”的行。同一用户、同一商品、同一行为,但时间差只有1秒,这大概率也是前端重复上报。前面代码里用groupby后做时间差过滤,就是为了处理这种“近似重复”。阈值设多少要看数据特征,我推荐先做分布分析,找到明显的时间差断层,再定阈值。
5.4 行为日志数据与交易数据对齐问题
这个场景适用于实际业务数据:行为表记录的是用户行为,订单表记录的是交易订单。行为表的buy行为和订单表的下单量往往对不上,因为可能存在下单后取消、支付失败等情况。公开数据集一般没有这么复杂,但在真实业务中做用户行为分析时,一定要和交易订单表做交叉验证,明确“buy”到底代表“下单”还是“支付成功”。口径不一致会让整个漏斗失真。
我在做这个项目时设定了一个检查原则:任何关键指标先找另一种口径交叉验证。比如购买用户数,既可以从行为表统计,也可以从订单表统计,两边的差额就是需要解释的部分。分析报告里主动披露口径差异,比被业务方质疑再解释要好得多。
5.5 快速验证分析结果的小技巧
每次跑完一个指标,我都会随机抽取几个用户做人工核验。直接用SQL或Pandas筛出某个用户在原始表中的行为序列,按时间排序后肉眼过一遍,看行为路径是否符合常识。这个方法虽然土,但能发现很多自动化流程查不出的问题。
比如我抽查过一个“高活跃·复购”用户,发现他一周内对同一个商品反复收藏又取消、加购又删除。这种用户的“购买”很有可能是帮别人代拍或者比价行为,和普通用户的价值含义不完全一样。抽样核验的意义在于:理解数字背后的真实业务场景,避免纯粹从统计角度解读数据。
6. 分析结论的业务落地建议
6.1 不同用户分层对应不同运营动作
分层结果如果不能指导运营,价值就少了一大半。我的建议是按分层结果设计差异化策略:
| 用户分层 | 典型特征 | 运营动作建议 |
|---|---|---|
| 高活跃·新客 | 频繁访问但购买很少 | 推送热销品榜单,降低决策门槛 |
| 高活跃·成熟 | 收藏加购频繁 | 定向优惠券,限时降价提醒 |
| 低活跃·高价值 | 到店即买,停留时间短 | 精品上新通知,维护品牌信任 |
| 低活跃·沉默 | 行为频率显著降低 | 低频召回,避免过度打扰 |
这里面最关键的一条是:不要给所有用户发一样的消息。把预算平摊到沉默用户和核心用户身上,效率差距可以很大。
6.2 转化漏斗优化的优先级判断
根据漏斗数据,从pv到cart的折损最严重。优化建议按优先级排:
- 商品详情页改造:突出价格优势、库存提醒、关联推荐。
- 加购按钮的可视化引导:按钮位置、大小、文案都可以做A/B测试。
- 收藏后的自动触达:收藏用户7天内未加购,系统自动推送降价或库存变化通知。
- 购物车回访提醒:加购未支付的用户,48小时内定向提醒。
这套优先级排序的逻辑是“先在流量最大的环节做优化,收益才最大”。pv到cart的基数大,哪怕转化率提升一个小百分点,带来的加购增量都超过后面任何环节的优化。
这里我想分享一下漏斗分析的另一个心得:不要只看单层转化率,看相邻两层之间的流失原因更重要。用户没有从pv走到cart,到底是商品详情没吸引力,还是价格超出预期,还是竞品比价更优?数据只能告诉你“哪里流失了”,要回答“为什么流失”,还需要结合评论、价格、同类目商品对标等外部数据。这也是用户行为分析的边界。
6.3 项目复盘中的可复用模板
这个项目沉淀下来的分析框架,我整理成了四步模板,换任何数据集都能用:
第一步,明确业务目标和核心指标。先问自己“这个分析做出来给谁看,要推动什么决策”。没有业务目标的探索性分析,很容易淹没在细枝末节里。
第二步,按业务口径清洗数据。时间戳、去重、漏斗口径都要在这个阶段定清楚,并记录在分析文档里。
第三步,从漏斗、分层、行为规律三个角度搭指标体系。三者相互印证,比单独看任何一个都能得出更立体的结论。
第四步,结论必须有运营落地建议。数据分析的终点不是输出报表,而是驱动业务动作。
最后说一点实操感受。做用户行为分析,真正困难的地方往往不在算法,而在对数据的理解和对业务的理解。数据是七分清洗、三分分析,业务是七分常识、三分技巧。拿到一份原始数据,先花时间感受它、验证它、理解它,比直接套模型重要得多。我每次拿到新数据都习惯先做一轮“人肉探索”,把字段分布、缺失情况、极端值都过一遍,虽然耗时,但后续每一步都会走得更稳。
这个分析框架后续还可以继续扩展。可以接入商品维度的数据做价格带分析,也可以引入行为序列的时序模型预测用户下一单的时间点。对于想深入做下去的同学,我建议从“预测用户购买意向”这个方向入手,基于清洗好的行为特征,尝试逻辑回归或者树模型,这是用户行为分析从“统计描述”走向“业务预测”的自然延伸。