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

发布时间:2026/10/12 17:02:47

Python美妆销售数据分析实战:从清洗到可视化 简介这份Python示例源码面向数据分析初学者与高校学生以天猫双十一美妆销售数据为对象演示从数据清洗、转换到可视化呈现的完整分析流程适合用作课程大作业或数据分析入门练手项目。压缩包共17个文件约5.08MB包含py主程序、ipynb与html分析笔记、csv与xlsx原始及清洗后数据、png图表、xml配置与url快捷方式等覆盖代码、数据与文档多种类型便于直接运行与对照学习。目前已有149人学习下载。源码约400行重点运用pandas完成分组、排序、筛选与聚合并借助matplotlib绘制直方图、折线图、散点图等展示品牌、类别、价格区间等维度的销售变化同时涉及数据预处理与自动化分析思路可帮助读者掌握电商数据清洗、可视化与结果解读的完整方法。1. 一份约400行的美妆销售数据分析源码到底能跑出什么结论每年大促结束后台都会堆出一份几十万行的订单明细。字段看着齐全下单时间、商品名、品类、单价、销量、省份、渠道。可真要回答“哪个品类贡献了最多销售额”“哪个价格带卖得最好”“复购集中在哪几天”很多人第一反应还是把表丢进表格软件拉几个透视表然后对着屏幕发呆。这份约400行的 Python 示例源码解决的正是这个尴尬它用 matplotlib 加基础数据分析把一份电商美妆销售明细从清洗一路做到可视化结论。它适合两类人——正在做大作业、需要一份能跑通、能改、能讲清楚逻辑的学生以及刚接手电商数据、想用最短路径搭出一套分析脚本的初级数据从业者。核心不是代码多高级而是把“数据进、结论出”这条链路走完整。2. 先想清楚分析目标美妆销售数据要回答哪几个问题2.1 从业务问题倒推字段需求拿到一份销售明细不要急着写read_csv。先问自己这份数据要回答什么。美妆电商大促场景下最常被追问的无非四类问题。第一类是品类结构哪个品类卖得好是护肤、彩妆还是香水。第二类是价格带分布销量集中在哪个价位是百元以下还是三百以上。第三类是时间节奏大促当天和预热期的销量曲线长什么样。第四类是地域差异哪些省份贡献了主要销售额。这四类问题决定了你至少需要这些字段订单编号、下单时间、商品名称、品类、单价、销量、省份、渠道。如果原始表里没有品类就得从商品名称里做关键词映射比如含“精华”“面霜”的归到护肤含“口红”“粉底”的归到彩妆。这一步是后面所有分析的根基字段缺了图再漂亮也是空中楼阁。我一般会先写一个字段盘点的小脚本把列名、缺失率、唯一值数量打印出来确认哪些字段能用、哪些要补。这个习惯能省掉后面大量返工。2.2 数据清洗的三个必做动作清洗不是可选项。美妆销售数据常见的脏法有三种单价带货币符号、销量是字符串、时间格式不统一。下面这段代码是我常用的清洗骨架约400行的源码里也是类似结构。import pandas as pd import numpy as np # 读取原始销售明细注意编码电商导出常见 utf-8-sig 或 gbk df pd.read_csv(sales_raw.csv, encodingutf-8-sig) # 1. 列名统一去掉空格转小写避免后续调用时找不到列 df.columns [c.strip().lower().replace( , _) for c in df.columns] # 2. 单价清洗去掉 ¥、$ 等符号转成浮点数 df[price] ( df[price] .astype(str) .str.replace(r[^\d.], , regexTrue) .replace(, np.nan) .astype(float) ) # 3. 销量清洗转数值无法转换的置为 NaN df[qty] pd.to_numeric(df[qty], errorscoerce) # 4. 时间统一兼容多种格式统一转 datetime df[order_time] pd.to_datetime(df[order_time], errorscoerce) # 5. 丢弃关键字段缺失的行避免后续聚合被污染 df df.dropna(subset[price, qty, order_time]) # 6. 新增销售额字段后续所有金额分析都基于它 df[amount] df[price] * df[qty] print(df.shape) print(df[[price, qty, amount]].describe())这段代码的逻辑很直白先把列名规整避免大小写和空格导致的 KeyError再用正则把单价里的非数字字符清掉这一步能救回大量因为货币符号而变成 object 类型的列销量用to_numeric加coerce把脏值变成 NaN 而不是直接报错时间用to_datetime统一后面才能按天、按小时聚合。最后新增amount字段这是所有销售额分析的唯一口径。参数上encoding要根据实际文件调整中文环境常见utf-8-sig和gbk读出来乱码就换。errorscoerce是清洗时的后悔药宁可先变 NaN也不要让一行脏数据把整个脚本打断。dropna的 subset 只锁关键字段不要全列 drop否则容易把有效行误删。2.3 品类映射没有品类字段时怎么补很多电商导出表只有商品名称没有品类。这时候需要做关键词映射。常见做法是维护一个字典把商品名里的关键词映射到品类。# 品类关键词映射实际项目里可以放到单独的 json 或 csv 里维护 category_map { 精华: 护肤, 面霜: 护肤, 面膜: 护肤, 口红: 彩妆, 粉底: 彩妆, 眼影: 彩妆, 香水: 香水, 香氛: 香水, } def map_category(name): if not isinstance(name, str): return 其他 for kw, cat in category_map.items(): if kw in name: return cat return 其他 df[category] df[product_name].apply(map_category) print(df[category].value_counts())映射逻辑是逐关键词匹配命中即返回。这里有个坑关键词顺序会影响结果比如“香水口红”这种组合名先命中哪个取决于字典顺序。实际使用时建议把更具体的关键词放前面。映射完一定要value_counts看一眼“其他”占比过高说明字典覆盖不够需要补词。3. 用 matplotlib 把结论画出来四张图讲清销售结构3.1 品类销售额柱状图与占比饼图清洗完数据第一张图我一般画品类销售额。柱状图看绝对值饼图看占比两张配合能快速判断结构。import matplotlib.pyplot as plt # 设置中文字体Windows 常用 SimHeiMac 常用 Arial Unicode MS plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False # 按品类聚合销售额 cat_amount df.groupby(category)[amount].sum().sort_values(ascendingFalse) fig, axes plt.subplots(1, 2, figsize(12, 5)) # 左图柱状图 axes[0].bar(cat_amount.index, cat_amount.values, color#4C72B0) axes[0].set_title(各品类销售额) axes[0].set_ylabel(销售额) for i, v in enumerate(cat_amount.values): axes[0].text(i, v, f{v:.0f}, hacenter, vabottom, fontsize9) # 右图饼图 axes[1].pie(cat_amount.values, labelscat_amount.index, autopct%1.1f%%, startangle90) axes[1].set_title(品类销售额占比) plt.tight_layout() plt.savefig(category_amount.png, dpi150) plt.show()groupby加sum是最基础的聚合sort_values保证图里顺序清晰。柱状图上加数值标签是为了让读者不用猜高度。饼图的autopct控制百分比格式startangle90让第一块从正上方开始视觉上更稳。dpi150是保存图片的清晰度大作业里够用再高文件会变大。中文字体是 matplotlib 最常见的翻车点。如果图里中文变成方框先确认系统里有没有对应字体再检查font.sans-serif是否设置正确。Linux 环境下可能需要额外指定字体路径。3.2 价格带分布用分箱看销量集中区价格带分析能把“卖得好”拆成“哪个价位卖得好”。常见做法是用pd.cut分箱。# 定义价格带边界根据实际数据调整 bins [0, 50, 100, 200, 300, 500, 1000, np.inf] labels [0-50, 50-100, 100-200, 200-300, 300-500, 500-1000, 1000] df[price_band] pd.cut(df[price], binsbins, labelslabels, rightFalse) # 按价格带聚合销量和销售额 band_stat df.groupby(price_band, observedTrue).agg( qty_sum(qty, sum), amount_sum(amount, sum) ).reset_index() fig, ax1 plt.subplots(figsize(10, 5)) ax1.bar(band_stat[price_band], band_stat[qty_sum], color#55A868, label销量) ax1.set_xlabel(价格带) ax1.set_ylabel(销量) ax1.set_title(各价格带销量与销售额) ax2 ax1.twinx() ax2.plot(band_stat[price_band], band_stat[amount_sum], color#C44E52, markero, label销售额) ax2.set_ylabel(销售额) fig.legend(locupper right) plt.tight_layout() plt.savefig(price_band.png, dpi150) plt.show()pd.cut的bins和labels要一一对应rightFalse表示左闭右开避免边界值重复计数。双轴图里左轴画销量柱状右轴画销售额折线能同时看量和额。observedTrue在 pandas 新版本里能避免空类别报错。价格带边界不是拍脑袋定的。美妆品类常见价格带在 50 到 300 之间但具体要看数据分布。我一般先画一个价格直方图看数据集中在哪再定分箱边界。分箱太粗看不出差异太细又会让图变得零碎。3.3 时间趋势按天聚合看大促节奏大促数据的时间维度最有价值。按天聚合销售额能看出预热、爆发、返场三个阶段。# 按天聚合 df[order_date] df[order_time].dt.date daily df.groupby(order_date)[amount].sum().reset_index() daily[order_date] pd.to_datetime(daily[order_date]) fig, ax plt.subplots(figsize(12, 5)) ax.plot(daily[order_date], daily[amount], markero, color#4C72B0) ax.set_title(每日销售额趋势) ax.set_xlabel(日期) ax.set_ylabel(销售额) ax.tick_params(axisx, rotation45) # 标注峰值 peak_idx daily[amount].idxmax() peak_date daily.loc[peak_idx, order_date] peak_val daily.loc[peak_idx, amount] ax.annotate(f峰值 {peak_val:.0f}, xy(peak_date, peak_val), xytext(peak_date, peak_val * 1.1), arrowpropsdict(arrowstyle-)) plt.tight_layout() plt.savefig(daily_trend.png, dpi150) plt.show()dt.date把时间戳截断到天groupby后得到每日汇总。折线图加marker让每个数据点可见。annotate标注峰值是大作业里很讨巧的一笔能让结论一眼可见。rotation45防止日期标签重叠。如果数据量很大按天聚合前可以先确认时间范围避免把非大促期间的数据混进来。常见做法是用df df[(df[order_time] start) (df[order_time] end)]先截取活动周期。3.4 省份销售额横向条形图更适合长标签省份名称较长横向条形图比纵向柱状图更易读。# 按省份聚合取前 10 province_amount df.groupby(province)[amount].sum().sort_values(ascendingTrue).tail(10) fig, ax plt.subplots(figsize(10, 6)) ax.barh(province_amount.index, province_amount.values, color#8172B3) ax.set_title(销售额前 10 省份) ax.set_xlabel(销售额) for i, v in enumerate(province_amount.values): ax.text(v, i, f{v:.0f}, vacenter, fontsize9) plt.tight_layout() plt.savefig(province_amount.png, dpi150) plt.show()barh画横向条sort_values(ascendingTrue)配合tail(10)让最大值在最上面。数值标签用ax.text加在条末端vacenter保证垂直居中。省份字段如果有“省”“市”后缀不统一清洗时可以用str.replace去掉避免同一个省被拆成两条。4. 避坑与排查这类分析脚本最容易翻车的五个地方4.1 中文显示成方框现象图里标题、标签全是方框英文正常。原因matplotlib 默认字体不含中文。解决设置plt.rcParams[font.sans-serif]为系统里存在的中文字体Windows 用 SimHeiMac 用 Arial Unicode MSLinux 需要先确认字体已安装。设置完记得加axes.unicode_minusFalse否则负号也会出问题。4.2 金额算出来偏大或偏小现象销售额和后台对不上。原因单价字段里混了“元”“¥”等符号或者销量字段里有“件”“个”导致转数值失败被置 NaN聚合时被忽略。解决清洗后先describe看 price 和 qty 的 min、max 是否合理再抽查几行原始数据比对。amount字段一定要在清洗完成后统一计算不要在原始表上直接乘。4.3 时间聚合出现重复日期现象按天聚合后同一天出现两条记录。原因order_time里混了不同时区或格式to_datetime解析后有的带时间有的不带dt.date截断后仍可能因原始格式差异产生重复。解决统一用pd.to_datetime(..., errorscoerce)后再dt.normalize()或dt.floor(D)统一到天然后groupby时用as_indexFalse并检查唯一值。4.4 饼图类别太多挤成一团现象饼图标签重叠看不清。原因品类或省份唯一值太多每个扇形太小。解决先聚合取前 N 个剩下的归为“其他”。饼图适合 5 到 7 个类别超过就换柱状图或横向条形图。不要为了“全”而牺牲可读性。4.5 保存图片空白现象savefig出来的图是空白或只有一部分。原因savefig在show之后调用或者tight_layout没生效导致内容被裁掉。解决先savefig再show保存时加bbox_inchestight自动裁剪边距。如果用了subplots确认tight_layout在保存前调用。5. 进阶技巧让这份分析脚本从作业变成可复用工具5.1 把配置抽出来换数据不用改代码约400行的脚本如果所有参数都写死在代码里换一份数据就要改十几处。我的习惯是把路径、价格带边界、品类字典、图表尺寸都抽到一个配置字典或单独的 json 文件里。import json config { data_path: sales_raw.csv, encoding: utf-8-sig, price_bins: [0, 50, 100, 200, 300, 500, 1000, 999999], price_labels: [0-50, 50-100, 100-200, 200-300, 300-500, 500-1000, 1000], top_n_province: 10, figsize: [12, 5], dpi: 150 } with open(config.json, w, encodingutf-8) as f: json.dump(config, f, ensure_asciiFalse, indent2)读取时用json.load加载后续所有函数从 config 取值。这样换数据只需改 json不用动分析逻辑。大作业里这么做答辩时也能讲出“配置与逻辑分离”的设计意识。5.2 用函数封装每张图方便单独调用把每张图的绘制逻辑封装成函数输入是 DataFrame 和配置输出是图片路径。这样主流程只剩读取、清洗、调用几个函数结构清晰也方便单独调试某一张图。def plot_category_amount(df, config): cat_amount df.groupby(category)[amount].sum().sort_values(ascendingFalse) fig, axes plt.subplots(1, 2, figsizeconfig[figsize]) axes[0].bar(cat_amount.index, cat_amount.values) axes[0].set_title(各品类销售额) axes[1].pie(cat_amount.values, labelscat_amount.index, autopct%1.1f%%) axes[1].set_title(品类销售额占比) plt.tight_layout() path category_amount.png plt.savefig(path, dpiconfig[dpi], bbox_inchestight) plt.close() return pathplt.close()很重要批量画图时不关闭会内存泄漏。返回路径方便后续汇总报告。函数只做一件事参数从 config 取这样每个函数都能独立测试。5.3 验证分析结果是否可信的三个检查图跑出来不等于结论可信。我一般做三个检查。第一总额核对df[amount].sum()和后台总销售额比差异超过 5% 就要查清洗逻辑。第二抽样核对随机抽 5 行手工算price * qty是否等于amount。第三分布检查describe看 price 和 qty 的 max 是否离谱比如单价出现 999999多半是脏数据没清干净。这三个检查花不了几分钟但能挡住大部分“图很漂亮、结论全错”的情况。血泪经验是曾经有一次单价字段里混了“1,299”这种带千分位的值正则没处理逗号结果 1299 变成 129销售额直接少了一个数量级。从那以后我每次清洗完都会先看 describe 的 max。5.4 从作业到工具加一个命令行入口如果想让脚本能直接命令行调用加一个argparse入口传入数据路径和输出目录。import argparse def main(): parser argparse.ArgumentParser() parser.add_argument(--data, requiredTrue, help原始销售数据路径) parser.add_argument(--out, defaultoutput, help图片输出目录) args parser.parse_args() df load_and_clean(args.data) plot_category_amount(df, config) plot_price_band(df, config) plot_daily_trend(df, config) plot_province_amount(df, config) print(f分析完成图片已保存到 {args.out}) if __name__ __main__: main()这样换数据只需python analyze.py --data new_sales.csv --out result不用改代码。大作业里加上这个演示时能直接跑观感会好很多。参数requiredTrue强制传数据路径避免跑空。我自己的习惯是任何分析脚本只要超过 200 行就一定要抽配置、封函数、留入口。不然过两周回头看自己都记不清哪段代码对应哪张图。这份约400行的美妆销售分析源码价值不在于代码量而在于它把清洗、聚合、可视化、排查这条链路完整走了一遍。你把它跑通改一改配置换一份自己的数据就能得到一套能讲、能复现、能继续扩展的分析流程。希望帮到你。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/10/12 17:02:47

Python实现写真人脸数据采集、检测与DCGAN生成全流程

简介:本资源是一套基于Python实现的美女写真图像处理与生成实践项目,面向机器学习初学者及计算机视觉兴趣者,聚焦人脸检测、数据采集与生成式建模三大技术环节。项目整合了网络爬虫获取写真图集、OpenCV/Dlib等库实现脸部识别与裁剪、DCGAN模…

2026/10/12 16:57:46

网络协议实训任务书设计:从抓包到读懂包的完整指南

简介:面向网络协议学习与实训的一份任务书,以Sniffer软件为核心工具,围绕数据包采集与协议分析展开,适合网络工程、计算机等相关专业学生完成实训任务并巩固TCP/IP协议栈认知。任务书涵盖网络协议基本概念、Sniffer工作原理、杂收…

2026/10/12 16:57:46

四种机器学习模型股价预测实战:从数据预处理到滚动回测

简介:面向高校毕业设计、期末大作业和量化入门实践的一份Python股价预测源码项目,围绕股票价格数据建模,实现LR、LSTM、ARIMA、KNN等多种经典模型,并给出了对比分析。代码附详细注释,涵盖数据预处理、模型训练、回测与…

2026/10/12 19:33:30

二阶常系数线性微分方程:特征方程与待定系数法完全指南

先说明一下:这题目我在给学生辅导时被问过无数次。刚开始他们以为只是《高等数学》里一个章节,学完应付考试就完事。直到后来学《大学物理》里的弹簧振子、《电路分析》里的RLC回路、《自动控制原理》里的系统响应,发现全是同一个方程反复出现…

2026/10/12 19:33:30

电商评论情感分析实战:从34000条数据到TF-IDF与朴素贝叶斯模型

简介:这份资源包面向Python开发者、NLP初学者以及正在准备毕业设计或课程设计的学生,提供一套完整的电商买家评论情感分析实践项目。内容覆盖从数据预处理到模型训练的全流程,帮助读者理解情感分析的基本原理并动手构建可运行的系统。压缩包共…

2026/10/12 19:33:30

Python股票量化系统源码拆解:从回测到实盘的工程链路避坑指南

简介:Python股票量化系统源码及教程是一套实战型学习资源,面向具备基础Python知识并希望进入量化投资领域的读者,解决从零搭建股票量化策略系统、理解量化分析流程的问题。资源包共244个文件,压缩后约3.53MB,包含71个p…

2026/10/12 19:33:30

AI+基础教育从试点到全面铺开:落地路径与实操避坑指南

1. 从试点到全面铺开:AI基础教育落地路径全拆解这两年基础教育圈子里聊得最多的话题之一,就是AI到底怎么进课堂、进教研、进日常管理。我前后参与过三个区域的AI教育试点项目,从最初只有两三所学校“吃螃蟹”,到后来全区几十所学校…

2026/10/12 19:33:30

Python金融风控建模实战:从数据预处理到可审计模型服务

简介:本资源是一套面向金融风控领域初学者与课程设计者的Python机器学习实战项目,聚焦信贷风险评估建模全流程,适用于毕业设计、期末大作业及课程实践。项目包含完整可运行代码、详细文档说明与清晰注释,覆盖数据清洗、特征工程、…

2026/10/11 0:02:13

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/12 12:01:26

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/12 12:54:07

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/12 0:04:22

绝缘子缺陷检测数据集清洗与工业级训练实战指南

简介:本资源是面向电力AI研发人员、工业视觉工程师及智能巡检系统开发者的绝缘子缺陷检测专用YOLO格式数据集,解决无人机航拍场景下绝缘子破损、污闪、积雪等9类典型缺陷的精准识别与定位难题。数据集共2139张真实巡检图像(含训练/验证/测试集…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑