资讯详情

Python美妆销售数据分析实战:pandas与matplotlib从清洗到可视化

📅 2026/10/12 15:07:02 | 华诺云谱 👁 阅读
Python美妆销售数据分析实战:pandas与matplotlib从清洗到可视化
简介这份资源是面向高校学生与数据分析初学者的Python电商数据分析实战项目以天猫双十一美妆销售数据为对象帮助读者掌握从数据清洗到可视化呈现的完整分析流程适合用作课程大作业、自学练手或教学参考。压缩包共17个文件、约5.08MB包含py源码、ipynb笔记本、csv与xlsx原始数据、html分析报告、png图表及xml配置等覆盖代码、数据与结果展示三类内容便于直接运行与二次修改。目前已有149人学习。项目约400行代码围绕pandas完成分组、排序、筛选与聚合等操作并借助matplotlib绘制直方图、折线图、散点图从品牌、类别、价格区间等维度展示销售规律同时涉及数据清洗、格式转换与规约等预处理环节体现自动化分析思路。读者可据此理解电商数据的分析框架快速复现图表并迁移到其他数据集。1. 从一份约 400 行的 Python 大作业说起美妆销售数据到底能分析出什么每年双十一结束后台都会堆出一批美妆品类的销售明细品牌、单品、成交价、销量、省份、时段。数据量不大几十万行以内但真正动手分析时很多人第一反应是打开 Excel 拉透视表拉到一半就卡死或者做出来的图连自己都不想看第二眼。这份约 400 行的 Python 示例源码解决的正是这个场景用 pandas 做清洗聚合用 matplotlib 出图把一份电商美妆销售明细变成能讲清楚结论的分析报告。它适合两类人——正在做大作业、需要一份能跑通、能改、能讲清逻辑的学生以及刚转数据分析、想找一个完整小项目练手的从业者。核心不是代码有多长而是每一步为什么这么切、图为什么这么画、参数为什么这么设。下面按“先跑通、再拆解、后避坑”的顺序讲透。2. 环境与数据准备让 400 行源码在你机器上先跑起来2.1 依赖选型为什么是 pandas matplotlib而不是别的这份源码的技术栈很朴素pandas 负责数据读取、清洗、分组聚合matplotlib 负责可视化numpy 做数值辅助。有人会问为什么不用 pyecharts 或 seaborn原因很实际——大作业和入门项目最怕环境复杂。pyecharts 出交互图好看但依赖前端渲染导出和截图麻烦seaborn 封装太厚调一个坐标轴刻度要翻半天文档。matplotlib 虽然默认样式丑但它是所有绘图库的底座参数可控、报错信息明确出问题能查到根上。常见做法是先用 matplotlib 把图跑通再考虑换皮。版本上不需要追新pandas 1.x 以上、matplotlib 3.x 以上即可。装依赖就一行pip install pandas matplotlib numpy openpyxlopenpyxl 是读 xlsx 用的如果数据是 csv 可以不装。这里有个血泪经验不要用 pip install 装到系统 Python 里建个虚拟环境否则后面版本冲突会让你怀疑人生。python -m venv venv # Windows venv\Scripts\activate # macOS / Linux source venv/bin/activate pip install pandas matplotlib numpy openpyxl逻辑说明venv 把依赖隔离在项目目录删掉 venv 文件夹就等于卸载干净。参数上没什么可调的唯一注意的是 Python 版本别低于 3.8否则 pandas 新版装不上。2.2 数据字段与读取先看清列名再写代码美妆销售明细的典型字段包括订单日期、品牌、商品名称、品类口红/粉底/面膜等、成交价、销量、销售额、省份、时段。不同来源列名可能带空格或中文读取后第一件事是统一列名。import pandas as pd # 读取数据注意编码中文 csv 常见 gbk df pd.read_csv(beauty_sales.csv, encodinggbk) # 统一列名去空格、转小写英文方便后续引用 df.columns [c.strip() for c in df.columns] print(df.columns.tolist()) print(df.shape) print(df.head())逻辑说明encoding 参数是第一个坑utf-8 读中文 csv 报 UnicodeDecodeError 时换 gbk 或 gb18030。df.shape 先看行列数df.head() 看前五行确认列名和数据类型符合预期。如果列名是中文建议重命名成英文后面 groupby 写起来不容易打错。df df.rename(columns{ 品牌: brand, 品类: category, 成交价: price, 销量: qty, 销售额: amount, 省份: province, 时段: hour })参数说明rename 用字典映射只改需要改的列没列出的保持原样。改完再 print 一次列名确认。2.3 数据清洗缺失值、异常值、重复订单怎么处理真实数据没有干净的。销量为负、成交价为 0、品牌名为空这些都会让后面的聚合结果失真。# 1. 去掉完全重复的行 df df.drop_duplicates() # 2. 关键字段缺失直接删 df df.dropna(subset[brand, amount, qty]) # 3. 过滤异常价格和销量必须为正 df df[(df[price] 0) (df[qty] 0)] # 4. 销售额字段如果缺失用 价格 * 销量 补 df[amount] df[amount].fillna(df[price] * df[qty]) print(清洗后行数:, len(df))逻辑说明drop_duplicates 去重dropna 只删关键列缺失的行不要无脑 df.dropna() 把整行删光。异常值用布尔索引过滤比 clip 截断更安全因为负销量往往是退货记录截断会污染分析。参数上subset 指定判断缺失的列fillna 用计算值补比用均值补更符合业务逻辑。提示清洗前后都打印行数差值就是被处理掉的数据量。如果删掉超过 30%要回头检查是不是读取或列名映射出了问题。3. 核心分析逻辑从品牌排名到时段分布的六步拆解3.1 品牌销售额排名groupby 聚合与排序分析美妆销售第一个要回答的问题是“哪个品牌卖得最好”。用 groupby 按品牌聚合销售额再排序取前 N。# 按品牌聚合销售额和销量 brand_stat df.groupby(brand).agg( total_amount(amount, sum), total_qty(qty, sum), order_cnt(amount, count) ).reset_index() # 按销售额降序 brand_stat brand_stat.sort_values(total_amount, ascendingFalse) # 取前 10 top10 brand_stat.head(10) print(top10)逻辑说明agg 里用字典指定每列做什么聚合sum 求和、count 计数。reset_index 把 groupby 后的索引还原成列否则 brand 会变成索引后面画图取列名会报错。sort_values 的 ascendingFalse 表示降序这个参数新手最容易忘忘了就是从小到大排图看起来完全反了。参数说明head(10) 取前十如果品牌很多可以改成 head(15) 或按累计占比 80% 截取。order_cnt 用 count 统计订单数能看出是“高价少单”还是“低价多单”。3.2 品类占比饼图还是柱状图怎么选品类占比是典型的部分与整体关系直觉上该用饼图。但品类超过 6 个时饼图会挤成一团标签重叠看不清。我的习惯是品类少于等于 5 个用饼图多于 5 个用横向柱状图。import matplotlib.pyplot as plt # 解决中文显示 plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False cat_stat df.groupby(category)[amount].sum().sort_values(ascendingFalse) fig, ax plt.subplots(figsize(8, 5)) ax.barh(cat_stat.index, cat_stat.values, color#c44e52) ax.set_xlabel(销售额) ax.set_title(各品类销售额分布) plt.tight_layout() plt.savefig(category_bar.png, dpi150) plt.show()逻辑说明font.sans-serif 设成 SimHei 解决中文乱码axes.unicode_minus 设 False 解决负号显示成方块。barh 是横向柱状图品类名长的时候比竖着好读。tight_layout 自动调整边距防止标签被裁掉。savefig 的 dpi150 保证导出图清晰默认 100 发到文档里会糊。参数说明figsize 控制画布大小宽 8 高 5 是常用比例。color 可以用十六进制也可以用 matplotlib 内置的 tab10 配色。3.3 价格带分布直方图的 bins 到底设多少美妆产品价格跨度大从几十到上千。看价格分布用直方图但 bins 设不好图要么太粗要么太碎。fig, ax plt.subplots(figsize(8, 5)) ax.hist(df[price], bins30, color#4c72b0, edgecolorwhite) ax.set_xlabel(成交价) ax.set_ylabel(订单数) ax.set_title(美妆产品价格分布) plt.tight_layout() plt.savefig(price_hist.png, dpi150) plt.show()逻辑说明bins30 是经验值数据量几千到几万时比较合适。数据量小就减到 15数据量大就加到 50。edgecolorwhite 给柱子加白边视觉上更清晰。如果价格有极端值比如有个别上千的直方图会被拉扁可以先按分位数过滤。# 用分位数过滤极端值再看分布 q99 df[price].quantile(0.99) df_price df[df[price] q99]参数说明quantile(0.99) 取 99 分位去掉最高的 1%。这个阈值可以调0.95 更激进0.99 更保守。3.4 时段销售趋势折线图与双轴双十一的销售有明显时段特征凌晨和晚上是高峰。看趋势用折线图如果要同时看销售额和订单数用双轴。hour_stat df.groupby(hour).agg( amount(amount, sum), qty(qty, sum) ).reset_index() fig, ax1 plt.subplots(figsize(10, 5)) ax1.plot(hour_stat[hour], hour_stat[amount], color#c44e52, markero, label销售额) ax1.set_xlabel(时段) ax1.set_ylabel(销售额, color#c44e52) ax2 ax1.twinx() ax2.plot(hour_stat[hour], hour_stat[qty], color#4c72b0, markers, linestyle--, label销量) ax2.set_ylabel(销量, color#4c72b0) plt.title(双十一美妆销售时段趋势) plt.tight_layout() plt.savefig(hour_trend.png, dpi150) plt.show()逻辑说明twinx 创建共享 x 轴的第二个 y 轴两条线量纲不同也能放一起看。marker 给数据点加标记o 是圆点s 是方块linestyle-- 是虚线两条线区分更明显。颜色和 y 轴标签颜色对应读者一眼能对上。参数说明如果时段是 0-23 的整数x 轴会自动按数值排。如果时段是字符串如“00:00”要先转成有序类别否则 x 轴顺序会乱。3.5 省份维度横向柱状图与 Top N 截取省份分析看地域差异用横向柱状图取 Top 10 就够了全画出来标签挤不下。prov_stat df.groupby(province)[amount].sum().sort_values(ascendingFalse).head(10) fig, ax plt.subplots(figsize(8, 6)) ax.barh(prov_stat.index[::-1], prov_stat.values[::-1], color#55a868) ax.set_xlabel(销售额) ax.set_title(销售额 Top10 省份) plt.tight_layout() plt.savefig(province_top10.png, dpi150) plt.show()逻辑说明index[::-1] 和 values[::-1] 把顺序倒过来因为 barh 默认从下往上画不倒序的话最大值在最下面看着别扭。这是 matplotlib 横向柱状图的一个固定操作记住就行。参数说明head(10) 的 10 可以按需改。如果省份数据里有“海外”“其他”这类非省份值先过滤掉再聚合。3.6 品牌与品类交叉透视表怎么做想看每个品牌在不同品类上的表现用 pivot_table。pivot df.pivot_table( indexbrand, columnscategory, valuesamount, aggfuncsum, fill_value0 ) # 取销售额前 8 的品牌 top_brands brand_stat.head(8)[brand].tolist() pivot_top pivot.loc[top_brands] print(pivot_top)逻辑说明pivot_table 的 index 是行、columns 是列、values 是聚合值、aggfunc 指定聚合函数。fill_value0 把缺失组合填 0否则会显示 NaN。loc 按品牌名筛选只保留头部品牌表格不会太大。参数说明aggfunc 除了 sum还可以用 mean 看均价、count 看订单数。如果要做热力图把 pivot_top 传给 imshow 即可但要注意数值量纲差异大时先做归一化。4. 可视化细节让图能直接放进报告里的调参经验4.1 中文字体与负号两个必设的 rcParams前面提过这里展开说。matplotlib 默认字体不支持中文不设就是满屏方块。负号问题是因为 SimHei 里没有 Unicode 负号字形。plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei, Arial Unicode MS] plt.rcParams[axes.unicode_minus] False逻辑说明font.sans-serif 给一个字体列表matplotlib 按顺序找找到哪个用哪个。Windows 一般有 SimHei 和 Microsoft YaHeimacOS 有 Arial Unicode MS。这样写跨平台不容易翻车。axes.unicode_minus 设 False 是用 ASCII 负号替代显示正常。参数说明如果服务器上没有中文字体需要先装字体再清缓存否则设了也没用。这是部署到 Linux 时最常见的坑。4.2 配色与样式不用 seaborn 也能好看matplotlib 默认配色确实一般但不用引入 seaborn手动指定几个颜色就行。colors [#c44e52, #4c72b0, #55a868, #8172b2, #ccb974, #64b5cd]逻辑说明这组颜色来自 seaborn 的 deep 调色板对比度够、不刺眼直接拿来用。画多系列图时按顺序取色。另外可以设全局网格让图更易读。plt.rcParams[axes.grid] True plt.rcParams[grid.alpha] 0.3 plt.rcParams[grid.linestyle] --参数说明grid.alpha 控制网格透明度0.3 不会抢主体。linestyle 用虚线比实线柔和。4.3 保存图片dpi、bbox_inches 与格式选择图做完了要导出参数不对就是白边一堆或者糊成马赛克。plt.savefig(output.png, dpi150, bbox_inchestight, facecolorwhite)逻辑说明dpi150 是文档用的清晰度屏幕预览 100 够印刷要 300。bbox_inchestight 自动裁掉多余白边比 tight_layout 更彻底。facecolorwhite 保证背景是白的否则透明背景贴到深色文档里会很难看。参数说明格式按扩展名自动判断png 适合文档svg 适合需要缩放的场景jpg 有损不推荐用于图表。5. 避坑与排查跑这份源码时最容易翻车的五个地方5.1 中文乱码图里全是方块现象标题、轴标签的中文显示成一个个方块。 原因matplotlib 默认字体不含中文字形rcParams 没设或设的字体系统里没有。 解决先确认系统有哪些中文字体再设 font.sans-serif。Linux 上可以用 fc-list :langzh 查。设完如果还不行删掉 matplotlib 缓存目录~/.matplotlib 或 ~/.cache/matplotlib重启内核。5.2 groupby 后列名丢失现象groupby 聚合后想取某一列报 KeyError。 原因groupby 后分组列变成索引聚合结果可能是多级索引。 解决聚合后加 reset_index()把索引还原成列。或者用 as_indexFalse 参数。brand_stat df.groupby(brand, as_indexFalse)[amount].sum()5.3 数据类型不对导致聚合失败现象销售额求和结果是一串字符串拼接或者排序结果不对。 原因读取时数值列被识别成 object字符串常见于数字里混了逗号或空格。 解决读取后检查 dtypes用 pd.to_numeric 强制转换。df[amount] pd.to_numeric(df[amount], errorscoerce)errorscoerce 把转不了的变成 NaN再统一处理缺失。5.4 饼图标签重叠现象品类多的时候饼图标签挤在一起完全看不清。 原因饼图不适合超过 5-6 个类别。 解决换成横向柱状图或者把占比小的合并成“其他”。如果非要用饼图用 autopct 显示百分比标签放图例里。ax.pie(cat_stat.values, labelsNone, autopct%1.1f%%) ax.legend(cat_stat.index, loccenter left, bbox_to_anchor(1, 0.5))5.5 图片保存后白边过大现象导出的图四周白边很宽放进文档里显得很小。 原因默认保存不裁剪画布边距保留。 解决savefig 加 bbox_inchestight同时画图时用 tight_layout。如果还有白边检查 figsize 比例是否和数据区域匹配。6. 进阶技巧把这份源码改成能复用的分析模板跑通一次不算本事能改成换个数据集就能用的模板才算真正掌握。我的习惯是把分析逻辑拆成函数配置和逻辑分离。def load_and_clean(path, encodinggbk): df pd.read_csv(path, encodingencoding) df.columns [c.strip() for c in df.columns] df df.drop_duplicates() df df.dropna(subset[brand, amount, qty]) df df[(df[price] 0) (df[qty] 0)] return df def top_n_by(df, group_col, value_col, n10): return (df.groupby(group_col)[value_col] .sum() .sort_values(ascendingFalse) .head(n)) def plot_barh(series, title, xlabel, filename): fig, ax plt.subplots(figsize(8, 6)) ax.barh(series.index[::-1], series.values[::-1], color#55a868) ax.set_xlabel(xlabel) ax.set_title(title) plt.tight_layout() plt.savefig(filename, dpi150, bbox_inchestight) plt.close()逻辑说明load_and_clean 把读取和清洗封装换数据集只改列名映射。top_n_by 是通用排名函数group_col 和 value_col 参数化。plot_barh 封装横向柱状图plt.close() 释放内存批量画图时不会越画越卡。参数说明n 默认 10按需传。filename 带扩展名。如果要做成命令行工具可以用 argparse 接收路径和参数这里不展开。再进一步可以把所有图表输出到一个 HTML 报告里用 base64 嵌入图片或者用 pandas 的 to_html 输出表格。但那是另一个话题了。这份约 400 行的源码价值不在行数在于它把“读取-清洗-聚合-可视化”这条链路走完整了。我见过太多人卡在清洗那一步就放弃或者图出来了但参数没调好放进报告里自己都嫌弃。真正要练的是每换一个数据集能快速判断哪些列要清洗、哪些图合适、参数怎么设。这个判断力比记住某个 API 重要得多。希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑