Python电商数据分析源码:订单清洗、销售趋势到RFM分层实战 简介面向计算机、自动化等相关专业学生与从业者这份基于Python的电商平台数据分析系统课程作业资源覆盖从数据读取、数据清洗、可视化分析到分析报告输出的全流程并专门处理了支付时间间隔过长、订单金额或支付金额为负等异常业务数据。资源包共30个文件包括7个可运行Python脚本、19张分析结果图表、3个编译缓存文件及1份项目说明文档整体压缩包大小约1.68MB脚本涵盖销售趋势、用户行为、复购率、RFM用户分层、渠道来源等典型分析模块。目前已有883人学习浏览代码均经过运行验证适合作为期末课程设计、课程大作业或毕业设计的参考实现。项目说明文档详细记录了分析思路与关键步骤配合可视化图片可帮助读者快速理解业务逻辑并复现结果具有较高的学习借鉴价值。1. 电商平台数据分析系统为什么这份源码能帮你扛过课程大作业期末课程设计拿到一张几万行的电商订单表要你用 Python 产出销售趋势、复购率、用户行为、渠道来源和 RFM 分层五类分析还要配图表和报告——这是很多计算机、自动化专业学生头疼的事。这份 Python 课程大作业源码正是冲着这个场景来的它把数据分析从读取数据、清洗脏数据到可视化报告的完整链路做成了六个可独立运行的脚本代码经过运行验证拿过来改改数据路径就能出结果。适合两类人一是期末大作业、课程设计需要交完整分析系统的在校生二是不想从零搭分析框架、想直接看成熟实现思路的从业者。它不是万能模板但足以让你少走几个月的弯路。2. 数据读取与字段清洗订单表里每个字段怎么用2020 年数据怎么抽拿到源码先别急着跑第一个坑是数据长什么样。这套系统针对的是电商订单明细表字段在项目说明里列得很清楚编号、订单编号、用户编号、商品编号、订单金额、支付金额、渠道编号、平台类型、下单时间、支付时间、退单拒付标记。所有分析脚本都建立在这 11 个字段之上搞清楚它们再动代码。2.1 字段语义与业务映射id、orderID、userID 到 chargeback 到底怎么用字段别只看字面意思要按业务口径理解。id是行记录唯一标识去重时优先用它orderID是订单号一个订单可能含多个商品所以一个orderID会对应多行userID是用户维度做复购率和用户行为分析时以它为主键。orderAmount是订单原始金额payment是实际支付金额两者差值是优惠或退款导致的脏数据判断时要同时看这两个字段。chanelID和platformType容易混淆chanelID是渠道来源比如 1搜索、2广告、3直接访问platformType是平台类型PC/移动端/小程序。渠道分析看chanelID终端偏好看platformType。orderTime和payTime都是时间字符串后续要转成datetime类型才能做按年、按月统计。chargeback是退款拒付标记数值为 1 表示该笔订单退款做销售趋势时应剔除做用户复购时却要保留订单记录这是最容易算错的地方。2.2 加载数据与时间范围提取只取 2020 年数据的落地写法源码里的公共数据加载逻辑大概是这样的我一般会封装成一个函数避免每个脚本重复写import pandas as pd def load_orders(file_path: str, year: int 2020) - pd.DataFrame: # 读取原始订单表假设原始文件是 CSV编码按实际数据调整 df pd.read_csv(file_path, encodingutf-8) # 把下单时间字符串转成 datetime方便后续按年过滤 df[orderTime] pd.to_datetime(df[orderTime]) # 提取指定年份的数据其余年份直接丢弃 df df[df[orderTime].dt.year year] return df orders load_orders(order_data.csv, year2020) print(orders.shape)逻辑说明pd.to_datetime把字符串列转换成datetime64类型转换后.dt.year才能取出年份。这里用的是year 2020的等值过滤比字符串切片稳定得多也支持后面改成 2020做跨年对比。参数说明file_path是数据文件路径year是目标年份默认 2020如果老师给的是 2021 年数据只需改这一个参数。2.3 脏数据处理支付时间间隔过长、金额为负的过滤逻辑项目说明里专门提到两类脏数据支付时间间隔过长、订单金额与支付金额为负。这两类不处理统计出来的销售总额会严重失真。我见过的常见过滤写法如下def clean_orders(df: pd.DataFrame) - pd.DataFrame: # 复制一份避免修改原始数据 cleaned df.copy() # 计算下单到支付的时间差单位转为小时 cleaned[pay_gap_hours] (cleaned[payTime] - cleaned[orderTime]).dt.total_seconds() / 3600 # 支付时间间隔过长超过 24 小时视为无效订单业务上很少有人下单一天后才支付 cleaned cleaned[cleaned[pay_gap_hours] 24] # 金额为负订单金额和支付金额任何一个小于 0 都剔除 cleaned cleaned[(cleaned[orderAmount] 0) (cleaned[payment] 0)] return cleaned逻辑说明payTime和orderTime在进入函数前必须是datetime类型否则两个时间直接相减会报错。dt.total_seconds() / 3600把时间差转成小时数这样pay_gap_hours就是一个普通数值列能直接跟 24 比较。金额过滤用连接两个条件是因为 DataFrame 的布尔索引不支持and。参数说明24 小时这个阈值是业务假定的如果你们的业务允许付款周期更长可以放宽到 48 小时但一般课程数据不会出现超过 24 小时的合理支付。3. 五大分析脚本拆解SalesTrend、RepurchaseRate、UserBehavior、ChanelSource 与 RFM 的分工这五个脚本名起得非常直白每个对应一类业务分析。它们共用load_orders和clean_orders这两个公共函数彼此之间不依赖对方的输出可以单独运行。下面逐个拆算法和可视化要点你在交报告时能说清楚“每个脚本怎么算的”才算真正掌握。3.1 SalesTrend.py按月销售趋势统计与环比计算销售趋势是最基础也最容易被要求画图的部分。这个脚本按月份聚合订单金额和订单量再算环比增长率。核心代码逻辑import pandas as pd import matplotlib.pyplot as plt def sales_trend(orders: pd.DataFrame) - pd.DataFrame: # 按月统计订单金额总和与订单量使用 payment 而不是 orderAmount trend orders.groupby(orders[orderTime].dt.to_period(M)).agg( total_payment(payment, sum), order_count(orderID, nunique) ).reset_index() # 转成字符串月方便画图当 x 轴 trend[month] trend[orderTime].astype(str) # 计算环比增长率本期 / 上期 - 1 trend[payment_growth] trend[total_payment].pct_change() * 100 return trend逻辑说明groupby传入dt.to_period(M)把时间聚合到月份粒度这是处理日期的常用技巧。聚合时用payment而不是orderAmount因为实际收入以支付金额为准。order_count用nunique对订单编号去重防止一个订单多条商品记录被重复计数。pct_change自动算环比第一期默认是 NaN画图前要填充或忽略。参数说明to_period(M)中的M是月份粒度想按季度看趋势就改成Q按天就改D。3.2 RepurchaseRate.py复购率口径与去重统计复购率是电商分析的经典指标脚本里会同时算“整体复购率”和“按月窗口复购率”。整体复购率的口径是购买次数 ≥ 2 的用户数 ÷ 有购买行为的用户总数。具体实现def repurchase_rate(orders: pd.DataFrame) - float: # 每个用户的购买次数按 userID 分组去重订单号 user_orders orders.groupby(userID)[orderID].nunique() # 至少买过 2 单的用户数 repeat_users user_orders[user_orders 2].count() # 总购买用户数 total_users user_orders.count() return repeat_users / total_users if total_users 0 else 0逻辑说明先按userID分组统计每个用户的去重订单数再筛选出订单数 ≥ 2 的用户。关键点是nunique如果没有它同一订单里买了 3 件商品会被算成 3 次购买复购率直接虚高。分母是user_orders.count()它统计的是有订单记录的用户数而不是订单行数。参数说明这个函数返回一个浮点数你可以直接 print 出百分比也可以在脚本里按月分组循环调用得到每个月的复购率曲线。3.3 UserBehavior.py用户行为路径与高频时段分析用户行为分析在这套源码里重点看两件事一天中的下单高峰时段、用户平均购买间隔。时段分析要把orderTime拆出小时再统计订单分布def hour_distribution(orders: pd.DataFrame) - pd.Series: # 提取下单小时 hour_series orders[orderTime].dt.hour # 按小时分组统计订单量 return hour_series.value_counts().sort_index()逻辑说明value_counts()自动对每个小时出现次数计数sort_index()让结果按 0 点到 23 点排序而不是按数量降序。画图时用柱状图能直接看出晚上 2022 点是高峰。用户行为脚本里还会算相邻订单的间隔天数按用户分组后对orderTime排序再用diff()取差值这个指标能反映用户活跃度。3.4 ChanelSource.py渠道来源贡献与转化漏斗渠道来源分析的核心是算出每个渠道的订单量、支付总额和客单价再决定哪条渠道值得加大投放。常见写法def channel_summary(orders: pd.DataFrame) - pd.DataFrame: summary orders.groupby(chanelID).agg( order_count(orderID, nunique), total_payment(payment, sum), user_count(userID, nunique) ).reset_index() # 客单价 支付总额 / 下单用户数 summary[avg_order_value] summary[total_payment] / summary[order_count] return summary逻辑说明渠道维度下order_count用去重订单号user_count用去重用户数这样能看出渠道的拉新能力。avg_order_value是渠道质量的核心指标如果某渠道订单多但客单价低说明是低价引流渠道。源码里的可视化思路一般是画成饼图或条形图配上一个渠道占比表格。参数说明如果你把chanelID换成platformType这同一个函数就能分析 PC 和移动端的差异。3.5 RFM.py用户价值分层与四象限散点图RFM 是这三个字母对应三个维度R最近一次购买距今的天数、F购买频次、M累计支付金额。脚本先算每个用户的三个值再按中位数打分最后分层。核心实现def rfm_scores(orders: pd.DataFrame, ref_date) - pd.DataFrame: # 以参考日期为基准计算每个用户最近消费距今天数 rfm orders.groupby(userID).agg( R(orderTime, lambda x: (ref_date - x.max()).days), F(orderID, nunique), M(payment, sum) ) # 中位数打分方法高于中位数记高分低于记低分 rfm[R_score] (rfm[R] rfm[R].median()).astype(int) rfm[F_score] (rfm[F] rfm[F].median()).astype(int) rfm[M_score] (rfm[M] rfm[M].median()).astype(int) # 组合成 8 类用户标签 rfm[segment] rfm[R_score].astype(str) rfm[F_score].astype(str) rfm[M_score].astype(str) return rfm逻辑说明R用x.max()取该用户最后一次下单时间再与参考日期相减得到天数天数越小说明越活跃所以R的打分条件是中位数反而得 1 分。F用nunique统计去重订单数M直接求和。最后把三个打分拼接成 3 位字符串例如101表示最近活跃、频次低、金额高。脚本里还会画一张四象限散点图x 轴是F_scorey 轴是M_score点的大小或颜色映射R_score方便你一眼看出重要用户。参数说明ref_date要取订单表最大时间加一天不然最近购买的用户的 R 值为 0并不代表他今天下过单。4. 从零跑通项目环境安装、脚本执行顺序与图表产物对照源码不是一份文档而是六个可以运行的.py文件和几十张结果图。要让它在你电脑上跑出同样的图表环境、数据路径、执行顺序这三个环节缺一不可。这里记录我实际跑通的步骤。4.1 环境要求Python 3.7、pandas、matplotlib、numpy 的安装源码里的__pycache__文件夹和SalesTrend.cpython-37.pyc表明原项目在 Python 3.7 下运行过但 Python 3.83.12 也能兼容只要核心依赖版本不太老。最低依赖是这四个库版本建议用途pandas1.3数据读取、分组聚合numpy1.21数组计算、统计辅助matplotlib3.4折线图、柱状图、散点图openpyxl3.0导出 Excel 结果如果脚本用安装命令一次到位pip install pandas numpy matplotlib openpyxl逻辑说明pandas和numpy是分析基础matplotlib负责出图openpyxl是 pandas 导出 Excel 的后端引擎。如果你的环境是 Anaconda前两个通常自带只需补openpyxl。注意不要用pip install python那不是装 Python 的办法。参数说明如果公司或学校内网限制可以改用pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pandas换国内镜像源速度更快。4.2 执行顺序哪个脚本先跑哪个依赖前一个的输出好消息是这几个脚本互相独立它们都从同一个原始数据文件读数据输出各自的结果图。所以执行顺序没有强依赖但我建议按这个顺序跑因为报告里图表的呈现顺序通常也是这个逻辑python PythonDataAnalyse.py python SalesTrend.py python RepurchaseRate.py python UserBehavior.py python ChanelSource.py python RFM.pyPythonDataAnalyse.py是总入口它可能整合了读取和清洗逻辑先跑它还能验证数据路径是否正确。后面五个脚本分别产出对应主题的图。如果某个脚本报错说找不到order_data.csv就把数据文件放到脚本同目录或改脚本里的绝对路径。这里的执行顺序是给新手兜底用的熟手可以随意乱序跑。4.3 输出物说明png 图表与项目说明.md 的对应关系zip 里大量001.png、003.png、t1.png、t2.png到t6.png、RFM.png之类的图片就是脚本运行后生成的图表。命名规律通常是这样纯数字编号对应主报告中的分析图t开头对应表格式截图脏数据处理方式.png是清洗前后数据对比图。跑完脚本后检查是否生成了新的 png就能判断脚本是否成功。图片命名常见对应分析001.png~011.png销售趋势、用户行为、渠道占比等主分析图t1.png~t6.png数据清洗前后对照、字段说明表格RFM.pngRFM 散点图或分层结果图脏数据处理方式.png脏数据过滤逻辑示意图如果脚本没有自动保存图片可以在每个脚本的plt.savefig(result.png, dpi150)部分手动调整路径。项目说明.md是写给老师看的包含字段说明和分析思路建议把运行结果截图补充进去再交。5. 避坑指南中文乱码、时间字段和负金额这关怎么过这套源码本身能跑通但换成自己的数据或换台电脑踩坑点立马暴露。以下五条是我实际帮助学生调试时遇到过的高频问题按“现象 → 原因 → 解决”列给你。5.1 加载 CSV 报错或中文乱码现象pd.read_csv(order_data.csv)报UnicodeDecodeError或者读出来后表头、渠道名称全是乱码。原因Windows 下 Excel 另存的 CSV 默认是 GBK 编码而 pandas 在 UTF-8 环境下默认用 UTF-8 解码两边不一致导致乱码或直接报错。解决读取时显式指定编码参数。我在源码里见过encodingutf-8的写法如果报错可以改成df pd.read_csv(order_data.csv, encodinggbk, enginepython)enginepython是为了绕过 C 引擎对多字节编码的一些限制GBK 数据通常要带上它才稳定。如果数据是从数据库导出的utf-8-sig也是常见选择它会自动去掉 BOM 头。5.2 时间字段过滤不到 2020 年数据现象按orderTime.dt.year 2020过滤后结果是空表或报AttributeError: str object has no attribute dt。原因orderTime那一列还是字符串类型没有转datetime.dt访问器只存在于 datetime 列上。解决先转换再过滤df[orderTime] pd.to_datetime(df[orderTime]) df[payTime] pd.to_datetime(df[payTime])注意如果原始时间格式是2020-01-05 12:30:00pd.to_datetime能自动识别如果混入了2020/01/05这种斜杠格式也要先统一格式。转换后可以用df.dtypes检查看到datetime64[ns]才算成功。5.3 负金额过滤后数据量骤减结果反而异常现象把订单金额和支付金额为负的行全删掉后销售趋势图出现断崖或月度总额比不过滤时还高。原因数据里可能同时存在“退款订单”和“更正订单”。退款订单的支付金额为负更正订单则是先负后正的一对记录。全删会把更正订单也删了导致正向订单缺失。解决先看chargeback字段退款订单的chargeback通常为 1。按业务规则退款订单保留但不算入销售总额更正订单成对保留。我一般改成这样# 只看支付金额为负但未被标记退款的记录 negative_no_refund cleaned[(cleaned[payment] 0) (cleaned[chargeback] ! 1)] print(negative_no_refund.shape)如果这类记录极少直接删掉如果很多就要查是不是字段含义理解反了。血泪经验动手过滤前先对chargeback做一次计数别让一个字段的误解毁掉整个分析。5.4 图表中文标签显示成方块现象plt.xlabel(月份)出来的标题是一个个空心方块英文正常。原因matplotlib 默认字体是 DejaVu Sans不支持中文字符需要手动指定中文字体。解决在脚本开头统一设置字体import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei, WenQuanYi Zen Hei] plt.rcParams[axes.unicode_minus] False第一行把 sans-serif 字体列表设为常见中文字体第二行关闭 Unicode 负号显示否则负数的负号会变成方块。Linux 服务器上没有SimHei就换成WenQuanYi Zen Hei或安装fonts-noto-cjk。这一行配置能救回所有图表的中文标签。5.5 RepurchaseRate.py 算出来的复购率超过 100%现象打印结果repeat_rate 1.32明显不合理。原因nunique用错了对象可能是按订单行去重算用户数或者没有排除撤销订单。复购率的分子和分母必须以userID为单位而不是订单行。解决检查中间结果user_orders orders.groupby(userID)[orderID].nunique() print(user_orders.describe())如果mean接近 1说明大部分用户只买一单复购率不会超过 30%。看到 100% 以上的数字基本就是分母统计了订单行数需要回头把orders[userID].nunique()作为分母。这个指标做出来超过 40% 就要怀疑口径问题了。6. 进阶验证用 SQL 对拍复购率把 RFM 结果导出成 CSV源码能跑出图只是第一步老师追问一句“你这个复购率怎么算的”你答不上来就露馅了。我习惯把 Python 计算结果导出一份中间表再用 SQL 做交叉验证两边数字对上才算真的完成。这里分享两个我常用的验证技巧。6.1 复购率的两种口径按订单 vs 按用户你用的是哪一种复购率常见的口径有两种按订单口径是“重复购买订单数 ÷ 总订单数”按用户口径是“购买次数 ≥ 2 的用户数 ÷ 总用户数”。课程作业里通常要求的是按用户口径。验证时先确认RepurchaseRate.py里用的是哪一种然后在 SQL 里复现同样口径SELECT COUNT(CASE WHEN buy_count 2 THEN 1 END) * 1.0 / COUNT(*) AS repeat_rate FROM ( SELECT userID, COUNT(DISTINCT orderID) AS buy_count FROM orders WHERE orderTime 2020-01-01 AND orderTime 2021-01-01 GROUP BY userID ) t;这段 SQL 的逻辑和 Python 里的groupby(userID)[orderID].nunique()完全一致COUNT(DISTINCT orderID)对应nuniqueCASE WHEN对应 2的筛选。如果两边结果差距超过 0.5%就去看 Python 是否多删了退款订单或者 SQL 表里混入了测试数据。逻辑说明* 1.0是为了让除法结果是浮点数避免 SQLite 里整数相除得到 0。6.2 用 SQL 或 Excel 交叉验证 Python 输出除了复购率销售趋势也可以验证。把SalesTrend.py结果导出为 CSV然后在 Excel 里做数据透视表行是月份值是支付金额再对比 Python 折线图的每个点。如果某个月对不上优先查时间边界——比如 SQL 里用了 AND 而 Python 用了dt.year 2020两边都符合如果 Python 里用了 2020-12-31而时间精度带上23:59:59就会多出最后一秒的订单。这种误差很难肉眼发现但透视表一拉就暴露了。建议在脚本末尾统一加一行导出trend.to_csv(sales_trend_check.csv, indexFalse, encodingutf-8-sig)utf-8-sig导出的 CSV 在 Excel 里打开不会乱码这个参数值得记住。6.3 把 RFM 结果导出为 CSV 做二次分析RFM 算出来的 8 类用户标签放在 DataFrame 里不直观。我一般会把它导出成 Excel再利用数据透视表看每类用户的支付金额占比rfm rfm_scores(orders, ref_dateorders[orderTime].max() pd.Timedelta(days1)) rfm.to_excel(rfm_scores.xlsx, sheet_nameRFM) # 按分层汇总用户数和金额 segment_stats rfm.groupby(segment).agg( user_count(userID, count), total_m(M, sum) ).reset_index() segment_stats.to_csv(segment_stats.csv, indexFalse)导出后用 Excel 拉一个柱状图哪类用户贡献了最大金额一清二楚。我自己的习惯是每次跑完分析都先对拍两张验证表再开始写报告的结论部分。曾经有一次偷懒跳过验证直接把 Python 输出的复购率写进报告结果老师拿 SQL 一查差了 3 个百分点原因是我忘了过滤chargeback 1的退款订单。从那以后我每次做完分析都强制走一遍对拍流程先导 CSV 再讲结论这个习惯帮我躲过了不少翻车。希望帮到你。本文还有配套的精品资源点击获取