☰
Python电商数据分析系统实战:从数据清洗到RFM用户分层
2026/9/27 23:04:27 网站建设 项目流程

简介:面向Python课程设计与期末大作业的电商平台数据分析系统完整资源,适合需要可直接运行项目作为参考的高校学生。资源围绕电商业务数据设计,实现了用户行为分析、RFM用户分层、回购率统计、销售趋势分析、渠道来源拆解等常见模块,覆盖数据分析课程中的典型实验场景;同时附带脏数据处理方式说明和README文档,便于理解数据清洗、特征计算与可视化输出的完整流程。压缩包共30个文件,以7个Python源码脚本为主体,实现数据清洗、特征分析与图表绘制;辅以19张结果展示图与3个缓存文件,整体仅1.68MB,轻量易部署;源码经本地编译通过,评审分98分,助教审定,难度适中,可直接用于期末大作业、课程设计或毕业设计参考。目前已有197人学习浏览,作为高分项目范例,能帮助读者快速搭建电商数据分析框架并借鉴实现思路。

1. 为什么电商数据分析是大作业里的“安全牌”:这个题到底在考你什么

Python 大作业电商平台数据分析系统实现源码+文档说明(高分项目)这类题目,几乎每个开 Python 课的学校都会出现一版。它不是让你做一个能下单的商城,也不是让你训练一个预测模型,而是要把一整条数据处理链路走通:从拿到或构造订单数据,到清洗、聚合、算指标,再到画图、写结论、打包成一份能答辩的文档。评分老师看的就是“你是不是真的会分析,而不是会抄代码”。

这个方向适合两类人:一类是课程要求必须做“信息系统类”大作业,想找一个数据量可控、工作量肉眼可见的题目;另一类是求职作品集里缺一个数据展示项目,想用半天到两天时间补上一个完整案例。它不依赖 GPU、不依赖真实业务接口,一台普通笔记本就能跑完。前面把技术栈和目录结构定好,后面就是照方抓药的事。

2. 技术选型与工程结构:先摆平“用什么”和“怎么放”,再谈写代码

2.1 技术栈决策:纯 Python 脚本还是带 Web 壳

先回答一个最常见的问题:这个系统到底要不要做成网页?很多高分模板会给你一个 Flask 页面,打开能看到图表和数据表格,但这不代表你必须这么做。我见过两种做法,各有各的道理:

方案组成适合情况答辩风险
纯脚本版pandas 清洗 + matplotlib 出图 + 控制台输出结论课程只要求“分析报告 + 图表”功能偏少,容易被问“系统在哪里”
带 Web 壳版脚本版基础 + Flask 渲染页面 + ECharts 图表课程要求“系统”,或展示给非技术 audience工作量多一天,但效果显著

我的建议是:如果老师明确说了“系统”两个字,就做带 Web 壳的版本。这里的“系统”在答辩语境下就等于“有界面、能交互”。但如果时间只够一天,脚本版也能过,只要文档里写清楚“数据入库—清洗—分析—可视化”四层结构,把脚本输出截图放进文档,观感并不差。下文默认按脚本版为主、最后补 Web 壳的方案讲,这样两头都能落。

技术栈选型上没有悬念:pandas 做数据清洗和聚合,matplotlib 做静态图表,Flask 只用来把结果展示成网页。不用 Django,因为只暴露几个路由,Django 的 admin、ORM、迁移机制在这里全是负资产。数据库用 SQLite 还是直接 CSV?如果你没学过 SQL,直接用 CSV + pandas.read_csv 就行,减少一个变量;如果你会用 pandas 的 merge、groupby,数据量在几万行以内时 CSV 完全不输数据库。

2.2 目录设计与文档联动:源码和文档说明怎么配套

很多人的项目只有一个 main.py,跑完输出几张 png,这在小作业里可以,但在“系统 + 文档说明”的评分结构下很吃亏。我一般会把工程拆成这样,你直接照着建:

ecommerce_analysis/ ├── data/ │ ├── raw_orders.csv # 原始订单数据,不手动改 │ └── clean_orders.csv # 清洗后数据,分析只用这份 ├── scripts/ │ ├── generate_data.py # 造数脚本,保证可复现 │ ├── clean_data.py # 清洗脚本 │ ├── analysis.py # 核心指标计算 │ └── visualize.py # 生成图表 ├── output/ │ ├── daily_sales.png │ ├── top10_products.png │ ├── rfm_heatmap.png │ └── metrics_summary.json # 指标汇总,Web 版直接用 ├── web_app/ │ └── app.py # Flask 入口(可选) ├── docs/ │ └── 说明文档.md └── requirements.txt

每个文件只干一件事,是这套结构能避免答辩翻车的核心。比如 generate_data.py 只负责产出 raw_orders.csv;clean_data.py 只读 raw,绝不回头改源文件;analysis.py 输出 JSON 而不是直接 print 一眼就过。这样文档里写“各模块职责”时你就有话可说,而不是笼统的一句“主程序完成所有功能”。

requirements.txt 也必须有,这是文档能复现的前提:

pandas==2.1.4 matplotlib==3.8.2 flask==3.0.0 openpyxl==3.1.2

2.3 环境准备与本机跑通最小命令

别用系统全局 Python,大作业最大的隐性扣分项就是“换个环境跑不起来”。用虚拟环境隔离,也能在文档里写上“virtualenv 创建独立运行环境”这一句加分话。

cd ecommerce_analysis python -m venv venv # Windows: venv\Scripts\activate # macOS / Linux: source venv/bin/activate pip install -r requirements.txt

说明:python -m venv venv 创建虚拟环境到 venv 目录,激活后 pip 装的包只对当前项目生效。装依赖用 -r 从 requirements.txt 读取,避免手工逐个装漏掉。如果你用的是 PyCharm,创建项目时可以直接选 Virtualenv,效果一样。到这一步,环境能跑通,后面的流程就都是确定性的了。

3. 数据准备与清洗:没有干净数据,后面全是废图

3.1 造一份可复现的模拟订单数据:随机种子决定你报告里的“真实性”

电商平台的真实订单数据拿不到,也绝不能拿爬虫去搞。最常见、最可靠的做法是自己造一份带业务含义的模拟数据。造数不是随便 random,要让分布大致合理:晚上和周末订单多、爆品销量高但单价低、数码类客单价明显高于零食类。这样你分析出来的结论才像话。

import random import pandas as pd from datetime import datetime, timedelta random.seed(42) # 商品池:不同品类给不同价格区间 products = [ {"name": "无线鼠标", "category": "数码配件", "min_price": 49, "max_price": 89}, {"name": "机械键盘", "category": "数码配件", "min_price": 199, "max_price": 499}, {"name": "手机支架", "category": "数码配件", "min_price": 9, "max_price": 29}, {"name": "保温杯", "category": "家居日用", "min_price": 39, "max_price": 129}, {"name": "收纳箱", "category": "家居日用", "min_price": 25, "max_price": 79}, {"name": "零食礼包", "category": "食品", "min_price": 29, "max_price": 99}, ] # 时间范围:近 90 天 start_date = datetime(2024, 1, 1) end_date = datetime(2024, 3, 31) rows = [] for order_id in range(1, 3001): # 周末和晚上下单概率更高 day_offset = random.randint(0, (end_date - start_date).days) order_time = start_date + timedelta(days=day_offset, hours=random.randint(10, 22), minutes=random.randint(0, 59)) if order_time.weekday() >= 5: weight = 2 # 周末订单翻倍 else: weight = 1 if 19 <= order_time.hour <= 22: weight += 1 # 晚间再加权 for _ in range(random.randint(1, 3)): # 一单可能含多件商品 product = random.choice(products) qty = random.randint(1, 3) if weight > 1 else random.randint(1, 2) price = round(random.uniform(product["min_price"], product["max_price"]), 2) rows.append({ "order_id": order_id, "user_id": random.randint(1000, 1500), "product_name": product["name"], "category": product["category"], "quantity": qty, "price": price, "amount": round(price * qty, 2), "order_time": order_time.strftime("%Y-%m-%d %H:%M:%S"), }) df = pd.DataFrame(rows) df.to_csv("data/raw_orders.csv", index=False, encoding="utf-8-sig") print(f"生成 {len(df)} 条订单明细")

逻辑说明:random.seed(42) 固定随机种子,保证每次运行生成完全相同的数据。你文档里写的所有数值都来自这份确定性的数据,否则两次运行结果不同,答辩时老师让你重跑一遍就对不上。weight 变量模拟业务权重,周末权重为 2、晚间额外加 1,但注意它只影响购买件数概率,不是直接给金额乘系数——这只是为了让数据分布更像真实订单,不必做成精确的概率模型。

参数说明:订单数 3000,用户数 501(1000-1500),时间范围 90 天。这三个参数决定你后续图表的密度:3000 条明细画日趋势足够饱满,501 个用户做分层也够。如果你的报告需要月度对比,就把日期范围改成 6 个月并把订单数加到 6000 以上。生成 CSV 时用 encoding="utf-8-sig",这是给 Excel 用的编码,Excel 直接打开不会乱码——很多教程只写 utf-8,Windows 上 Excel 打开就是一堆乱码,这一步值得记住。

3.2 清洗三步:去重、补空、拒掉异常订单

造出来的数据也要走一遍真实清洗流程,这是文档里“数据预处理”章节的素材。清洗本身不在于数据有多脏,而在于你展示了“我知道这些坑存在并做了处理”。

import pandas as pd df = pd.read_csv("data/raw_orders.csv") # 1. 去重:同一订单号加同一商品名才算重复 df.drop_duplicates(subset=["order_id", "product_name"], inplace=True) # 2. 缺失值:金额为空或用户为空的行直接剔除 df.dropna(subset=["amount", "user_id"], inplace=True) # 3. 异常值:数量<=0、金额<=0、单价>9999 都视为脏数据 df = df[(df["quantity"] > 0) & (df["amount"] > 0) & (df["price"] < 9999)] # 4. 时间转 datetime,方便后续按天/月聚合 df["order_time"] = pd.to_datetime(df["order_time"]) df["order_date"] = df["order_time"].dt.date df.to_csv("data/clean_orders.csv", index=False, encoding="utf-8-sig") print(f"清洗前 {len(df)} 行,清洗后保留 {len(df)} 行")

逻辑说明:drop_duplicates 的 subset 参数限定判断重复的列,不要全列去重——同一个订单含两条不同商品是正常业务,全列去重会误删。dropna 只针对关键字段,因为商品名缺失可能不影响金额汇总,而 user_id 缺失会导致后续 RFM 分析无法做用户分组。异常值过滤用布尔条件组合,这里不用 replace,直接剔除是更干净的做法。

参数说明:price < 9999 是防呆阈值,你可以按业务调整为 100000,关键是它必须比商品池里的最高单价大一个量级。清洗完的行数建议和原始行数对比,在文档里写“共剔除 N 行无效数据”,这就是数据质量的量化证明。上面这段代码只是标准流程,如果你自己模拟数据时没生成缺失值,可以在 generate_data.py 里留 5% 的空金额字段——故意制造脏数据再清洗,报告反而更有说服力。

3.3 Excel 落盘与对账:拿 Excel 透视表当裁判

清洗后的数据除了存 CSV,我会再导出一份 Excel,目的是给文档附件用,也方便老师自己打开筛选。这不是冗余,而是让审核者能亲手核对你算的每个数。

import pandas as pd df = pd.read_csv("data/clean_orders.csv") # 按天汇总销售额,写入 Excel 的 sheet1 daily = df.groupby("order_date")["amount"].sum().reset_index() monthly = df.groupby(df["order_time"].dt.month)["amount"].sum().reset_index() with pd.ExcelWriter("output/agg_tables.xlsx", engine="openpyxl") as writer: daily.to_excel(writer, sheet_name="日销售额", index=False) monthly.to_excel(writer, sheet_name="月销售额", index=False)

代码说明:groupby 聚合后要 reset_index,如果不重置,order_date 会变成索引列,to_excel 写出来会带一层奇怪的行名。pd.ExcelWriter 配合 with 语句能确保文件写完后关闭句柄,不然在 Windows 上文件可能被占用,第二次运行报 PermissionError。

参数说明:engine="openpyxl" 是写 xlsx 必需的后端,xls 老格式是 xlwt,别混用。这个 Excel 文件生成后,你可以在 Excel 里插入透视表拉一个“日销售额”,和程序算出来的数对一遍——对上了,说明 groupby 逻辑没问题;对不上,优先检查 order_date 是否真的转了 datetime 类型。这一步对账花五分钟,能避免“图表数据和描述文字对不上”这种最尴尬的答辩事故。

4. 分析指标与可视化:从平均值到 RFM 的完整链路

4.1 时间趋势分析:先 resample 再画线,别直接 plot 原始数据

做完清洗,第一个必做的分析是时间趋势。我见过很多人把 3000 行订单直接按天 groupby 后画线,画出来锯齿状极其难看。正确做法是按天聚合后再做时间序列重采样,把“天数”这个单位统一。

import pandas as pd import matplotlib.pyplot as plt df = pd.read_csv("data/clean_orders.csv") df["order_time"] = pd.to_datetime(df["order_time"]) df.set_index("order_time", inplace=True) # 按天求和,再取 7 日均线平滑 daily = df["amount"].resample("D").sum() weekly = daily.rolling(window=7, min_periods=1).mean() plt.rcParams["font.sans-serif"] = ["SimHei", "Microsoft YaHei"] plt.rcParams["axes.unicode_minus"] = False fig, ax = plt.subplots(figsize=(12, 5)) ax.plot(daily.index, daily.values, label="日销售额", color="#888", linewidth=0.8) ax.plot(weekly.index, weekly.values, label="7日均线", color="#E63946", linewidth=2) ax.set_title("近90天销售额趋势") ax.legend() plt.tight_layout() plt.savefig("output/daily_sales.png", dpi=150)

逻辑说明:resample("D") 是把索引为时间的数据重采样到天,和单纯的 groupby 日期不同,它会把没有订单的日期也补成 0,画出来是一条连续的时间轴,不会有断点。rolling(window=7) 取 7 天滑动平均,把周内波动抹平,趋势线才看得出整体方向——这是报告里“销售额整体呈上升趋势”这句话的唯一依据。

参数说明:figsize=(12, 5) 长宽比适合在文档里插入,不会过高占版;dpi=150 是打印清晰度的底线,低于 100 放到答辩 PPT 里会明显发虚。字体设置里 SimHei 是黑体,Microsoft YaHei 是微软雅黑,前者在 Linux 上通常没有,所以列两个备选,系统按顺序找。

4.2 Top10 商品与二八法则:用条形图把差异拉出来

趋势图证明“总量”,接下来必须展示“结构”。Top10 商品贡献了多少销售额,在电商报告里几乎必提。这一步能引出二八法则的分析结论,让你的报告从“罗列图表”升级到“有观点”。

import pandas as pd import matplotlib.pyplot as plt df = pd.read_csv("data/clean_orders.csv") top10 = (df.groupby("product_name")["amount"] .sum() .sort_values(ascending=False) .head(10)) total_sales = df["amount"].sum() top10_share = top10.sum() / total_sales * 100 fig, ax = plt.subplots(figsize=(10, 6)) ax.barh(top10.index[::-1], top10.values[::-1], color="#2A9D8F") ax.set_xlabel("销售额(元)") ax.set_title(f"Top10 商品销售额(贡献占比 {top10_share:.1f}%)") plt.tight_layout() plt.savefig("output/top10_products.png", dpi=150) print(top10)

逻辑说明:sort_values(ascending=False) 按销售额降序排,head(10) 截前 10。barh 是横向条形图,类别名称长时横向更易读。这里用 top10.index[::-1] 反转顺序让最大值在顶部,如果不反转,matplotlib 默认从底部往上排,最大项反而不在最显眼的位置。

参数说明:top10_share 是 Top10 销售额占总销售额的百分比,这个数通常是报告里最有冲击力的一个数字。例如算出来可能是 45% 左右,你就可以在文档里写“Top10 商品贡献了约 45% 的营收,头部商品集中度明显”——有数据有判断,这就是高分项目和普通作业的差别。

4.3 RFM 用户分层:找出“高价值沉默用户”

用户分层是电商数据分析系统的标配内容,也是拉开档次的模块。RFM 三个字母分别代表最近一次消费时间、消费频率、消费金额。实现不复杂,但必须讲清理由:你为什么要分层?因为运营资源有限,得优先服务值得服务的用户。

import pandas as pd df = pd.read_csv("data/clean_orders.csv") ref_date = df["order_time"].max() + pd.Timedelta(days=1) # 按用户聚合 RFM 三指标 rfm = df.groupby("user_id").agg( recency=("order_time", lambda x: (ref_date - x.max()).days), frequency=("order_id", "count"), monetary=("amount", "sum") ) # 四分位数分档:数值高于中位数的记为 1,否则 0 def score(series): return (series > series.median()).astype(int) rfm["R"], rfm["F"], rfm["M"] = score(rfm["recency"]), score(rfm["frequency"]), score(rfm["monetary"]) # 分层规则:8 类用户合并成 4 类主型 def rfm_label(row): if row["R"] == 1 and row["F"] == 1 and row["M"] == 1: return "重要价值用户" if row["M"] == 1: return "高消费潜力用户" if row["R"] == 1: return "活跃普通用户" return "沉默风险用户" rfm["label"] = rfm.apply(rfm_label, axis=1) rfm.to_csv("output/rfm_result.csv", encoding="utf-8-sig") # 看一眼分层结果 print(rfm["label"].value_counts())

逻辑说明:recency 用 ref_date 减去每个用户最近一次下单日期,得到“距今多少天没消费”;frequency 用 order_id 出现次数统计,注意 agg 里第一个元组 ("order_time", lambda ...) 表示对 order_time 列应用 lambda,("order_id", "count") 表示对 order_id 计数——字段映射别写反。四分位数分档是 RFM 最常见实现:数值大于全体中位数为 1,否则为 0。阈值用中位数而不是平均值,因为消费金额分布通常右偏,平均值会被大额用户拉高,中位数更稳。

参数说明:ref_date 取数据最大时间 + 1 天,确保“最近消费”是相对完整日期区间计算的。分层规则这里只写了 4 类,完整 RFM 是 2³=8 类,但我建议报告里合并成 4 类,因为 8 类会分散用户数,答辩时解释成本高。你可以把 rfm_result.csv 导入 Excel 筛选某个县体用户 ID,并去明细表里验证这个用户的真实消费记录——能验证,说明分层不是黑匣子。

4.4 热力图与图表保存:可视化配置统一入口

图表风格统一是“高分项目”的外观底线。如果每张图颜色、字体、尺寸都不一样,老师扫一眼就觉得是拼凑的。我会把可视化配置抽到一个公共函数,然后热力图用 seaborn 风格画出来展示 RFM 分布。

import pandas as pd import matplotlib.pyplot as plt # 统一图表风格 plt.rcParams["figure.figsize"] = (10, 6) plt.rcParams["axes.grid"] = True plt.rcParams["grid.linestyle"] = "--" plt.rcParams["grid.alpha"] = 0.4 plt.rcParams["font.sans-serif"] = ["SimHei", "Microsoft YaHei"] plt.rcParams["axes.unicode_minus"] = False rfm = pd.read_csv("output/rfm_result.csv") # 画 R、F、M 三个分档的平均值雷达式对比 summary = rfm.groupby("label")[["recency", "frequency", "monetary"]].mean() # 对数值归一化到 0-1,避免金额数值过大压扁其他轴 summary_norm = (summary - summary.min()) / (summary.max() - summary.min()) fig, ax = plt.subplots(figsize=(8, 6)) im = ax.imshow(summary_norm.T, cmap="YlOrRd", aspect="auto") ax.set_xticks(range(len(summary_norm.index))) ax.set_xticklabels(summary_norm.index, rotation=30) ax.set_yticks(range(3)) ax.set_yticklabels(["最近消费", "消费频率", "消费金额"]) plt.colorbar(im, ax=ax, label="归一化均值") plt.tight_layout() plt.savefig("output/rfm_heatmap.png", dpi=150)

逻辑说明:imshow 把数值矩阵渲染为热力图,cmap="YlOrRd" 从黄到红渐变,数值越大颜色越深。summary_norm.T 做转置,让每一列是一个用户类型、每一行是一个指标,这样竖向看某个用户类型的三项指标强弱更直观。归一化到 0-1 是必要的,否则 monetary 均值几千,recency 均值几十,两者没法在同一张图上比较颜色深浅。

参数说明:aspect="auto" 让单元格自动铺满画布,不加这个参数热力图会被拉成正方形导致变形。rotation=30 让用户类型的标签斜着排,避免文字相互遮挡。这一张热力图配合上一节的柱状图、趋势图,整套图表组合已经覆盖“时间、结构、人群”三个维度,文档里每个分析结论都能落到图上。

5. 避坑:会毁掉分数的 5 个常见问题

5.1 中文乱码与方块字:编码问题现场

现象:matplotlib 画出的图里,所有中文标题变成方框;CSV 用 Excel 打开后中文全是乱码;或者读 CSV 直接报 UnicodeDecodeError。

原因:三个环节各有差异。matplotlib 默认字体不支持中文;pandas to_csv 默认 utf-8,而 Excel 默认 GBK 打开;pandas read_csv 默认按系统编码读取,Windows 下可能是 GBK。

解决:matplotlib 在 pyplot 画图前设置 plt.rcParams["font.sans-serif"] = ["SimHei", "Microsoft YaHei"] 和 plt.rcParams["axes.unicode_minus"] = False;CSV 写入统一 encoding="utf-8-sig";读取时如果报错,显式指定 pd.read_csv(path, encoding="utf-8"),还是报错就试试 encoding="gbk"。这个坑最值得提前规避,因为乱码图一旦生成,只能重跑,没有后悔药。

5.2 日期轴变成密集黑线:没做重采样

现象:画日销售额趋势时,横轴密密麻麻挤成一团,线条杂乱看不出规律。

原因:直接把每行的订单时间作为横轴绘图,没有先 resample 到天,导致同一日期出现多条记录,或者日期格式不统一。另一个原因是日期索引没有排序,matplotlib 按出现顺序画线,线就来回折。

解决:先 df["order_time"] = pd.to_datetime(...),然后 set_index("order_time"),再 resample("D").sum()。resample 会自动按时间排序并补齐空日期。如果只想显示 1 月到 3 月,可以先用 df.loc["2024-01-01":"2024-03-31"] 切片再聚合。

5.3 图糊了或被截断:dpi 和 tight_layout 同时生效

现象:保存的 png 放到文档里模糊,或图例、标题被截掉一半。

原因:dpi 低于 100,图在文档里被拉伸后像素不够;figsize 设置过大、内容超出画布边界。

解决:保存时统一 dpi=150,画布 figsize 控制在 (10, 6) 到 (12, 6) 之间。打印前调 plt.tight_layout(),它会自动收缩留白,避免标题被子图遮挡。生成后先本地打开看一眼,别直接插进文档——图被截断这一项,几乎每个班都有几个人中招。

5.4 造数“太假”:分布不合理被老师当场问住

现象:答辩时老师说“你这数据怎么每天销售额都一样高?”或者“凌晨 3 点还在疯狂下单?”

原因:random.randint 均匀生成时间,没有模拟真实人群作息;商品价格区间太窄,导致销量和金额高度线性相关。

解决:generate_data.py 里对时间加权——晚上和周末提高下单概率,工作日上午 10 点到下午 4 点保持常规,凌晨直接降低到接近零。价格区间要拉开差距,数码配件定价几十到几百,食品就几块到几十。还可以再往数据里加 3% 的退款记录(amount 为负),清洗时剔除,这样报告里就能写“剔除了 N 条退款异常数据”,真实性明显提升。

5.5 文档和代码对不上:换个电脑跑不出来

现象:代码在你自己电脑上运行正常,拷到老师机器上 ImportError 或者路径报错。

原因:依赖没锁版本;用绝对路径读文件;中文文件名在 macOS 和 Windows 编码不一致。

解决:所有文件路径用相对路径,比如 "data/clean_orders.csv",不要写 "C:/Users/xxx/Project/..."。requirements.txt 锁住 pandas、matplotlib、flask 的版本号。跑之前确认当前工作目录在项目根目录,而不是在子目录里执行脚本。最后用 Python 的 ifname== "main": 包住所有执行代码,防止被 import 时意外跑一遍。这三件事做齐,换环境基本不会再翻车。

6. 进阶技巧:把静态图升级成数据看板,再用一张表自证没算错

如果还想往上够一够,可以把 matplotlib 的 png 换成网页看板,工作量不大但观感提升明显。Flask 里写一个路由,读取 analysis.py 输出的 metrics_summary.json,把日销售额、Top10、RFM 分布渲染成 ECharts 图表。关键在于:Flask 只做数据中转,不做计算,所有指标都在 analysis.py 里算好,网页端只是换一种形式展示。我一般会在 app.py 里写一个 /dashboard 路由,返回一个模板页,页面上的图表数据全部来自 JSON 文件——这样你可以在答辩现场刷新页面给老师看,比翻 png 图片有说服力得多。

计算正确性上,最后务必跑一次对账脚本:把 analysis.py 算出的月度总销售额,和 agg_tables.xlsx 里 Excel 透视表的数核对。对不上,优先查 groupby 是否漏了过滤条件;对得上,文档里可以加一句“输出结果已与 Excel 透视表交叉验证”。这套流程做完,你会发现自己对数据链路的理解比照着教程敲二十遍都有用。我当年做类似项目时就是在 RFM 分层上吃了亏——阈值用平均值而不是中位数,导致所有用户都被归到“沉默风险”,答辩时被问得说不出话,后来习惯所有分箱先画分布图再定阈值,这个习惯一直留到现在。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询