
上周终于把编号 zuoye01.10 的作业交上去了。这是我数据分析训练营里阶段考核的综合题数据是一份模拟电商订单记录要求从清洗、分析到可视化输出一份完整报告。说实话这份作业真正花时间的不是写代码而是理解业务口径、处理脏数据、反复校正图表逻辑的过程。这篇博文就当一次完整复盘拆题思路、清洗决策、指标计算、图表选型以及提交前踩过的三个坑。如果你也正在做类似的练手项目应该能从中找到几个可以直接抄的细节。1. 作业题目拆解先把业务问题翻译成技术任务1.1 拿到题目后先别写代码先做需求翻译作业题目本身不长核心是一句话基于一份电商订单数据分析销售趋势、用户复购情况和用户价值分层输出分析报告。听起来很简单但一打开数据集就发现问题没那么容易字段有订单ID、用户ID、下单时间、支付金额、商品类目、支付状态一共8000多行存在空值、重复值、时间格式不统一等问题。我做的第一件事不是写pandas代码而是把题目里的业务问题翻译成一组技术任务业务问题技术任务输出物销售趋势分析按月份汇总支付金额观察整体走势趋势图 波动解释用户复购情况按用户ID分组统计下单次数分布复购比例表用户价值分层计算RFM三个指标再做分箱R/F/M分层结果异常数据识别清洗空值、重复值、格式错误清洗记录表这样翻译完之后整个作业的结构就清楚了。少了这一步很容易陷入拿到数据先head()一下然后东看一眼西看一眼的无序状态。我见过很多同学做类似作业时一开始就画图最后发现口径不对整个图表推翻重做。先把需求拆成任务表后续每一步都对着表来效率会高很多。1.2 作业也是产品交付物里有隐性验收标准训练营的作业通常不会给出特别详细的评分标准但根据之前几次经验这类综合分析作业的隐性验收标准其实有三条第一清洗过程要可追溯第二分析口径要自洽第三图表必须能独立说明问题。可追溯的意思是你删掉的每一行、填充的每一个空值都要有理由。不是代码里dropna()一行带过而是清楚说明这里删了30行因为这些订单的支付金额为0且下单时间缺失属于无效记录。这不仅是作业要求也是真实业务里数据分析师的基本素养——老板随时会问你这个数据为什么是这么个数答不上来就尴尬了。口径自洽则决定了你的结论能不能站得住脚。比如计算复购率时到底是以用户ID出现次数为准还是以支付成功的订单数为准口径不同结果可能差出好几个百分点。这个问题我在后面章节详细展开。2. 数据清洗阶段最枯燥但最拉分的环节2.1 先给数据做一次体检拿到CSV文件后我习惯先用一行代码看整体概况import pandas as pd df pd.read_csv(orders.csv, encodingutf-8) print(df.info()) print(df.isnull().sum()) print(df.duplicated().sum())这一步相当于给人做体检先看有没有明显异常。当时的结果是总行数8126其中订单ID有17个重复支付金额有43个空值支付状态里有120行不是已支付下单时间有部分记录是2024/1/5 12:23这种格式而不是标准的2024-01-05 12:23:00。这里有个容易忽略的点很多人一看到空值就dropna()但我建议先用业务逻辑判断。比如支付金额为空的行如果是支付状态本身也是未支付那这些记录其实不属于已支付订单在后续做销售分析时直接排除是合理的但如果订单状态是已支付而金额为空那就属于数据录入错误得进一步处理不能简单删掉。我最终的清洗策略是这样的删除订单ID完全重复的17条记录保留第一条排除支付状态不是已支付的120行因为这些订单没有真实成交不该进入销售金额统计对已支付但金额为空的少量记录按该类目最近30天的平均支付金额填充并在清洗记录中备注统一时间格式把混合格式的日期字符串统一转成datetime类型。2.2 清洗动作留痕给自己留一条回退的路清洗记录这一步很多人会忽略但我在这次作业里专门建了一个clean_log.txt每做一次处理就记一行。比如[1] 删除重复订单ID 17条保留首次出现的记录。 [2] 排除支付状态为未支付/已取消的订单 120条。 [3] 填充已支付但金额为空的记录 5条用该类目近30天均价。 [4] 时间字段统一为YYYY-MM-DD HH:MM:SS格式涉及 326行。这样做的好处是万一后面分析结果不对可以溯源回去知道是哪一步操作造成了偏差而且最后写报告时这些记录可以直接用在数据处理说明那一节不用再回头翻代码。真实工作中这一步叫数据血缘项目越大越重要。作业阶段就养成习惯后面做实习或正式项目会顺手很多。清洗完之后数据从8126行变成了7989行已支付有效订单。这个基数就是后面所有指标计算的基础。3. 核心分析过程三个关键指标的拆解与计算3.1 销售趋势分析先做按月汇总再决定要不要看更细销售趋势分析最忌讳一上来就按天画折线图。按天画不是不行但作业数据量只有8000多行、时间跨度六个月按天看会有明显的周期性噪声——周末高、工作日低画出来是一条锯齿很重的线不利于看出趋势。我是先按月汇总支付金额画了总体走势然后再按类目拆开看结构变化。按月汇总的代码很简单df[order_month] df[下单时间].dt.to_period(M) monthly df.groupby(order_month)[支付金额].sum().reset_index()结果是一个很清晰的上升曲线前三个月平稳第四个月开始有明显增长第五个月出现一个峰值。此处有一个值得留意的点这个增长究竟是用户变多了还是老用户买得更勤了我把月活用户数当月有支付记录的用户ID数拉出来对比发现第四个月开始用户数并没有大幅增长但人均购买次数提升了。这说明增长的驱动因素更有可能是复购而不是新客获取。这样一个简单的对比就把分析从描述现象推到了解释原因报告深度立刻不一样。3.2 复购分析两个口径得出的结论天差地别复购分析是我这次作业里踩坑最多的地方。先说口径问题。口径A以用户ID出现的次数来算一个用户ID出现两次及以上就算复购用户。这个口径的问题在于如果同一用户ID在一笔订单里买了多个类目比如同一天分别下了一单数码一单服饰他会出现两次但这两单未必是复购。口径B以下单时间区分。同一用户ID在不同日期有过支付记录才算复购。同一天内多次下单算一次购买行为。用这两个口径分别算复购率结果差了大约6个百分点。作业题目里没有明确规定我最终采用了口径B并在报告里用一句话说明了选择原因本报告将复购定义为不同日期产生的多次购买行为同一日内的多笔订单合并为一次购买。复购用户的计算代码大致是这样purchase_dates df.groupby(用户ID)[下单日期].apply(lambda x: x.dt.date.nunique()) repurchase_users purchase_dates[purchase_dates 2].index然后是下单次数分布表。把每个用户的有效购买天数做分箱1次、2次、3-5次、6次以上做一个占比表。结果是典型的长尾结构只买过一次的用户占接近一半但购买6次以上的用户贡献了大约四成的销售额。这个对比放进报告里很有说服力直接指向维护高价值用户比拉新更划算的结论。3.3 RFM分层不用复杂的聚类用简化可用版本RFM是用户价值分析里的经典套路RRecency代表最近一次购买距离现在多久FFrequency代表购买频次MMonetary代表累计消费金额。很多教程会教你用K-Means聚类来分层但作业场景下我选择用一个更直白、也更稳妥的方法每个指标按中位数分成高低两组组合出8个用户类型。这样做的理由很简单聚类结果需要解释成本而且K-Means在8000多行数据上做容易出现分出来一堆但说不清每个簇的业务含义的尴尬。中位数二分组的方式虽然粗糙一点但每个分组的含义是明确的报告里写起来也顺。实际操作中我给RFM三个指标各自算中位数然后分别标记高低R值距分析日取数据最后一天超过中位数的记为低R流失风险高否则高RF值购买次数高于中位数的记高FM值累计金额高于中位数的记高M。最后组合出的重要价值用户高R、高F、高M数量只有两百多人但贡献的销售额占比达到三成。这个结论和复购分析形成了呼应整个报告的分析逻辑就闭环了。RFM的价值不在于分类本身而在于分类之后你能针对不同人群提出不同的运营动作——比如高R低M的用户适合做小额唤醒优惠低R高M的用户则需要电话回访而不是短信推送。4. 可视化与图表选型同样一张图为什么你的像草稿4.1 选图遵循的是一个直觉安全检查图表选型这块网上有很多所谓XX数据该用XX图的速查表但实际用的过程中我自己总结了一个更简单的判断标准这张图能否让读者在一秒内说出结论。月度销售趋势选折线图因为要看连续变化类目销售额对比选柱状图因为要横向比较大小用户购买频次分布选柱状图加百分比标签因为这本质上是排序比较RFM四象限散点图则能让高价值用户直接浮现在右上角区域一眼定位重点。有几个我特意避开的坑不用饼图展示多个类目的销售额结构因为超过5个类目后饼图占比会变得很难读不用面积图做趋势对比因为面积重叠后视觉上会产生误导——上方的面积会被看成更大的值其实只是先画的层被后画的层遮住了。这些细节不会让数据出错但会影响报告的专业感受。4.2 配色、标注、字体专业感藏在细节里我在配色上踩过一次教训这次长记性了。第一次做出的图表用的是默认配色的红蓝组合红色代表重要价值用户蓝色代表一般用户图片放大后整体刺眼而且色盲人群很难区分这两种颜色。后来我把配色改成了一组更稳妥的方案主色用深蓝#2E86C1强调色用橙色#E67E22其他辅助色统一降低饱和度。改动不大但整张图的气质立刻不一样了。标注方面每张图都必须有三样东西标题、坐标轴说明、关键数据点注释。尤其是注释这是能否体现分析感的分水岭。同一个折线图有注释第5个月销售额环比增长43%主要由老用户复购带动和没有注释读者获取信息的速度完全不一样。图表不是用来展示数据的而是用来呈现观点的。4.3 图表要能独立讲故事这是我交作业前反复迭代的一个标准不看报告正文只把图表按顺序摆出来能不能大概读懂这个故事我的图表顺序是月度销售趋势折线图——整体先平后升类目销售结构柱状图——数码、服饰、家居三分天下复购用户占比与购买次数分布图——长尾结构RFM散点图——高价值用户集中在右上角。这四张图连起来故事是销售额增长主要靠复购驱动复购又集中在少数高价值用户上而这类用户主要买的品类是数码和家居。这个叙事逻辑清晰图表之间互相补充不是各说各话的素材堆砌。5. 翻车现场这次作业里真正卡住我的三个问题5.1 日期字段解析一个报错背后是混合格式问题第一次运行pd.to_datetime()时我以为一行代码就能搞定时间解析结果报错信息提示Unknown string format而且是断断续续地报错。排查了一下才发现数据里的下单时间格式既有2024-01-05 12:23:00这种标准格式也有2024/1/5 12:23这种混合写法还有少数是2024-01-05T12:23带T的ISO格式。解决方法倒是很简单指定formatmixed参数可以让pandas自动适配多格式df[下单时间] pd.to_datetime(df[下单时间], formatmixed)这个参数在pandas 2.0之后可用旧版本可能需要先手动替换分隔符再解析。这个问题给我提了个醒真实业务数据里的时间字段永远比你想的更乱清洗阶段先跑一个unique()看看有哪些写法再决定解析策略而不是盲目套格式。5.2 复购人数对不上一次性用多个指标分组时出现了数据变形这是我这次作业最费时间的一个问题。我在计算每个用户的购买天数和累计金额时一开始用了两个groupby结果直接merge发现行数膨胀到接近两倍。原因是我在分组时没有把用户ID作为唯一的键去重一个用户购买了10次在购买天数分组里占一行在累计金额分组里也占一行两个表一合并就变成了多行对应多行的笛卡尔连接。排查思路是这样的先用shape对比合并前后行数确认发生了数据膨胀然后单独跑每个分组的nunique()发现用户ID数量相同但行数不同定位到是分组键粒度不一致最后改用一次groupby同时聚合多个指标一行搞定rfm_raw df.groupby(用户ID).agg( R(下单日期, max), F(订单ID, nunique), M(支付金额, sum) ).reset_index()这样出来的结果就是每个用户一行。这个问题的本质是分组粒度不一致而它藏得很深因为结果看起来好像没错只有行数对不上的时候才会暴露。排查过程中我是靠逐行核对head()慢慢发现的过程很磨人但对先检查行数再检查数值这个习惯算是彻底记住了。5.3 折线图自带锯齿差点把分钟级噪声当真实趋势做销售趋势分析的时候我先按天汇总画了一张折线图发现线上布满细密的锯齿看起来似乎有明显的周期性波动。于是我又按小时拆开统计发现一天之内有十几个波峰波谷。幸好我没有急着给这些波动编故事——按小时的数据显示凌晨三四点几乎没有成交中午和晚上各有一次高峰这是典型的正常消费节奏不是异常信号。如果我不做这一步验证直接按天数据解释工作日和周末有明显差异虽然结论本身可能没错但论证过程站不住脚。后来我把时间粒度调整到周级别趋势线才变得平滑可用结论也更稳健。这次经历让我养成了一个习惯看到任何异常的波峰先怀疑粒度再怀疑业务最后才怀疑数据错误。6. 交作业之前的自查清单与个人复盘6.1 报告结构自检从数据到结论能不能闭环写完初稿后我没有立刻提交而是按下面这份清单过了一遍[x] 总述里是否说清了数据来源、时间范围和有效订单量[x] 每个图表是否都有数据口径说明读者能否理解复购用户的定义[x] 趋势分析是否解释了变化原因而不是只描述变化本身[x] RFM分层结果是否给出了对应的运营建议[x] 全文结论是否有支撑数据是否存在我觉得式的空判断其中第四点我打磨了好几遍。最初的版本只写了重要价值用户贡献了30%销售额后来加了一句建议针对该群体做新品优先推荐和专属客服维护。就这一句话报告从数据分析变成了分析建议观感差别很明显。6.2 这次作业完成后我自己沉淀下来的三条经验第一作业和真实项目一样交付质量的差距往往不在分析技术而在流程控制先拆题、再清洗留痕、接着算指标、最后画图填结论按顺序走每一步的输出物都是下一步的输入物。第二口径说明一定要写在报告里。哪怕题目没有要求主动写一句这里采用的口径是……会让读者觉得你是个靠谱的分析师反过来不写口径的数字会让读者心里一直悬着一个问号。第三图表风格统一比做很多炫酷图表更重要。颜色、字体、标题格式全篇保持一致整份报告的专业感立刻上一个台阶。这个经验以后我写任何分析报告都会沿用。最后再分享一个小技巧分析报告的结论尽量控制在三条以内而且要能互相独立、合在一起又能拼出一个完整故事。这次作业的最终结论就是三条——销售额整体上升但波动明显、增长主要由复购驱动、高价值用户集中在少数人群。三条结论互相支持没有重叠也没有互相矛盾的地方。复盘完这一轮我的感受是数据作业做到位之后回头看真的会发现收获比想象中大得多。