拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Python数据清洗与可视化:Pandas+Matplotlib完整实战案例

当业务方丢来一份“很不干净”的 Excel 表格要求第二天给出销售趋势图和分类占比图时大多数人的第一反应是打开 WPS 或 Excel 手动拉透视表、插入图表。但如果数据量上万行、字段错乱、日期格式五花八门、还夹杂空值和重复记录手动操作很快就会变成一场噩梦。本文围绕“9.5.1 数据整理与图表生成案例”这一主题以 Python 生态中最常用的 pandas 和 matplotlib 为核心工具完整演示从原始数据清洗、字段加工、分组聚合到图表生成的全流程。如果你的日常工作也经常和数据打交道或者正准备学习数据分析这篇文章非常适合收藏后照着操作。为了让案例更贴近真实场景我会使用一份模拟的电商订单数据数据里刻意加入了缺失值、重复记录、格式不统一等问题。这样既能展示数据整理的必要性也能让图表生成的结果更有说服力。1. 数据整理与图表生成的核心概念1.1 什么是数据整理数据整理Data Wrangling是指将原始、杂乱的数据转换为适合分析和可视化表达的结构化数据的过程。它并不是简单地把 Excel 里的列复制到新表里而是包含:整理动作说明常见操作数据探查观察数据的整体结构、字段类型、数据量head()、info()、describe()数据清洗处理缺失值、重复值、异常值dropna()、fillna()、drop_duplicates()数据转换调整字段格式、单位、类型astype()、pd.to_datetime()数据聚合按维度统计汇总构造业务指标groupby()、pivot_table()数据合并将多张表按关键字段拼接merge()、concat()在实际数据分析项目中数据整理往往占整个流程 60% 以上的时间。一个结构化、干净的数据集是后续绘图和分析结论可靠的前提。1.2 图表生成的定位图表生成是基于整理后的数据使用可视化工具将数据中的趋势、占比、分布、对比关系直观呈现。图表不是数据的装饰品而是一种沟通语言。例如:折线图适合表达随时间变化的趋势。柱状图适合对比不同类别的数值差异。饼图适合展示各部分占整体的比例。散点图适合观察两个变量的相关性。Python 中最常用的可视化库是 Matplotlib 和 Seaborn。Matplotlib 灵活度高几乎可以控制图表的每个细节Seaborn 则在统计图表方面更简洁、美观。本案例以 Matplotlib 为主同时穿插 pandas 内置的绘图接口让代码更简洁。1.3 两者之间的关系数据整理决定了图表能“画什么”图表生成决定了数据“怎么表现”。如果数据没有清洗干净画出来的图表可能包含缺失值断点、错误数值导致的异常峰值甚至完全错误的结论。举个简单的例子一份包含“2024年1月1日”“2024/01/02”“2024-01-03”三种日期格式的销售明细如果不进行统一转换直接按日期分组绘图结果会被拆成多个互不相干的类别折线图自然无法形成连续的趋势。所以本文虽然叫“数据整理与图表生成案例”但本质上是一条完整的分析流水线读取 → 探查 → 清洗 → 转换 → 聚合 → 可视化。2. 环境准备与版本说明2.1 运行环境本文示例以 Python 3.10 环境为基础操作系统为 Windows 10/11但代码在 macOS 和 Linux 下同样适用。主要依赖库如下库名称用途安装命令pandas数据读取、清洗、聚合pip install pandasmatplotlib图表绘制与导出pip install matplotlibnumpy数值计算辅助pip install numpyopenpyxl读取和写入 Excel 文件pip install openpyxl需要注意的是pandas 的版本迭代较快不同版本的 API 会有细微差异。如果你使用 pandas 2.x 版本示例代码可以直接运行如果使用 1.x 版本大部分功能也兼容。安装时建议使用如下命令一次性装齐pip install pandas numpy matplotlib openpyxl如果下载速度较慢可以切换为国内镜像源例如pip install pandas numpy matplotlib openpyxl -i https://pypi.tuna.tsinghua.edu.cn/simple2.2 准备示例数据为了模拟真实情况我在本案例中手工构造了一份电商订单数据order_data.csv。字段包括order_id订单编号order_date下单日期category商品分类region销售地区sales销售额cost成本quantity销量这份数据中故意包含空值、重复行、日期格式不统一等“脏数据”便于展示数据整理过程。你可以先复制下面的 CSV 内容保存为order_data.csv文件order_id,order_date,category,region,sales,cost,quantity 1001,2024-01-01,电子产品,华东,1200,800,2 1002,2024/01/02,服装,华南,350,150,3 1003,2024-01-03,食品,华东,88.5,50,5 1004,2024-01-03,电子产品,华北,2400,1600,1 1005,2024-01-04,图书,华北,79,40,2 1006,2024-01-05,服装,西南,420,200,2 1007,2024-01-05,电子产品,华南,1899,1200,1 1008,,食品,华中,156,90,4 1009,2024-01-07,图书,华东,128,60,1 1010,2024-01-08,电子产品,东北,3299,2100,1 1001,2024-01-01,电子产品,华东,1200,800,2 1011,2024/01/09,服装,华北,560,280,4 1012,2024-01-10,食品,华南,99.9,55,6这是一个非常典型的“脏乱差”数据集日期格式不统一、存在空值、存在完全重复的行。后续所有整理操作都会围绕这份数据展开。3. 数据整理核心操作详解在正式画图之前我们需要先把数据处理干净。本节会逐步讲解 pandas 中最常用的数据整理操作每个操作都配有代码示例和结果说明。3.1 读取数据与初步探查读取 CSV 文件使用pd.read_csv()如果需要读取 Excel 文件可以使用pd.read_excel()。数据读取之后第一件事不是急着清洗而是先“看”。import pandas as pd # 读取 CSV 文件指定文件编码为 UTF-8 df pd.read_csv(order_data.csv, encodingutf-8) # 查看前 5 行 print(df.head())输出结果order_id order_date category region sales cost quantity 0 1001 2024-01-01 电子产品 华东 1200.0 800.0 2 1 1002 2024/01/02 服装 华南 350.0 150.0 3 2 1003 2024-01-03 食品 华东 88.5 50.0 5 3 1004 2024-01-03 电子产品 华北 2400.0 1600.0 1 4 1005 2024-01-04 图书 华北 79.0 40.0 2通过info()方法可以查看每列的数据类型和非空数量这一步非常重要print(df.info())输出结果class pandas.core.frame.DataFrame RangeIndex: 13 entries, 0 to 12 Data columns (total 7 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 order_id 13 non-null int64 1 order_date 12 non-null object 2 category 13 non-null object 3 region 13 non-null object 4 sales 12 non-null float64 5 cost 12 non-null float64 6 quantity 13 non-null int64 dtypes: float64(2), int64(2), object(3)从输出可以看到order_date是 object 类型说明日期没有解析为时间类型sales和cost存在缺失值。这些信息是后续清洗的依据。3.2 缺失值处理缺失值是数据清洗中最常见的问题。处理方式通常有两种删除和填充。对于销售金额缺失的记录如果整行其他字段齐全可以先看看缺失比例有多大。# 统计每列缺失值数量 print(df.isnull().sum())输出结果order_id 0 order_date 1 category 0 region 0 sales 1 cost 1 quantity 0 dtype: int64本案例中缺失值数量较少直接删除缺失行对整体影响不大。使用dropna()可以删除包含缺失值的行# 删除包含空值的行 df df.dropna().copy() # 再次确认缺失值情况 print(df.isnull().sum())需要注意dropna()默认会删除只要含任意一个空值的行。如果只想删除指定列存在空值的行可以传入subset参数# 只根据 order_date 和 sales 两列判断是否删除 df df.dropna(subset[order_date, sales]).copy()实际项目中缺失值处理策略取决于业务含义数值型字段可以用均值、中位数填充分类字段常用众数填充时间字段如果缺失往往只能删除或通过其他字段推算。3.3 重复值处理重复数据会让统计结果虚高必须清理。使用duplicated()可以判断哪些行是重复的返回的是布尔序列drop_duplicates()则直接删除重复行。# 查看重复行数量 print(df.duplicated().sum()) # 删除重复行 df df.drop_duplicates().copy() # 删除后查看数据量 print(f处理后数据量: {len(df)})输出结果1 处理后数据量: 12这里删除的就是order_id1001的重复记录。drop_duplicates()默认会保留第一次出现的行如果希望保留最后一次出现的行可以设置keeplast。如果判断重复的规则不是整行完全一致而是某几列相同也可以传入subset参数# 以 order_id 为唯一判断依据 df df.drop_duplicates(subset[order_id]).copy()3.4 日期格式统一原始数据中的日期格式有三种2024-01-01、2024/01/02和空值。虽然经过dropna()后空值已经删除但两种格式仍然存在。如果不统一分组聚合时会得到错误的维度。使用pd.to_datetime()可以将列转换为统一的时间类型# 统一转换日期格式 df[order_date] pd.to_datetime(df[order_date]) # 查看转换后的结果 print(df[order_date].dtype) print(df[[order_id, order_date]])pd.to_datetime()会自动识别大多数常见日期格式包括2024-01-01、2024/01/02、2024.01.03等。如果希望提取年、月、日等字段也可以通过dt访问器完成df[year] df[order_date].dt.year df[month] df[order_date].dt.month df[weekday] df[order_date].dt.dayofweek # 0周一, 6周日3.5 字段计算与加工很多业务指标不是原始数据里直接有的而是通过计算得到的。本案例中利润profit可以通过销售额减去成本得到# 计算利润字段 df[profit] df[sales] - df[cost] # 添加利润率的辅助字段 df[profit_rate] (df[profit] / df[sales] * 100).round(2) print(df[[order_id, sales, cost, profit, profit_rate]])输出结果order_id sales cost profit profit_rate 0 1001 1200.0 800.0 400.0 33.33 1 1002 350.0 150.0 200.0 57.14 2 1003 88.5 50.0 38.5 43.50 ...这里使用向量化运算效率远高于遍历每一行。pandas 的向量化特性也是它比 Excel 手写公式更适合大批量数据处理的原因之一。3.6 分组聚合数据整理完成后需要按维度汇总才能用于图表绘制。groupby()是 pandas 中最核心的聚合方法。例如按商品分类统计销售额总和# 按分类分组计算销售额总和 category_sales df.groupby(category)[sales].sum().reset_index() print(category_sales)输出结果category sales 0 图书 207.0 1 服装 930.0 2 食品 344.4 3 电子产品 8798.0reset_index()的作用是把分组键从索引变回普通列便于后续绘图。如果不调用它category会成为索引图表生成时处理起来会麻烦一些。再比如按日期统计每天的销售总额和订单数# 按日期分组统计销售额和订单数量 daily_sales df.groupby(order_date).agg( total_sales(sales, sum), total_quantity(quantity, sum), order_count(order_id, count) ).reset_index() print(daily_sales)输出结果order_date total_sales total_quantity order_count 0 2024-01-01 1200.0 2 1 1 2024-01-02 350.0 3 1 2 2024-01-03 2488.5 6 2 ...上述agg()方法支持为不同字段指定不同的聚合函数在很多分析场景中非常实用。4. 图表生成完整实战案例数据整理完毕接下来进入图表生成环节。本案例会绘制四种常用图表每日销售趋势折线图、分类销售额柱状图、地区销售占比饼图、利润与销售额对比图。4.1 创建项目结构为了方便管理建议按照以下目录结构组织代码和输出文件data_analysis_case/ │ ├── data/ │ └── order_data.csv │ ├── output/ │ ├── daily_trend.png │ ├── category_sales.png │ ├── region_share.png │ └── profit_compare.png │ └── analysis.py如果目录不存在可以提前创建或者在 Python 代码中通过os.makedirs()自动创建输出目录import os os.makedirs(output, exist_okTrue)4.2 导入依赖与读取数据将前面的数据整理流程封装成一个函数便于后续复用import os import pandas as pd import matplotlib.pyplot as plt # 解决中文显示问题 plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False os.makedirs(output, exist_okTrue) def load_and_clean_data(file_path): 读取原始数据并完成基础清洗 df pd.read_csv(file_path, encodingutf-8) df df.dropna() df df.drop_duplicates() df[order_date] pd.to_datetime(df[order_date]) df[profit] df[sales] - df[cost] return df df load_and_clean_data(data/order_data.csv) print(f数据清洗完成当前数据量: {len(df)})4.3 绘制每日销售趋势折线图折线图适合展示时间序列数据的变化趋势。这里需要先按日期分组绘制时把日期作为 x 轴销售额作为 y 轴。# 按日期汇总销售额 daily_sales df.groupby(order_date)[sales].sum().reset_index() # 绘制折线图 plt.figure(figsize(10, 5)) plt.plot(daily_sales[order_date], daily_sales[sales], markero, linewidth2) plt.title(每日销售趋势) plt.xlabel(日期) plt.ylabel(销售额元) plt.grid(True, linestyle--, alpha0.6) plt.tight_layout() plt.savefig(output/daily_trend.png, dpi150) plt.show()关键参数解释markero在数据点上显示圆形标记便于观察每个具体值。linewidth2设置线条粗细。grid(True, linestyle--, alpha0.6)添加半透明虚线网格提高可读性。dpi150设置导出图片分辨率清晰度足够放到 PPT 或文档中。执行后会生成output/daily_trend.png图片你可以打开查看趋势是否平滑、是否存在异常峰值。4.4 绘制分类销售额柱状图柱状图适合进行类别间的横向对比。在绘制前建议先按销售额降序排列这样图中柱子的顺序更直观。# 按分类汇总销售额并排序 category_sales df.groupby(category)[sales].sum().sort_values(ascendingFalse).reset_index() # 绘制柱状图 plt.figure(figsize(8, 5)) bars plt.bar(category_sales[category], category_sales[sales], color[#4C72B0, #55A868, #C44E52, #8172B2]) plt.title(各分类销售额对比) plt.xlabel(商品分类) plt.ylabel(销售额元) # 在柱子上方显示数值 for bar in bars: height bar.get_height() plt.text(bar.get_x() bar.get_width() / 2, height 30, f{height:.0f}, hacenter, vabottom, fontsize10) plt.tight_layout() plt.savefig(output/category_sales.png, dpi150) plt.show()这里使用了plt.text()在柱子顶部添加数值标签。如果不加这个标签柱状图的精确数值只能靠目测不够专业。4.5 绘制地区销售占比饼图饼图适合展示各分类的占比关系。虽然在某些场景下饼图受到争议但在部分占比对比不复杂的情况下它仍然直观易懂。# 按地区汇总销售额 region_sales df.groupby(region)[sales].sum().reset_index() # 绘制饼图 plt.figure(figsize(8, 8)) plt.pie(region_sales[sales], labelsregion_sales[region], autopct%.1f%%, startangle90, explode[0.05, 0.05, 0.05, 0.05, 0.05, 0.05]) plt.title(各地区销售占比) plt.tight_layout() plt.savefig(output/region_share.png, dpi150) plt.show()参数说明autopct%.1f%%在扇区中心显示百分比保留一位小数。startangle90从 90 度方向开始绘制便于调整起始角度。explode将扇区向外拉开一段距离形成分离效果。如果地区数量较多饼图上的标签会重叠此时更建议改用水平柱状图。4.6 绘制利润与销售额对比图为了对比不同分类的销售额和利润情况可以使用分组柱状图。制作分组柱状图的技巧是控制柱子的位置偏移。import numpy as np # 按分类汇总销售额和利润 category_summary df.groupby(category).agg( total_sales(sales, sum), total_profit(profit, sum) ).reset_index() # 排序按销售额降序 category_summary category_summary.sort_values(total_sales, ascendingFalse) x np.arange(len(category_summary)) width 0.35 plt.figure(figsize(10, 6)) bars1 plt.bar(x - width / 2, category_summary[total_sales], width, label销售额, color#4C72B0) bars2 plt.bar(x width / 2, category_summary[total_profit], width, label利润, color#55A868) plt.title(各分类销售额与利润对比) plt.xlabel(商品分类) plt.ylabel(金额元) plt.xticks(x, category_summary[category]) plt.legend() plt.tight_layout() plt.savefig(output/profit_compare.png, dpi150) plt.show()np.arange()生成等间距的 x 坐标通过x - width / 2和x width / 2让两组柱子并排显示这是分组柱状图的标准做法。4.7 运行与验证将上述代码整合到同一个analysis.py文件中并在终端执行cd data_analysis_case python analysis.py如果终端没有报错且 output 目录下生成了 4 张图片说明整个流程已经跑通。你也可以在命令中追加plt.close()避免多次绘图时内存占用过高plt.close()5. 常见问题与排查思路在实际运行数据整理和图表生成代码时最容易出现以下几类问题问题现象常见原因解决思路图表中文显示为方块系统中没有匹配的中文字体设置plt.rcParams[font.sans-serif] [SimHei]macOS 可改为[Arial Unicode MS]日期列无法排序日期仍是字符串格式使用pd.to_datetime()转换列类型图表横坐标过于拥挤类别太多或日期跨度大旋转标签plt.xticks(rotation45)或者缩小画布比例柱状图上没有数值标签未调用plt.text()遍历柱子对象读取get_height()添加文本数据量很大时绘图卡顿图表元素过多先聚合再绘图不要直接用明细数据绘图保存的图片背景不透明默认背景是白色设置transparentTrue或调整facecolor参数其中中文显示问题是最常见的。如果你在 PyCharm 或 Jupyter Notebook 中运行代码遇到图上的中文全部变成小方块优先检查当前系统的中文字体可用性。如果你使用的是 macOS 系统可以将字体配置改为plt.rcParams[font.sans-serif] [Arial Unicode MS, PingFang SC, SimHei]如果是在 Linux 服务器上运行可能需要安装中文字体例如sudo apt install fonts-wqy-zenhei安装完成后重启 Python 进程图表中的中文就能正常显示。6. 最佳实践与工程建议6.1 关于数据整理的建议数据整理的目标是“干净、规范、可复现”。为了实现这一点建议遵循以下原则第一把清洗逻辑封装成函数而不是在代码中从头到尾堆命令。函数化的好处是当原始数据更新时只要重新执行一次函数就能得到最新的清洗结果避免复制粘贴导致的遗漏或错误。第二保存中间结果。数据清洗过程中的很多步骤是不可逆的。如果后续发现清洗规则错误重新从原始数据执行成本很高。建议在合理的位置使用to_csv()保留中间数据df.to_csv(output/cleaned_order_data.csv, indexFalse, encodingutf-8-sig)使用utf-8-sig编码而不是utf-8是为了解决 Excel 打开 CSV 文件时中文乱码的问题。这一点在交付数据文件给非技术同事时特别重要。第三要养成查看info()、describe()、isnull().sum()的习惯。数据分析里的很多“坑”不是代码问题而是数据理解问题。6.2 关于图表生成的建议图表生成并不是“用默认参数画出来就行”至少要考虑以下三个维度一是图表类型要匹配业务意图。趋势数据用折线图对比数据用柱状图占比数据用饼图或堆叠柱状图。不要为了好看而选择错误的图表类型。二是配色要克制。默认配色的效果通常够用不要为每个柱子单独指定“五彩斑斓”的颜色。颜色过多会分散注意力反而不利于表达。三是图片导出要满足使用场景。如果图要放进论文或报告建议设置dpi300如果只是放进 PPTdpi150已经足够。导出前用tight_layout()调整布局避免标题或坐标轴标签被截断。6.3 工程化建议如果你的分析脚本需要定期运行可以考虑将路径配置放到代码开头方便修改。使用日志记录每个阶段处理的数据量便于排查问题。绘图前判断数据是否为空避免因空数据导致绘图报错。重要分析结论建议输出文本或 Excel 汇总表而不是只输出图片。一个简单的日志输出示例import logging logging.basicConfig(levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s) logging.info(f数据清洗完成数据量: {len(df)}) logging.info(f输出图表至 output/ 目录)7. 总结与后续学习方向本文以一份包含缺失值、重复记录、日期格式混乱的电商订单数据作为切入点完整演示了 Python 数据整理到图表生成的实战流程。你掌握了 pandas 的缺失值处理、重复值去重、日期转换、字段加工和分组聚合也学会了使用 Matplotlib 绘制折线图、柱状图、饼图和分组对比图。这套方法不仅适用于本案例中的电商订单也可以直接迁移到销售报表、考试分析、用户行为统计、题库正确率分析等场景。接下来你可以继续学习以下方向使用 Seaborn 绘制更美观的统计图表例如热力图、箱线图、回归图。掌握pivot_table数据透视表方法处理更复杂的多维汇总需求。使用 Plotly 或 Pyecharts 绘制交互式图表方便在 Web 端展示分析结果。将分析流程封装成函数或类结合定时任务实现报表自动化生成。数据整理与图表生成是数据分析的基本功也是很多数据岗位面试时的核心考察内容。建议你拿到一份真实数据后不急着画图先按照本文的顺序探查、清洗、转换、聚合最后再思考用什么图表表达。动手运行一遍完整案例比只看文章理解深刻得多。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门