尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Python电商订单数据分析:从数据清洗到用户分层与销量预测

Python电商订单数据分析:从数据清洗到用户分层与销量预测 我先把结论放在前面这份模拟电商订单数据如果只做“算一算 GMV、画两张折线图”那根本不算分析。真正值钱的是把用户的购买行为拆开看找到哪些人值得维护、哪些商品正在悄悄衰退、下个月大概能卖多少、以及促销活动到底有没有把利润吃掉。这篇文章就是按这个思路走一遍完整的 Python 数据分析流程代码、坑和业务结论都会提到。1. 项目背景一份“看起来能算”的订单表实际坑在哪里1.1 数据是什么样的先说清楚这次分析用到的数据。因为真实的电商订单数据涉及用户隐私和商家经营数据我这次用的是模拟生成的脱敏数据字段结构模仿国内某电商平台的后台导出结果一共 5 万多条订单记录时间跨度从 2025 年 1 月到 2025 年 6 月。用到的字段包括order_id订单编号user_id用户 ID已脱敏order_date下单日期product_id商品 IDcategory商品类目quantity购买数量price成交单价元payment_method支付方式province收货省份is_new_user是否新用户is_promotion是否参与促销活动拿到这类数据第一反应肯定是算总销售额、总订单量。但实际做的时候会发现原始表远没有想象中干净日期有的是字符串有的是时间戳、价格字段里混着“99.00元”这种带单位的值、同一订单可能有退款记录但是没有任何标记。所以分析的第一步不是画图是先把数据变成“可信的”。1.2 分析目标要拆成可执行的问题我习惯把业务问题拆成四个层级这样后续写代码不会跑偏经营大盘这个半年整体卖了多少、订单趋势怎么样、客单价有没有变化用户价值哪些用户在持续购买哪些是一次性用户新用户和老用户的贡献差多少商品结构哪些商品贡献了大部分营收尾部商品是不是在拖累库存和运营精力销售预测能不能基于已有趋势给下个月一个相对靠谱的销量区间。这四个问题背后其实对应了常见的管理动作调整促销预算、优化商品陈列、做用户召回、制定备货计划。没有这些场景。1.3 环境准备轻量但够用这次我全程用的是 Jupyter NotebookPython 3.10核心库是 pandas、numpy、matplotlib、seaborn外加一个 sklearn 做简单的销量预测。没有上 Spark 或数据库引擎因为 5 万条数据用 pandas 处理完全够用强行引入重工具反而拖慢分析节奏。pip install pandas numpy matplotlib seaborn scikit-learn装好之后必要的导入是这样import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False sns.set_style(whitegrid)需要提醒的是中文标签在 matplotlib 下默认会乱码SimHei 只是其中一种方案换成微软雅黑等字体都可以只要系统里存在对应的字体文件就行。后面所有可视化我都基于这套配置避免在图上出现方框字。2. 数据清洗多个字段的组合校验远比单独查空值重要2.1 读入数据后的第一件事我先把数据读进来做最基本的体检df pd.read_csv(ecommerce_orders.csv) print(df.shape) print(df.info()) print(df.head())输出的结果里5 万多条记录没有整列的空值看上去很完整。但不要被这个假象骗了我在第一次跑的时候就因为看到“没有空值”而直接进入了计算结果销售额比预期高出一截。原因后面会讲。正确的做法是先按字段看类型再按业务规则看内容。我把需要处理的字段分成几类订单编号必须是唯一的如果出现重复通常是补单或者系统日志导致时间字段要能转成 datetime且不能超过分析时间范围金额字段必须是正数且不能超过商品标价的合理倍数用户字段user_id 不能为空不能出现类似“测试用户”“undefined”这样的脏数据。2.2 日期与金额的隐藏问题原始表里的 order_date 字段长这样2025/1/3 14:22 2025-01-15 09:08:00 2025.2.1 20:12三种格式混在一起直接 pd.to_datetime 倒是能勉强解析出来但一旦中间混进“2025/13/1”这种无效日期报错信息只会提示“未知字符串格式”不会告诉你具体是哪一行。所以我一般会先把原始值保留一列然后用 errorscoerce 转完对比数量差把解析失败的行单独拎出来看df[order_date_raw] df[order_date] df[order_date] pd.to_datetime(df[order_date], errorscoerce) invalid_date df[df[order_date].isnull()] print(f解析失败 {len(invalid_date)} 行)金额字段更麻烦。模拟数据里人为加了各种脏数据比如price 列里出现“99.00元”数值中间去掉小数点变成“9900”促销价和原价混在同一列处理思路是先转成字符串用正则只保留数字和小数点再转 numericdf[price] df[price].astype(str).str.replace(r[^\d\.], , regexTrue) df[price] pd.to_numeric(df[price], errorscoerce)这里有个关键点不要小看字符串清洗。如果把“99.00元”直接 astype(float)pandas 不会报错但也不会帮你去掉“元”字最终这一列会变成 object 类型后面算求和就会报错。我在这上面吃过亏。2.3 重复订单、退款和超量购买订单编号去重是清洗中最容易被忽略的一步。同一个用户同一件商品一天内买了两次可能是真事也可能是系统重复记录了。我的处理办法是duplicated_key [user_id, product_id, order_date, quantity] dup df.duplicated(subsetduplicated_key, keepFalse) print(f疑似重复记录 {dup.sum()} 条)只标记不立刻删除。因为电商场景里“同一天买两单”是合理的直接去掉会损失真实购买行为我通常会把标记列加进去让业务方确认后再处理。退款和促销的判断同样要谨慎。我这次用 is_promotion 字段区分促销订单但发现部分订单金额低于该商品历史均价 30% 以上而有相当一部分并非促销单。这有两种可能一是商品确实调过价二是用户使用了优惠券但系统没有记录。最后我在建特征时加了一个“异常低价订单”的布尔字段后续分析里可以做剔除也可以单独分析。经验数据清洗不能只看单独字段要组合起来看。价格、数量、金额之间的倍数关系往往比单列空值更能暴露问题。清洗后的数据大概长这样order_iduser_idorder_datecategoryquantitypricepaymentprovinceis_new_useris_promotionA0001U10012025-01-03数码1899.0支付宝广东10A0002U10022025-01-03服饰2199.0微信浙江013. 经营大盘GMV、订单量、客单价和它们之间微妙的关系3.1 从哪几个指标看大盘大盘分析不等于堆指标。我看大盘时关注的核心只有三个总销售额GMV、总订单量、客单价外加一个“有支付行为的用户数”。这四个指标组合起来才能判断增长到底来自更多新用户、更多老用户复购还是单纯客单价提高。df[total_amount] df[quantity] * df[price] gmv df[total_amount].sum() order_count df[order_id].nunique() user_count df[user_id].nunique() aov gmv / order_count print(fGMV: {gmv:.2f} 元) print(f订单量: {order_count}) print(f客单价: {aov:.2f} 元)模拟数据算出来的结果是GMV 约为 470 万、订单量 5.2 万、客单价 90.4 元。客单价不算高符合普通电商快消品的特征。但只看总量没有意义我更想知道趋势。我按下单日期聚合到“日”再做 7 日滚动平均目的是抹掉周末和活动日的周期性波动。daily df.groupby(df[order_date].dt.date)[total_amount].sum().reset_index() daily.columns [date, gmv] daily[gmv_ma7] daily[gmv].rolling(7).mean() plt.figure(figsize(12, 5)) plt.plot(daily[date], daily[gmv], alpha0.3, label每日GMV) plt.plot(daily[date], daily[gmv_ma7], label7日均线, linewidth2) plt.legend() plt.title(每日GMV趋势) plt.xticks(rotation45) plt.show()从图上能明显看到春节期间有一个低谷随后稳步回升到了五月下旬有一次尖峰对应的是平台促销活动。这里特别注意促销日的峰值会把前后两周的销售额“吸走”消费者在活动前观望、活动后休息所以看趋势不能只看单日峰值要看滚动均线。3.2 促销订单和自然订单要分开看我把促销订单和自然订单分开统计后发现一个很有意思的事实促销订单数量能占到总订单量的 32%但促销订单的客单价只有自然订单的 61%。也就是说促销确实拉动了订单量但没有等价地拉动销售额这类活动的意义更偏向用户获取和清库存而不是利润增长。看代码promo df[df[is_promotion] 1] non_promo df[df[is_promotion] 0] print(促销单客单价, promo[total_amount].sum() / len(promo)) print(自然单客单价, non_promo[total_amount].sum() / len(non_promo))这里的结果直接影响了后面的用户分析如果促销带来了大量一次性用户那整体复购率会被拉低不能简单归因为产品质量不行。3.3 省份和支付维度的“意外发现”很多人喜欢上来先画省份柱状图但销售额排在前面的一定是人口大省没什么信息量。我这次用了“省份人均购买金额”来排才发现部分西部省份的客单价反而偏高原因很可能是物流成本高、用户更倾向于凑单。从这个角度看针对这些省份做满减门槛调整可能比统一降价的边际收益更高。支付方式方面支付宝、微信、银行卡三分天下但分期支付的平均客单价是其他方式的两倍多。这说明能分期的用户买的更多如果有条件接入更多分期渠道也许能拉高客单价。这是一个值得和业务方确认的点。4. 用户分层复购、RFM 和新老用户拆解4.1 复购率为什么不能只看一个总数复购率最简单的算法是“购买次数超过一次的用户数 / 总用户数”。但如果新用户占比很高这个指标会被稀释掉。我通常按时间窗口拆成几个指标首购后 30 天内复购率首购后 90 天内复购率按月份看新用户次月复购率模拟数据的 30 天复购率在 18% 左右90 天复购率在 31% 左右。第一眼觉得不高但如果只算“二月首次购买用户的 30 天复购率”数值会明显好于五月因为五月进来大量促销新用户这批用户的留存本来就需要持续观察过早算复购率没有参考价值。这样拆开之后运营动作完全不一样对二月的用户可以做“回归有礼”唤醒对五月的用户应该先做“首单后 7 天优惠”而不是无边界的推送。4.2 RFM 模型的简化落地RFM最近购买时间、购买频率、购买金额是经典的用户分层方法。但在没有专业数据团队的情况下全量算 RFM 分数容易过度工程化。我的做法是只取三个维度各算中位数然后划分成 8 个群体重点关注其中的重要价值用户、重点发展用户、流失风险用户和一般挽留用户。rfm df.groupby(user_id).agg( recency(order_date, lambda x: (pd.Timestamp(2025-06-30) - x.max()).days), frequency(order_id, nunique), monetary(total_amount, sum) ) r_med rfm[recency].median() f_med rfm[frequency].median() m_med rfm[monetary].median() rfm[R] (rfm[recency] r_med).astype(int) rfm[F] (rfm[frequency] f_med).astype(int) rfm[M] (rfm[monetary] m_med).astype(int) rfm[segment] rfm[R].astype(str) rfm[F].astype(str) rfm[M].astype(str)RFM 分群结果很典型重要价值用户RFM 111人数只占 5.8%却贡献了 27% 的销售额。尽管这个结果不算意外但它给了一个明确结论这批用户值得一对一维护比如人工回访、专属客服、生日礼物。反过来一般挽留用户RFM 000占了 40%这部分用户大量来自促销活动的一次性流量对他们最好的策略不是频繁发券而是减少触达成本等他们有明确需求再召回。经验RFM 的阈值不一定要用均值或中位数。如果业务方明确知道“月购买 2 次算高价值”就直接按业务规则切模型要服务于运营不是反过来让运营迁就模型。4.3 新老用户贡献拆解用 is_new_user 字段区分新老用户后老用户订单量虽然只有新用户的 0.8 倍但老用户的客单价高出 42%。结合 RFM 的结果我的结论是目前平台并不缺一次性新客缺的是把新客转化成老客的路径。新用户的首单类目也值得细看。如果是通过低价日用品拉新那么首单后的第二单往往会落在“相关品类”比如买了手机壳的人可能很快买充电线。基于这个逻辑可以给新用户做“连带购买推荐”而不是推爆款。5. 商品分析用 ABC 定位核心商品用价格带判断结构健康度5.1 ABC 分析不是只看销售额排名ABC 分析的传统做法是把商品按销售额累计占比分成 A、B、C 三类A 类占 70% 以上的销售额B 类占 20%C 类占 10%。但只看销售额会漏掉“利润贡献”和“库存风险”。在只有订单数据的情况下我加了一个维度销售额占比和订单量占比的差距。如果某个商品销售额占比高但订单量占比很低说明它是高客单价低频商品这种情况下用户决策周期长不适合和低单价商品做同一个满减策略。我用模拟数据跑出来A 类商品数量只有 SKU 总量的 3.2%却贡献了近 70% 的销售额头部效应非常明显。B 类中有一部分是“潜力商品”特点是订单量在近 60 天内持续上升但销售额占比还不够高我单独标记出来作为下一期主推的候选。5.2 价格带区间分析把商品按价格分成 5 个区间0 到 50、50 到 100、100 到 200、200 到 500、500 以上。结果很明显100 元以下商品贡献了 58% 的订单量但 500 元以上商品贡献了 21% 的销售额订单量占比只有 2.3%。这说明价格带分层清晰两个价位段服务的是完全不同的需求。借着这个结论我给业务建议是主力引流款放在 50 元以下利润款放在 200 到 500 元不要在 100 到 200 元这个区间配置过多资源因为它的订单量不能快速起量客单价又撑不起销售额容易变成鸡肋价格带。5.3 商品衰退信号用每个商品每月的订单量做环比变化我识别出明显衰退的商品集中在两个类目数码配件和家居小件。表面原因是这些商品大部分没有参与促销更深层原因可能是同类竞品频繁上新抢走了流量。做商品分析时我会加一个 DSR日销量趋势指标结合最近 14 天销量占比来判断商品是处于“稳定期”还是“衰退期”这比单独看总销量更灵敏。6. 销量预测先用简单模型跑通再考虑要不要上复杂模型6.1 为什么不用 ARIMA 或 LSTM 一上来就预测很多初学者看到“预测”两个字就直奔 LSTM结果数据量不够、特征太少模型效果还不如 naive 预测。我在这个项目里只有一个半年的日度数据时间跨度短做不了复杂的季节性建模。与其套复杂模型然后解释不清不如先用最直白的移动平均和线性回归给业务一个可解释的参考范围。预测的目标我定为“下个月总销售额”而不是某一天的销售额因为日度预测的误差在实际业务里很快就会超出可接受范围而月度预测用于备货和预算更有意义。6.2 时间序列交叉验证的简单做法我把前 5 个月作为训练集最后 1 个月作为验证集。用 pandas 重采样成周度数据然后建立一个带时间趋势的线性回归模型from sklearn.linear_model import LinearRegression weekly df.resample(W, onorder_date)[total_amount].sum().reset_index() weekly[week_num] np.arange(len(weekly)) weekly[month] weekly[order_date].dt.month train weekly.iloc[:-4] test weekly.iloc[-4:] model LinearRegression() model.fit(train[[week_num]], train[total_amount])预测值和实际值对比发现误差在 12% 左右。对一个销售预测来说这个误差勉强能接受但离“可用”还有距离。我随后加入了月份哑变量和“是否有促销”的特征误差能降到 9% 左右。这个结果说明促销信息对预测的贡献比时间趋势本身更显著。6.3 预测结果的业务落地方式我没有直接输出一个预测总数而是给了一个区间下月预测销售额约在 85 万到 105 万之间。这个区间上下差了 20 万看着很大但放在库存和营销费用决策里已经能派上用场。备货时按区间下限准备畅销品按上限准备活动库存中间的差额用供应商柔性补货来覆盖。预测模型的核心不是哪个算法更准而是特征的采集要稳定。比如“是否参与大促”这个字段如果在预测周期内还不知道有哪些活动模型就缺了一个最关键的输入。模型能跑通是第一步业务数据能不能按时准备好才是预测项目成败的关键。7. 复盘这次分析里最容易翻车的几个细节7.1 先定业务口径再写代码不然全部白算“销售额”到底算不含税价还是含税价“退款订单”要不要剔除“促销订单”是指参与促销的还是用优惠券的每一个口径不同结果可能差出 30%。我这次一开始就犯了这个错误直接把所有订单算成销售额后来发现模拟数据里包含部分退款单如果直接计算GMV 会虚高。最后统一口径是只保留 quantity 0 且 price 0 的有效订单促销状态按 is_promotion 标记单价按订单实际成交价计算不额外处理退款。虽然这个口径不一定适合所有场景但写代码前把它定死后面和业务沟通才有基准。7.2 代码要方便复查分析过程中会反复生成临时 DataFrame如果不注意变量命名过两天自己都看不懂。我的习惯是主表叫 df清洗后的叫 df_clean聚合结果带 _agg 后缀中间过程的列名不随便缩写。这一步节省的时间在项目后期比什么都值钱。另外我所有图都保存了带日期的 PNG 文件文件名类似daily_gmv_20250701.png。这样给别人发分析结论时直接甩图不用重新跑代码。7.3 分析报告要有取舍不要堆图最终报告里我没有把 20 张图全部放进去只放了四张每日 GMV 趋势、促销/非促销客单价对比、RFM 四象限分布、商品 ABC 累积曲线。每张图对应的是一句业务结论。比如 RFM 图对应“5.8% 的用户贡献 27% 销售额”商品 ABC 图对应“3.2% 的 SKU 贡献 70% 销售额”。最后的体会分析的价值不在于算出精确结果而在于让业务方看完之后愿意改变某个动作。哪怕结论只是“少给 000 用户发券”也比一份精确但没人看的报表有用。这次用 Python 分析电商销售数据的全过程核心就是用 pandas 把原始订单表整理成可信的数据用可视化和分组聚合找到异常和规律再结合一点简单的预测模型给下个月一个参考区间。整个过程难点不在算法而在对业务场景的理解和对细节的坚持。如果要从这套分析里提炼最想分享的一句经验那就是先搞清楚数据里的每个字段在业务上意味着什么再决定用哪种姿势分析它。
返回列表