电商平台数据分析系统Python实现:从数据清洗到可视化全流程解析

发布时间:2026/10/12 14:47:17
电商平台数据分析系统Python实现:从数据清洗到可视化全流程解析 简介这是一套基于Python实现的电商平台数据分析系统课程大作业源码包面向计算机、自动化及相关专业的学生适用于期末课程设计、课程大作业、毕业设计等场景。项目围绕电商订单数据展开涵盖数据读取、数据清洗、数据分析与可视化全流程内置用户行为分析、销售趋势、渠道来源分析及RFM客户价值分群等典型模块可帮助学习者快速理解数据分析项目的整体架构与实现思路。资源共30个文件以7个Python源码文件为核心配套19张分析结果图片、1份项目说明文档及编译缓存文件压缩包仅1.68MB结构轻巧便于直接运行与二次开发。目前已有883人学习下载代码经过运行验证能够正常执行适合希望以完整项目范式完成课程作业或毕业设计的高校学生参考借鉴。1. 电商平台数据分析系统这门大作业到底在做什么期末拿到一个压缩包名字叫“Python课程大作业电商平台数据分析系统Python实现源码使用教程说明.zip”解压后是一堆.py文件和几个 CSV 数据表。这是很多电商、大数据专业学生都会撞上的课程设计题目用 Python 实现一套能从原始订单数据里跑出销售报表、用户画像、商品结构分析的完整系统。这套源码的价值在于它不是某一段算法的玩具 demo而是把数据导入、清洗、分析、可视化整条链路串了起来。你拿到后只要环境配好、路径改对就能跑出一个能写进报告、能在答辩时现场演示的分析系统。本文就从实际复现的角度把这个系统拆开讲清楚哪些代码是核心哪些参数必须调哪些地方会莫名其妙翻车。2. 环境准备与数据约定拿到源码后先解决这三件事大多数课程大作业的源码都会默认“你机器上什么都有”但现实中光一个 Python 版本就能卡你半小时。所以第一步不是急着读代码而是先把运行环境、数据文件、配置路径这三件事搞清楚。2.1 Python 版本与依赖避免在环境问题上浪费时间先确认你的 Python 版本。这个项目用到的库主要是 pandas、numpy、matplotlib、flask 这几个它们对 Python 版本有兼容性要求。我的建议是使用 Python 3.8 到 3.10 之间的版本我自己跑这类课程项目习惯用 3.9。Python 3.11 以上某些旧版本 pandas 的 wheel 包可能装不上而 Python 3.7 以下又对 f-string 和部分语法支持不友好。# 创建独立虚拟环境避免污染系统 Python python -m venv venv # Windows 激活方式 venv\Scripts\activate # macOS / Linux 激活方式 source venv/bin/activate # 安装依赖 pip install -r requirements.txt如果压缩包里没有requirements.txt手动安装核心依赖pip install pandas numpy matplotlib flask openpyxl这里的openpyxl容易被忽略它是 pandas 导出 Excel 文件的后端引擎。如果你没有装它执行df.to_excel()时会直接报ModuleNotFoundError: No module named openpyxl。装依赖速度慢的话可以在 pip 命令后面加-i https://pypi.tuna.tsinghua.edu.cn/simple换成清华镜像源。装完后用pip list看一眼版本号pandas 版本最好在 1.3 到 2.1 之间太新或太旧都可能触发一些兼容性警告。2.2 数据源约定CSV 的目录结构与字段名压解包后先看data/目录下有哪些文件。常见做法是把订单表、商品表、用户表分开存放我见过最多的三种命名orders.csv、products.csv、users.csv或者中文名订单数据.csv、商品数据.csv。无论哪种你都要先打开看一眼字段名因为源码里的列名是硬编码的字段名对不上就会在执行到一半时报KeyError。我用表格列一下典型字段你拿到数据后对号入座文件常见字段说明订单表order_id, user_id, product_id, order_date, amount, quantity, statusstatus 用于区分有效订单和退款订单商品表product_id, category, price, cost价格与成本用于毛利分析用户表user_id, gender, age, city, register_date用于用户画像读取时建议使用utf-8-sig编码而不是utf-8。很多课程大作业的数据文件是用 Excel 另存为 CSV 的Windows 下这类文件会带 BOM 头直接用utf-8读会导致第一列列名变成\ufefforder_id后面所有按order_id的筛选都会失效。import pandas as pd # 用 utf-8-sig 读取兼容带 BOM 的 CSV 文件 orders pd.read_csv(data/orders.csv, encodingutf-8-sig) products pd.read_csv(data/products.csv, encodingutf-8-sig) users pd.read_csv(data/users.csv, encodingutf-8-sig) print(orders.columns.tolist()) # 打印列名确认没有 \ufeff 前缀 print(orders.head(3))如果打印出的列名里第一个元素是\ufefforder_id说明你用了utf-8改成utf-8-sig即可。这一步看起来小但很多同学在数据清洗章节卡住的根源就是这个看不见的字符。2.3 修改配置文件路径与输出目录多数项目会单独放一个config.py或settings.py用来集中管理文件路径。这是好事意味着你不需要在十几个.py文件里逐个找路径字符串。你只需要把DATA_DIR改成你本机解压后的实际路径其他模块都会从这里读。# config.py import os BASE_DIR os.path.dirname(os.path.abspath(__file__)) DATA_DIR os.path.join(BASE_DIR, data) OUTPUT_DIR os.path.join(BASE_DIR, output) REPORT_DIR os.path.join(BASE_DIR, report) # 如果输出目录不存在自动创建 for d in [OUTPUT_DIR, REPORT_DIR]: if not os.path.exists(d): os.makedirs(d)注意os.path.abspath(__file__)是根据配置文件所在位置计算的这样你把整个项目文件夹换个位置也不用改路径。如果你把config.py里的路径写死成C:\Users\yourname\Desktop\...换一台电脑或者移动文件夹后就会全部失效。这也是一个常见翻车点源码在作者机器上能跑到你机器上报FileNotFoundError。3. 数据清洗与加工把原始记录变成可分析的表原始订单数据基本不能直接用比如日期字段是字符串、金额列混入了空值、订单状态五花八门。数据清洗是这套系统的地基地基不稳后面所有统计都是错的。3.1 缺失值与异常值的处理逻辑拿到数据第一件事不是算平均值而是看每个字段缺失了多少。pandas 里最方便的命令是isnull().sum()一行就能看出哪些列不干净。# 统计各列的缺失值数量 print(orders.isnull().sum()) print(orders.describe()) # 删除关键字段为空的记录 orders orders.dropna(subset[order_id, amount, order_date]) # 非关键字段的缺失用填充策略处理 orders[quantity] orders[quantity].fillna(1) # 过滤金额异常值小于等于0的订单视为异常 orders orders[orders[amount] 0]逻辑说明dropna(subset...)只检查指定列不会因为某个无关字段为空就删掉整行。fillna(1)在处理数量字段时比较合适因为购物车里缺失数量通常默认为 1 件。amount 0这个条件专门过滤退款单和测试订单但注意退款单如果单独有 status 标记最好用 status 过滤而不是金额过滤因为部分退款金额可能是负数但也有正常退款是正数但状态为“已退款”的情况。参数这里最值得调的是dropna的 subset 列表你到底信任哪些字段作为订单有效性依据就把哪些字段放进去。3.2 日期规范化字符串日期是最容易坑人的类型CSV 里的日期往往长这样2024-11-11 23:15:32也有可能是2024/11/11甚至11-11-2024。pandas 的to_datetime能自动解析大部分常见格式但你得让它知道“这一列是日期”否则它默认当成字符串处理排序会按字典序排——2024-09-30会排在2024-10-01后面因为字符串比较到第三位时9大于1。# 转成 datetime 类型无法解析的值转为 NaT orders[order_date] pd.to_datetime(orders[order_date], errorscoerce) # 删除解析失败的脏数据 orders orders.dropna(subset[order_date]) # 设置成索引后后面按天聚合会非常方便 orders orders.set_index(order_date).sort_index() # 打印时间范围确认数据覆盖周期 print(orders.index.min(), orders.index.max())errorscoerce这个参数是后悔药它会将不能解析的字符串转成NaTNot a Time而不是直接抛异常。如果你不加这个参数一条脏数据就能让整个程序中断。设置索引后用orders.resample(D)这类操作会顺畅很多但要注意如果后续还要按order_id关联其他表索引不会影响合并因为 merge 默认按列名关联。3.3 构建宽表把订单、商品、用户三张表关联起来分析时你不可能每算一个指标就重新 join 一次。常见做法是一开始就把三张表合成一张宽表后面所有分析都基于这张宽表操作。# 订单表关联商品表取商品分类和单价 df orders.merge(products, onproduct_id, howleft) # 再关联用户表取用户城市和注册时间 df df.merge(users, onuser_id, howleft) # 派生新字段订单金额 单价 * 数量 df[line_total] df[price] * df[quantity] # 看一下最终表结构 print(df.shape) print(df.dtypes)howleft表示以订单表为主表商品表或用户表里找不到对应 ID 的记录会保留 NaN而不是整行消失。这个选择很重要订单表里有 1000 条记录商品表有 900 个商品howinner后订单只剩 900 条销售额瞬间少算 10%。大作业里通常不需要处理孤儿订单所以 left join 是相对稳妥的默认选项。关联之后检查一下行数如果行数比订单表还多说明商品表有重复的 product_id产生了笛卡尔积这种数据质量问题越早发现越好。4. 核心分析模块销售、用户、商品、趋势四个维度系统跑通后关键看分析模块能不能产出有说服力的结论。这四个维度基本覆盖了课程大作业的评分点整体销售、用户分层、商品结构、时间趋势。4.1 销售总览GMV、订单量、客单价先算整体经营指标这部分代码是统一口径的基础。很多同学容易翻车的地方是把所有订单都算进 GMV没排除退款单。下面的代码先按状态过滤再计算指标。# 只看有效订单 valid df[df[status] ! 退款] # 销售总览 gmv valid[line_total].sum() order_cnt valid[order_id].nunique() customer_cnt valid[user_id].nunique() avg_order_value gmv / order_cnt print(fGMV: {gmv:.2f}) print(f订单量: {order_cnt}) print(f客单价: {avg_order_value:.2f})这里要特别注意order_id.nunique()而不是count()。count()统计的是行数如果一张订单包含三件不同商品在宽表里就有三行count()会把一张订单算成三单。nunique()是去重后的订单数量才是真实的订单量。客单价的口径也一样分子 GMV 是行级金额求和分母必须是去重订单数否则客单价虚低。实际跑这种系统时我一般会加一句print(f有效订单率: {order_cnt / df[order_id].nunique():.2%})用于在报告里说明数据质量。4.2 用户分层RFM 模型与复购率用户价值分析是电商数据分析系统的经典加分项。RFM 模型从三个维度打分最近一次购买时间Recency、购买频率Frequency、购买金额Monetary。在 pandas 里实现不需要复杂算法几个 groupby 就能算出来。import datetime # 用数据最大日期 1 作为基准日 ref_date df[order_date].max() datetime.timedelta(days1) # 按用户聚合 RFM 三指标 rfm df.groupby(user_id).agg({ order_date: lambda x: (ref_date - x.max()).days, # R距上次购买天数 order_id: nunique, # F购买次数 line_total: sum # M累计消费金额 }) rfm.columns [R, F, M] # 用 qcut 分箱1 到 4 表示从差到好 rfm[R_score] pd.qcut(rfm[R], 4, labels[4, 3, 2, 1]) rfm[F_score] pd.qcut(rfm[F].rank(methodfirst), 4, labels[1, 2, 3, 4]) rfm[M_score] pd.qcut(rfm[M], 4, labels[1, 2, 3, 4])qcut是按分位数切分比cut按数值区间切分更合理因为用户消费金额往往呈长尾分布用固定金额区间会让大量用户挤在同一档。这里有一个小坑当 F 字段购买次数出现大量相同值时qcut会报Bin edges must be unique的错误解决办法就是代码里的rank(methodfirst)给相同值加一个顺序排名让分位数计算不再重复。分箱之后再把三个分数拼接成一个总分组# 组合 RFM 分组标签 rfm[RFM] rfm[R_score].astype(str) rfm[F_score].astype(str) rfm[M_score].astype(str) # 输出分组标签占比 print(rfm[RFM].value_counts().head(10))复购率也很好算直接看每个用户购买次数大于 1 的比例即可。如果有个用户一年买 50 次还有个用户只在促销日买一次二者在 RFM 的 F 维度自然拉开差距。4.3 商品结构热销 top 与价格带分布商品分析关注两件事哪些商品贡献了主要销售额以及价格区间分布是否合理。这部分结果通常是报告里的图表素材。# 热销商品 Top10 top_products df.groupby(product_id).agg( sales(line_total, sum), qty(quantity, sum) ).sort_values(sales, ascendingFalse).head(10) # 价格带分布 bins [0, 50, 100, 200, 500, 1000] labels [0-50, 50-100, 100-200, 200-500, 500-1000] df[price_band] pd.cut(df[price], binsbins, labelslabels) band_stats df.groupby(price_band, observedFalse).agg( order_cnt(order_id, nunique), gmv(line_total, sum) ).reset_index() print(band_stats)pd.cut的 bins 需要你自己定义这里没有标准答案取决于你的数据分布。先执行df[price].describe()看分位数用十分位数来定义档位比拍脑袋定区间更合理。observedFalse这个参数在 pandas 2.0 之后如果不加分组时会有警告旧代码里没有它也不报错但新款 pandas 会提示未来版本行为将改变。价格带统计结果还可以加一列占比用band_stats[gmv] / band_stats[gmv].sum()就能算图表里饼图和柱状图都会用到。4.4 趋势分析日粒度、周粒度与促销节点趋势分析最能体现数据洞察能力也是答辩时解释“为什么 11 月销售额特别高”的关键素材。用 pandas 的resample可以完成不同时间粒度的聚合。# 日粒度每日 GMV daily_gmv df.resample(D)[line_total].sum() # 周粒度按自然周聚合 weekly_gmv df.resample(W)[line_total].sum() # 找出最高销售额的前5天 top_days daily_gmv.nlargest(5) print(top_days) # 月份同比按月份聚合后算环比 monthly_gmv df.resample(M)[line_total].sum() monthly_gmv.index monthly_gmv.index.to_period(M) print(monthly_gmv.pct_change())resample(W)默认是从周一开始算一周如果你希望从周日开始可以改成W-SUN。pct_change()算的是环比变化率月度结果里能清晰看到哪个月有爆发式增长。做日粒度趋势时要注意数据量如果订单跨了半年画日粒度折线图会非常密横坐标全部堆在一起这时候可以把趋势图放在对应的小节开头或者后面抽稀展示。这个横坐标密集问题后面避坑章节我会单独展开。5. 避坑与排查跑通这套系统最常见的六个问题这些坑是我自己跑课程项目源码时真实踩过的有的是环境问题有的是代码兼容问题有的是数据文件本身的问题。明白了原因之后解决起来都很快。5.1 中文乱码列名变成\ufeff或锘[*开头现象读取 CSV 后print(orders.columns)显示第一列是\ufefforder_id按列名取数据时直接报 KeyError。原因CSV 文件本质是文本文件编码不对时 pandas 会原样把字节读进来。Windows 记事本或 Excel 另存为 CSV 会带上 UTF-8 BOM也就是文件开头多三个不可见字节utf-8编码读取时会把它们解析成\ufeff字符并黏在第一个字段名前。解决读取时统一使用encodingutf-8-sig。pandas 处理 BOM 时utf-8-sig会自动剥离前导字节。如果你已经用utf-8读进来了临时补救办法是orders.columns orders.columns.str.replace(\ufeff, )。5.2 Matplotlib 中文全部变成方框现象图表标题、坐标轴标签的中文全部变成小方块英文显示正常。原因Matplotlib 默认字体不支持中文字符集系统里没有匹配的中文字体时就直接画方框。解决在绘图脚本开头强制指定中文字体。import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei, PingFang SC] plt.rcParams[axes.unicode_minus] False # 解决负号显示为方块的问题Windows 上SimHei和Microsoft YaHei一定存在macOS 用PingFang SCLinux 用WenQuanYi Zen Hei如果没有就apt install fonts-wqy-zenhei。如果做完之后保存的图里中文仍然是方块重启 Python 内核再跑一次字体配置只对当前进程生效。5.3 日期排序错乱9 月排在 10 月后面现象按月做折线图时横坐标顺序是10月, 11月, 9月看起来时间轴完全乱了。原因查询结果返回的月份数据是字符串格式字符串排序先按第一位比较9大于1所以 9 月排到了 10 月后面。解决确保参与排序列是 datetime 类型。如果你的 Month 列是从order_date提取的df[月份] df[order_date].dt.to_period(M)就会得到一个 period 类型排序正确。如果在 groupby 时用了df[order_date].astype(str).str[:7]得到的就是字符串这时需要手动sort_index()或者转换回 datetimedf[月份] pd.to_datetime(df[月份])。5.4 透视表列名变化代码里找不到收入这个列现象用pivot_table生成了透视表但后面的代码执行df[收入]时报 KeyError。原因pivot_table会把聚合后的列名变成 MultiIndex比如(收入, sum)。你以为的列名收入实际上是(收入, sum)这个元组。解决有两个常用方案。一是在pivot_table里传values和aggfunc后用reset_index()平铺二是直接访问元组形式的列名# 方案一reset_index 把透视索引转回普通列 pivot pd.pivot_table(df, indexmonth, columnscategory, valuesline_total, aggfuncsum).reset_index() # 方案二如果是 MultiIndex用元组访问 pivot[(收入, sum)] # 不推荐太绕我一般推荐方案一因为后续步骤越多MultiIndex 带来的维护成本越高。5.5 内存不足几百万行订单数据直接内存溢出现象读取订单 CSV 后内存占用直接升到 90% 以上后续 groupby 操作变得极慢甚至卡死。原因pandas 读取数据时默认把每个字段都分配成宽松的类型。订单 ID、商品 ID 这类看起来是数字的字段如果 CSV 里有空值pandas 会把它读成 float64内存占用翻倍。解决用dtype参数预先指定列类型减少内存分配。orders pd.read_csv( data/orders.csv, encodingutf-8-sig, dtype{order_id: int32, user_id: int32, product_id: int32, quantity: int8}, parse_dates[order_date] )int32占 4 字节比默认的int64省一半。quantity用int8够用吗如果单笔订单数量不会超过 127就够。如果数据里有超范围的值pandas 会抛异常而不是静默截断这也是个安全兜底。执行完这步再用df.info(memory_usagedeep)看内存占用你能直观看到优化效果。5.6 Flask 页面打不开或端口被占用现象运行可视化模块后浏览器访问http://127.0.0.1:5000显示拒绝连接或者终端提示Port 5000 is already in use。原因5000 端口被其他进程占用这是最常见的场景。特别是 macOS 新版本的 AirPlay 接收器会默认占用 5000 端口杀都杀不完。解决换一个不常用的端口启动比如 8000。if __name__ __main__: app.run(host127.0.0.1, port8000, debugTrue)如果换端口还不行用lsof -i:8000查占用进程kill 掉再启动。debugTrue的好处是代码改完自动重启坏处是调试模式会启动一个额外的 reloader 进程有时候会重复执行初始化代码导致端口看起来被占用两次。正式答辩演示时建议改成debugFalse。6. 进阶让这个课程大作业更像一个能上线的数据分析系统课程大作业的功能做到“能算出指标”只是及格线真正拉开差距的是工程化和可维护性。从这个源码往下走有三件事投入产出比很高接入数据库、做增量更新、写数据质量自检函数。第一件是数据库替代 CSV。把orders.csv导进 MySQL 或者 SQLite用pandas.read_sql(SELECT * FROM orders, conn)读取好处是数据量大时不用全量加载到内存SQL 端可以先用 WHERE 过滤。而且数据更新后不需要重新生成 CSV数据库里 insert 新记录就能继续分析。如果不想搭 MySQL用 Python 内置的sqlite3模块零成本迁移。第二件是增量更新。课程大作业里通常是从头分析到尾巴但现实场景数据是每天增长的。常见做法是记录上次分析的日期只处理新增部分再和存量结果合并。大作业里你能体现这个思路评分观感会不一样。第三件是数据质量检查。分析之前先跑一段检查脚本订单数是否比昨天少、客单价是否突然翻倍、日期范围是否符合预期任何一个异常就停下来输出警告。这段逻辑不复杂却是从“能算”到“靠谱”的标志。我自己的习惯是拿到任何一份课程大作业源码第一件事不是直接跑主程序而是先读原始数据文件把字段名、行数、缺失情况列成一张表。这套动作做得越多后续排错越快。希望这篇文章能让你少走几个弯顺利把这套电商平台数据分析系统跑通写进报告里也能讲得清楚明白。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询