简介:这份基于 Python 的商品销售数据分析可视化系统源码,定位为高校期末大作业的高分参考项目,适合正在完成数据分析、数据可视化课程设计的学生快速复用,借助完整源码即可理解整个实现链路。项目以 Flask 为骨架,整合数据爬取、CSV/Excel/SQL 数据管理、LDA 主题分析及回归预测等模块,后端处理与前端展示相互配合,评审分达 95 分以上,经严格调试可直接运行。压缩包共 73 个文件,含 6 个 Python 源码、11 个 CSV 数据、2 个 Excel 表格、6 个 HTML 页面、6 个 JS 脚本及字体图片等资源,整体大小 14.28MB,目录清晰,便于按功能模块查找修改。目前已有 572 人浏览学习。下载后既能获得完整可演示的销售数据可视化系统,也能学习爬虫、数据清洗、文本分析与前端图表联动等思路,借助 SQL 文件与测试文档可进行二次开发,对期末答辩和项目复盘很有帮助。
1. 期末大作业凭什么拿95分:先搞懂这份商品销售数据分析源码在回答什么问题
期末大作业拿到95分以上的,从来不是界面最炫的那个,而是让导师觉得「这系统是真的能拿去用的」那个。基于Python的商品销售数据分析可视化系统,名字听起来平淡,但它踩中了评分表里权重最高的三块:分析逻辑是否闭环、可视化表达是否准确、代码工程是否规范。这份zip源码要解决的,是从一堆原始订单表出发,把销售额趋势、品类结构、地区贡献、用户复购这些业务问题逐一用图回答清楚,最后形成一份能站上答辩台讲满五分钟的完整报告。
它适合三类人:准备期末大作业的学生,想找一个练手项目把Pandas和Matplotlib串起来的新手,以及需要给团队快速搭一套轻量销售看板的开发者。文章不替导师打分,只把这条路上最容易翻车的环节拆开讲透,让你照着做就能拿到那个分数。
2. 商品销售数据分析的底层逻辑:画图之前先把四条业务主线定下来
2.1 数据不干净,画什么都白搭:清洗环节怎么取舍
拿到一份CSV或Excel格式的订单表,第一件事绝对不是画图。真实场景下的销售数据,脏得五花八门:订单日期格式不统一,有的列是「2024-01-05」,有的列是「2024/1/5」;金额字段带着货币符号或者千分位逗号;商品分类有大量空值;还混着负数的退款订单。我一般会先跑一条df.info()和df.describe(),看清每一列的类型、缺失情况和数值分布,再决定清洗策略,这一步能省下后面三个晚上的排错时间。
import pandas as pd df = pd.read_csv('sales_data.csv', encoding='utf-8') print(df.info()) print(df.describe())清洗的取舍逻辑要清醒:不是所有缺失都填平均值。「商品分类」缺失如果占比很小,直接dropna删行就完了;「销量」出现负数,那是退款单,单独标记出来,不要混进趋势分析。把每条清洗规则写进注释,答辩时候就是加分项,导师会看到你有数据质量意识。
# 删除关键字段为空的行,避免后续聚合时出现NaN df = df.dropna(subset=['订单日期', '商品分类', '销售额']) # 金额字段转数值类型:先去货币符号,再去千分位逗号 df['销售额'] = df['销售额'].astype(str).str.replace('¥', '').str.replace(',', '').astype(float) # 退款单单独标记,不进销售趋势分析 df['是否退款'] = df['销售额'] < 0 df_sales = df[df['销售额'] >= 0]这段代码解决三个高频问题:第一,dropna只针对三个核心字段,而不是全表去空,否则会把「备注」列的空值也当成清洗对象,误删有效行;第二,字符串转float前必须先清理格式,否则astype直接报错;第三,退款单隔离逻辑很关键,如果不处理,月度销售额会被负数拉出一个假低谷,折线图出现莫名其妙的深V,导师一眼就看出数据没做质检。
2.2 四张核心图定生死:趋势、结构、分布、对比
期末大作业能不能上95分,核心看你会不会选图。商品销售数据分析翻来覆去逃不出四组问题:销售额随时间的走势,用折线图;各类商品销售额的占比结构,用饼图或横向条形图;单笔订单金额的分布区间,用直方图;不同地区或门店的销售对比,用条形图。这四类图覆盖了导师评分表上「数据分析深度」那一栏的全部得分点。
选图的逻辑要记牢:时间维度用折线,不要用饼图,饼图展示时间序列会乱成一团;品类占比用饼图或堆叠条形图,不要用折线;分布形态用直方图,不要用散点图;地区对比用条形图,数据维度多再考虑地图。很多同学把四张图强行合并成两张复杂图想蒙混过关,导师一眼看穿,反而不如老老实实把四张图画清楚、配好说明。
2.3 技术栈的现实约束:Pandas负责算,Matplotlib负责画,Pyecharts负责炫
这套系统最常见的组合是Pandas + Matplotlib + Pyecharts。Pandas做分组聚合,Matplotlib出静态主图,Pyecharts生成交互式HTML图表,三者各司其职。选型时先想清楚三件事:运行环境能不能装第三方库,学校机房经常离线,没法现场pip install;输出物是打印版报告还是电子版演示,打印版静态图更清晰,电子版交互图更有冲击力;答辩时会不会被要求现场改图,交互式看板在这时候就是拯救分数的救命稻草。
我的建议是:主图全部用Matplotlib,保证离线和稳定性;报告附录里放一到两个Pyecharts交互图做亮点,兼顾分数和效果。不要一上来就上大而全的BI框架,期末大作业的时间经不起折腾,把核心代码写好,比套一个炫酷模板更能让导师认可你的工程能力。
3. 拆解核心代码:从CSV到看板的三段必经之路
3.1 第一步:read_csv把数据读进来,类型转换和索引设定一次到位
读取这一步,很多同学拿到源码直接跑,不管实际Excel里表长什么样。常见做法是先手动把表头统一成简短英文或中文列名,再读进来,避免后面groupby时写出一长串带空格和特殊符号的列名,把自己绕晕。读取时顺便把日期列、索引列一次性处理好,后面每行代码都能省一次转换。
import pandas as pd import matplotlib.pyplot as plt # 设置中文字体,解决默认字体下中文显示成方块的问题 plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'PingFang SC'] plt.rcParams['axes.unicode_minus'] = False df = pd.read_csv( 'sales_data_clean.csv', encoding='utf-8-sig', parse_dates=['订单日期'] ) # 把日期规整到月份,后续按月聚合的关键一步 df['月份'] = df['订单日期'].dt.to_period('M') print(df.head(3))read_csv里的parse_dates参数把订单日期列在读取时直接转成datetime类型,省掉后续pd.to_datetime的二次处理;to_period('M')把日期归整到月份,生成Period对象,之后groupby('月份')会非常干净,时间索引不会带着日时分秒。中文字体那两行rcParams建议放在脚本最开头,第一个参数是字体兜底列表,Windows用SimHei,Mac用PingFang SC,按顺序逐个尝试,第二个参数让负号正常渲染,否则坐标轴上的负号会显示成方块。注意:如果你读的文件是Excel另存的CSV,编码往往是gbk,那encoding要换成'gbk',用'utf-8'读会直接抛UnicodeDecodeError。
3.2 第二步:groupby与resample,让Pandas把「算数」这件事干完
核心分析全部由Pandas分组聚合完成,不要用Excel手动透视,也不要在Python里写for循环硬算,那既慢又难看。这里有三板斧:groupby做分类汇总,resample做时间重采样,agg做多指标聚合。三招配合,能把一份原始订单表揉成十几种视角的分析结果。
# 1. 月度销售额趋势 monthly_sales = df_sales.groupby('月份')['销售额'].sum() # 2. 各品类销售额与销量多指标统计 category_stats = df_sales.groupby('商品分类').agg( 总销售额=('销售额', 'sum'), 总销量=('销量', 'sum'), 订单数=('订单号', 'count') ).sort_values('总销售额', ascending=False) # 3. 各地区销售额排名 region_sales = df_sales.groupby('地区')['销售额'].sum().sort_values(ascending=False)按月聚合时,groupby的对象是df['月份']这列Period数据,不是原始日期,否则每个月会被拆成好几天,折线图变成锯齿状,这是最常见的翻车点。agg里面用元组('销售额', 'sum')的写法可以同时算多个指标,比分别sum再合并更简洁,而且列名可以自定义成中文,在报告表格里直接能用。sort_values的ascending参数务必注意:False是降序,True是升序,地区排名用False,把第一名放最上面。这段代码跑完,用print(category_stats.head())看一眼结果,确认没有NaN和离谱的负数,再往下走。
3.3 第三步:Matplotlib画静态图,Pyecharts补两个交互图
主图用Matplotlib画完保存成PNG,直接插进Word报告,稳定清晰;辅助图用Pyecharts生成HTML,答辩时浏览器打开,用鼠标悬停看数值,动态效果非常加分。先看一个组合图的经典案例:月度销售额柱状图叠加毛利率折线图,双Y轴解决量纲不一致问题。
import matplotlib.pyplot as plt fig, ax1 = plt.subplots(figsize=(10, 5)) months = [str(m) for m in monthly_sales.index] ax1.bar(months, monthly_sales.values, color='#4C72B0', label='月销售额') ax1.set_xlabel('月份') ax1.set_ylabel('销售额(元)') ax1.tick_params(axis='x', rotation=45) # 第二个Y轴,毛利率跟销售额不是一个量纲,不能共用坐标轴 ax2 = ax1.twinx() ax2.plot(months, monthly_profit_rate.values, color='#C44E52', marker='o', label='毛利率') ax2.set_ylabel('毛利率(%)') plt.title('月度销售额趋势与毛利率变化') plt.tight_layout() plt.savefig('monthly_trend.png', dpi=200) plt.show()twinx()创建共享X轴的第二个Y轴,这是双轴图的标配写法;figsize=(10,5)控制画布比例,横幅图在Word报告里占半页刚好,竖幅图容易显得小气。dpi=200保证导出图片打印不模糊,这个参数在答辩前的打印环节特别重要。Pyecharts部分,代码风格是链式调用,新版API和旧版差别很大,旧教程里的add_x_axis(带下划线)已经废弃,新版本要用add_xaxis,如果你看到AttributeError或TypeError报错,先检查pyecharts版本。
from pyecharts.charts import Bar from pyecharts import options as opts bar = ( Bar() .add_xaxis(months) .add_yaxis('月销售额', monthly_sales.values.tolist()) .set_global_opts( title_opts=opts.TitleOpts(title='月度销售额可视化'), yaxis_opts=opts.AxisOpts(name='销售额(元)') ) ) bar.render('monthly_bar.html')Pyecharts的render生成独立HTML文件,双击就能在浏览器打开,不需要启动任何服务,这是它在期末大作业场景下比Flask+D3更友好的原因。代码里注意add_yaxis的数据必须是list,Pandas的Series要先调tolist()转换,否则图表会空白,调试半天发现是数据类型问题,这种玄学错误在答辩现场最容易让人乱了阵脚。
4. 让图表比旁边同学的好看:六个必调的可视化参数与配色方案
4.1 中文字体、坐标轴旋转与网格线:默认配置下最先翻车的三处
Matplotlib默认字体是DejaVu Sans,对中文支持为空,这是每个用Python做数据分析的人第一次流汗的地方。除了rcParams设置字体,还要处理坐标轴刻度标签的拥挤问题:月份作为X轴标签,如果不旋转,8个以上的月份标签会叠成一团黑影。网格线透明度也是很多人忽略的细节,默认的实线网格会压在数据上,视觉上非常抢戏。
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei'] plt.rcParams['axes.unicode_minus'] = False plt.rcParams['grid.alpha'] = 0.4 ax.xaxis.set_major_locator(plt.MaxNLocator(6))grid.alpha是网格线透明度参数,默认1.0太抢眼,调到0.4让网格退到背景层,数据系列反而跳出来。MaxNLocator(6)限制X轴最多显示6个刻度,解决月份多时的标签重叠问题。这三个设置属于一次配置全局受益,建议放在整个脚本的最前面,和rcParams字体设置放在一起,后面所有图表都生效,不用每张图重复写。
4.2 六个影响评审观感的细节参数
一份大作业的图表观感,不是靠花哨模板,而是靠细节参数打磨出来的。第一,柱状图宽度bar width默认0.8,两组柱子并排对比时显得太粗,改成0.4更清爽;第二,图例位置默认best有时会压在数据上,手动指定loc='upper left'或'lower right';第三,折线图必须加marker,空心圆或方块都行,没有标记的线条在黑白打印时完全看不出趋势;第四,柱状图顶部加数值标签,导师不用眯眼猜数;第五,坐标轴上限设置set_ylim(0, max*1.1),给柱子顶部留白;第六,savefig时写bbox_inches='tight',否则标题可能被裁掉。
bars = ax.bar(categories, values, width=0.5, color='#55A868') for bar in bars: ax.text( bar.get_x() + bar.get_width() / 2, bar.get_height() + 20, f'{bar.get_height():.0f}', ha='center', va='bottom', fontsize=9 ) ax.margins(y=0.1) plt.savefig('category.png', dpi=200, bbox_inches='tight')ax.text里的坐标是数据坐标,bar.get_x()拿到柱子左侧的X值,加一半宽度得到柱子中心;Y方向在柱子高度基础上再加20个数据单位,数值标签悬在柱顶上方,不会贴死在边缘。ha和va是水平垂直对齐方式,center和bottom分别指水平居中、垂直底部对齐。ax.margins(y=0.1)在Y方向自动留10%余量,就算不手动算ylim也不会让柱子贴天花板,这一行比手写ylim更省心也更稳。
4.3 配色别用默认的彩虹:一套克制配色让报告质感翻倍
很多期末大作业死在配色上,红黄蓝绿齐上阵,像一台年久失修的彩电。Matplotlib默认配色已经比早期版本柔和了,但同一个脚本里多张图各用各的颜色,整体感会碎掉。我一般直接用Seaborn的color_palette来取色,或者从ColorBrewer里挑一组顺序色。主图用同一色系不同深浅,对比图用两组主色,双轴图一蓝一红,全局风格统一。
import seaborn as sns colors = sns.color_palette('Blues', n_colors=5)n_colors控制从色板里取几个色阶,Blues是从浅蓝到深蓝的渐变,适合柱状图按数值大小渐变着色。如果做扇形图,建议用Set3这种柔和色板,避免纯红纯绿刺激眼球。记住一个原则:同一张图内颜色不超过五个,同一份报告里所有图表的配色风格保持统一,不要一张冷淡灰一张鲜艳彩。用plt.style.use('ggplot')或者'seaborn-v0_8-whitegrid'这类内置风格,也能在不用心配色的情况下拿到一个及格以上的观感,但想冲95分,还是建议手动控色。
5. 期末答辩翻车现场:五个最常见的避坑点与评分红线
5.1 现象:程序一跑全是乱码,中文标题成了方块
这是最基本也最致命的翻车,代码逻辑全对,但图一出来标题全是方块,导师第一印象直接掉档。原因很明确:Matplotlib默认字体不含中文字形。解决:脚本最顶部加三行rcParams设置,就是第3章里那段,一行查缺补漏都不行。如果是在Jupyter Notebook里跑,还要注意单元格里print中文报UnicodeEncodeError的问题,那是终端编码问题,Windows下在代码第一行加# -- coding: utf-8 --能解决一部分,再不行就改环境变量PYTHONIOENCODING=utf-8。
5.2 现象:导出的图表标题被裁剪,Word里的图模糊不清
savefig时标题被裁掉,是bbox_inches参数缺失;Word里图片发虚,是dpi太低。这两个问题在打印纸质版报告时才会暴露,而那时候已经没有时间返工了。解决:savefig固定写dpi=200和bbox_inches='tight',养成肌肉记忆。另一个小坑:Word插入图片后被人为拉大,图片会被拉伸模糊,插入时保持原始显示尺寸,最多缩不小放大。
5.3 现象:生成的模拟数据太假,被导师一眼看穿
诚实说,大部分期末大作业的原始数据是模拟生成的,这本身没问题,但生成方式太敷衍就会穿帮。某学姐当年的模拟项目X就是栽在这:12月销售额从80万直接掉到20万,没有任何解释,导师当场问她「年底大促怎么还跌了?」,她答不上来。解决:模拟数据也要有业务逻辑,设定一个基础值,叠加季节波动,再加上随机噪声,生成后用describe看一眼分布是否合理。
import numpy as np np.random.seed(42) months = pd.period_range('2023-01', '2023-12', freq='M') base = 50000 # 夏季月份系数高、冬季低,模拟真实零售季节波动 seasonal = [1.2 if m.month in [6, 7, 8] else 0.8 for m in months] # 正态分布噪声,避免数据过于平滑 noise = np.random.normal(0, 5000, len(months)) sales = base * seasonal + noisenp.random.seed(42)固定随机种子,保证每次运行结果一致,答辩现场重跑不会变数,这一行在模拟数据场景里有奇效。seasonal列表用列表推导按月份定义季节系数,6、7、8月给1.2倍,其他月份0.8,你会发现夏季销售额天然比冬季高50%,这就是业务逻辑,答辩时讲「夏季为销售旺季」无比自然。noise用正态分布而不是uniform均匀分布,更接近真实销售数据的随机波动形态,均匀分布会在图上画出明显是假的锯齿。
5.4 现象:图做了一大堆,导师问「所以你的结论是什么?」
这是评分从90滑到75的最常见原因:只有可视化,没有数据分析结论。导师不会因为你画了20张图就给高分,他关心的是你有没有从数据里回答出一个业务问题。解决:每张图下面配不超过50字的结论,比如「夏季月份销售额平均高出冬季30%,建议夏季备货量提升25%」。源码里如果只渲染图表,没有产出文字结论,答辩前必须补上分析报告,把每张图和要讲的话对应起来。
5.5 现象:答辩现场演示,Pyecharts的HTML页面白屏
Pyecharts生成的是HTML文件,需要浏览器打开。如果答辩教室的电脑没有网络,或者浏览器版本太旧,ECharts的JavaScript文件加载不出来,页面就是白屏。解决:答辩前把HTML文件拿到目标电脑上实测一次,别指望教室电脑一定没问题。更稳妥的方案是把图表用render_notebook在Jupyter里内联显示,Jupyter已经装好的话不依赖外网;或者用Flask把所有图表包成一个本地Web服务,答辩时启动服务后浏览器访问localhost,完全离线可用。
6. 让评分再上一个台阶:两个加分的小技巧
6.1 技巧一:用Flask把静态图升级成可交互看板
已经做到第3章、第4章的程度,分数大概是85到90,想再往上走,把整个系统从「脚本出图」升级成「网页看板」,让导师在答辩时亲手点两下筛选器,这个体验的记忆点比任何一张静态图都强。Flask是最轻量的方案,用pandas把分析结果算好,渲染成带下拉筛选的页面,筛选项控制在三个以内:月份、品类、地区。注意不要塞一堆控件上去,导师点两下能出图,目标就达成了。
from flask import Flask, render_template, request app = Flask(__name__) @app.route('/') def index(): selected_month = request.args.get('month', '全部') if selected_month == '全部': data = monthly_sales else: data = monthly_sales[monthly_sales.index == selected_month] return render_template( 'dashboard.html', data=data.to_dict(), month=selected_month ) if __name__ == '__main__': app.run(debug=False, port=5000)request.args.get从URL查询参数里读取筛选条件,没有参数时默认'全部',保证首屏正常显示;monthly_sales.index == selected_month这个布尔索引只选一个月份的数据,性能没问题,数据量再大也不卡。app.run(debug=False)在演示时务必关掉debug模式,否则出错时浏览器会弹出调试器页面,非常难看。答辩前把模板文件和Python脚本放到同一目录,启动服务后本地浏览器打开localhost:5000,实测一遍筛选流程。
6.2 技巧二:用「结论优先」组织答辩PPT和报告
最后一点不是代码,是表达。答辩PPT第一页放核心结论:「2023年夏季销售额比冬季高出30%,主要由饮料品类拉动」,然后放三张支撑图表,最后放一页代码架构简述。导师的时间有限,前30秒听到一句有洞察的结论,比看三十页图让人安心得多。我个人的习惯是,报告最后放一页「如果我是运营负责人,我会做三件事」,不管数据是不是模拟生成的,这一页放上去,评分视角就从「代码是否跑通」自动切换成「这个人有没有商业分析思维」,分数自然往上走。
这套路线走完,从数据清洗、分组聚合、可视化出图到交互看板,每一层都踩在评分规则的核心权重上。期末大作业的终点不是交一个zip,而是让看到的人觉得这系统是能拿去用的。你如果正卡在最后一步调不出来,先别急着重写,把第5章的五条坑过一遍,再回到第3章的代码核对,基本就能跑通。希望帮到你。
本文还有配套的精品资源,点击获取