ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python数据整理与图表生成完整案例:从Excel清洗到可视化

Python数据整理与图表生成完整案例:从Excel清洗到可视化 在数据分析流程里最耗时间的往往不是建模而是进入建模之前的数据整理环节。而大量 Excel 手工操作、复制粘贴、逐列删除空值的做法在面对几百 MB 的日志数据或几千行字段混乱的业务表时效率会断崖式下降。这次我们来看一个相对完整的“数据整理与图表生成案例”从一份未清洗的原始数据开始走完字段检查、缺失值处理、类型转换、去重合并再到用 Python 生成可视化图表最终直接输出一份可用于汇报的分析结果。这个案例最值得关注的点有几个第一它不依赖任何商业 BI 工具用 pandas 加 matplotlib/seaborn 就能完成第二它会演示一套可以复用的数据整理流程而不是只针对某一个固定数据集写死脚本第三图表生成部分不是简单画一张折线图而是会处理中文乱码、坐标轴刻度、多子图布局等真实场景里一定会遇到的细节问题第四整套过程支持批量处理只要数据文件按规则命名改一个文件路径就能重复跑第五如果你愿意整理完的数据和生成好的图表都可以通过 FastAPI 暴露成接口方便接到自己的内部工具或定时任务里。本文会带读者完成以下内容搭建一个稳定的 Python 数据处理环境写一个结构清晰的 ETL 脚本完成数据清洗与特征加工生成多张可用于汇报的统计图表最后给出性能观察方法和常见问题排查清单。如果你平时的工作涉及报表数据处理、数据运营分析、毕业设计数据预处理或者只是想把手动 Excel 操作变成可复现的 Python 脚本这篇文章可以收藏备用。1. 核心能力速览在动手之前先把这套“数据整理与图表生成案例”的能力边界列清楚。这样你可以快速判断它适不适合你的场景。能力项说明项目类型数据处理与可视化完整流程案例核心依赖Python 3.9、pandas、matplotlib、seaborn、openpyxl可选 FastAPI主要功能数据加载、字段检查、缺失值处理、类型转换、去重合并、分组统计、图表生成、结果导出推荐硬件普通办公电脑即可内存建议 8G 以上显存占用不涉及 GPU纯 CPU 任务支持平台Windows / macOS / Linux 均可启动方式命令行运行 Python 脚本或 Jupyter Notebook 分步执行是否支持 API可以扩展使用 FastAPI 将整理结果和图表以接口形式输出是否支持批量任务支持遍历目录下多个 CSV/Excel 文件并生成对应图表适合场景报表数据处理、Excel 清洗自动化、运营数据可视化、毕设数据预处理需要说明的是这里不会绑定某一个固定的行业数据集。案例中的代码逻辑是通用的你可以把它当作一个“数据整理模板”来用。实际显存占用、运行耗时和内存消耗需要以你本机的数据量为准但正常办公场景下的数据规模这套脚本不会有压力。2. 适用场景与使用边界2.1 这个案例适合谁如果你是以下三类读者之一这个案例的性价比很高。第一类是数据分析初学者。你大概率已经背过 pandas 的常用函数但拿到一份真实数据时仍然不知道从哪里下手。这个案例给出一条完整的处理链路你可以照着走一遍理解每一步在解决什么问题。第二类是业务侧的数据处理人员。日常需要把 ERP、CRM 系统导出的 Excel 表做合并、去重、格式统一再做成日报周报图表。与其每周手工折腾两小时不如把流程固化成脚本以后双击运行或定时执行即可。第三类是准备毕业设计或项目答辩的学生。数据整理和可视化是论文里最容易被挑刺的部分。用一套规范化的处理流程导出图表一方面你自己心里有底另一方面答辩时也能讲清楚数据是怎么从“原始状态”变成“分析可用状态”的。2.2 不适合什么场景这个案例不适合处理超大规模数据。如果你的数据量是几千万行甚至上亿行pandas 的内存占用会非常夸张。此时应该考虑 PySpark、Dask、Polars 或者直接上数据库做聚合。此外如果你需要的是实时流式数据的清洗和展示这个静态批处理脚本也不匹配。2.3 版权、隐私与安全边界这里必须强调一下数据合规问题。整理的数据如果是公司内部数据处理前要确认脱敏策略。涉及用户手机号、身份证、住址等敏感字段时应该先完成脱敏或直接丢弃。图表如果可能包含业务核心数据要确认使用范围和发布权限避免把内部经营数据直接放到外部平台。同时建议只在本地环境测试运行不要随意将生产数据库的完整权限开放给这类处理脚本。3. 环境准备与前置条件3.1 操作系统与 Python 版本推荐使用 Python 3.9 到 3.11 之间的版本。Windows 下安装时注意勾选 Add Python to PATHLinux 下注意区分系统自带的 Python 和虚拟环境中的 Python。如果电脑里同时有多个 Python 版本建议用虚拟环境隔离避免依赖冲突。# 检查当前 Python 版本 python --version pip --version3.2 安装依赖库本案例需要的核心库如下pip install pandas matplotlib seaborn openpyxl如果你想在最后把处理流程包装成接口服务再额外安装 FastAPI 和 uvicornpip install fastapi uvicorn这里说明一下每个库的作用pandas负责数据读取、清洗、分组统计和合并。matplotlib底层绘图库负责生成 PNG/JPG/SVG 格式的图表。seaborn基于 matplotlib 的封装绘图更简洁默认主题更适合统计图表。openpyxl用于读写 Excel 文件不只是 csv。3.3 目录结构准备建议先把项目目录搭起来后面所有脚本都放在统一的目录结构里。这样批量任务和数据管理都会轻松很多。data_analysis_case/ ├── data/ │ ├── raw/ # 原始数据 │ └── cleaned/ # 清洗后的数据 ├── outputs/ │ ├── charts/ # 生成的图表 │ └── reports/ # 汇总报告 ├── scripts/ │ ├── clean_data.py # 数据整理脚本 │ └── gen_charts.py # 图表生成脚本 └── requirements.txt # 依赖清单在项目根目录执行mkdir -p data/raw data/cleaned outputs/charts outputs/reports scripts如果是在 Windows 的 cmd 或 PowerShell 下可以分开执行 mkdir 命令。3.4 端口与依赖冲突检查在启动 FastAPI 接口服务前需要检查端口占用。案例默认使用 8000 端口如果被占用可以用下面的命令查找并更换端口# Windows 下查看端口占用 netstat -ano | findstr :8000 # Linux / macOS 下查看端口占用 lsof -i :8000如果发现端口被占用可以先结束对应进程或者在启动时指定其他端口这个在后面的接口部分会具体说。4. 数据整理实现步骤数据整理是整个案例的核心部分。这里我们不假设具体的数据集而是写一套通用的处理脚本。你只需要把原始文件放到data/raw/目录下然后按实际情况调整字段名和清洗逻辑即可。4.1 数据加载与初步检查动手做任何处理之前先加载数据并观察它的整体情况。这一步看起来简单但很多问题其实在这一步就能暴露出来比如字段分隔符异常、编码错误、列名带空格等。import pandas as pd # 读取原始数据 df pd.read_csv(data/raw/sample_data.csv, encodingutf-8-sig) # 查看数据概览 print(数据形状:, df.shape) print(字段列表:, df.columns.tolist()) print(\n前 5 行数据:) print(df.head()) print(\n数据类型:) print(df.dtypes) # 统计缺失值 print(\n缺失值统计:) print(df.isnull().sum())在真实场景中CSV 文件的编码经常是坑。Windows 下用 Excel 导出的 CSV 经常是 GBK 编码直接按 utf-8 读取会报错。一个稳妥的做法是先用encodinggbk尝试如果报错再切换到utf-8-sig或者用chardet自动检测编码。# 自动检测编码的通用写法 import chardet with open(data/raw/sample_data.csv, rb) as f: result chardet.detect(f.read(10000)) encoding result[encoding] print(检测到的编码:, encoding) df pd.read_csv(data/raw/sample_data.csv, encodingencoding)这一步的输出应该包括数据总行数和列数、每一列的名称、前几行内容、数据类型分布和缺失值统计。拿到这些信息后才进入真正的清洗阶段。4.2 缺失值处理策略缺失值是数据整理中最常见的问题。处理缺失值不能无脑dropna()不同的列应该有不同的策略。# 根据缺失比例决定策略 missing_ratio df.isnull().mean() # 如果某列缺失超过 50%通常选择删除该列 cols_to_drop missing_ratio[missing_ratio 0.5].index.tolist() df df.drop(columnscols_to_drop) print(删除高缺失列:, cols_to_drop) # 数值型列用中位数填充避免均值受极端值影响 numeric_cols df.select_dtypes(include[number]).columns for col in numeric_cols: if df[col].isnull().sum() 0: median_val df[col].median() df[col] df[col].fillna(median_val) print(f数值列 {col} 用中位数 {median_val} 填充) # 分类型列用众数填充或者填充为 未知 category_cols df.select_dtypes(include[object]).columns for col in category_cols: if df[col].isnull().sum() 0: mode_val df[col].mode()[0] if not df[col].mode().empty else 未知 df[col] df[col].fillna(mode_val) print(f分类型列 {col} 用众数 {mode_val} 填充)需要提醒的是填充中位数、众数替换只是常规做法。如果缺失值本身具有业务含义比如“用户未填写年龄”和“系统未采集该字段”是不同的那应该先按业务逻辑拆分再决定是填充还是新增一个“是否缺失”的标记列。4.3 数据类型标准化读入的数据类型经常和真实语义不匹配。常见问题包括日期字段是字符串、数值字段混入逗号或货币符号、ID 列被识别成数值导致前导零丢失等。# 日期列标准化 # 假设原始数据中有 order_date 列其格式可能是 2024/1/5 或 2024-01-05 df[order_date] pd.to_datetime(df[order_date], errorscoerce) # 数值列去格式化 # 假设 amount 列带有 1,234.56 这样的格式 df[amount] ( df[amount] .astype(str) .str.replace(, , regexFalse) .str.replace(,, , regexFalse) .astype(float) ) # 字符串列去空格 df[user_name] df[user_name].str.strip().str.replace( , , regexFalse) # ID 列应该保留为字符串防止前导零丢失 df[user_id] df[user_id].astype(str).str.zfill(8)日期解析是这里最值得注意的。如果数据量很大pd.to_datetime可能会比较慢此时可以指定format参数来加速解析比如format%Y-%m-%d。同时errorscoerce会把无法解析的日期变成 NaT这样后续处理中可以直接发现异常数据。4.4 去重与合并重复数据在业务表中非常常见。去重之前先想清楚按哪一列或哪几列判断重复。# 按订单 ID 去重保留第一条 df df.drop_duplicates(subset[order_id], keepfirst) # 如果只根据部分字段判断重复 df df.drop_duplicates(subset[user_id, order_date], keeplast) print(去重后数据形状:, df.shape)合并操作则是为了把多张表拼成一张宽表。常见的合并场景是把订单表和用户表通过 user_id 关联起来。# 读取需要合并的另一张表 user_df pd.read_excel(data/raw/user_info.xlsx, engineopenpyxl) # 左连接保留订单表所有数据用户信息能匹配上的就带出来 merged_df pd.merge(df, user_df, onuser_id, howleft) print(合并后数据形状:, merged_df.shape) print(合并后字段列表:, merged_df.columns.tolist())合并之后要检查关联字段是否出现大量空值。如果howleft合并后某列大面积为空说明订单表中很多 user_id 在用户表里找不到这时候需要反向检查用户表的 ID 是否缺失或者检查两张表的 ID 类型是否不一致比如一边是字符串一边是数值。4.5 特征加工与分组聚合清洗完成后通常还需要生成新的业务字段再做分组聚合。这里给出两个常用的加工逻辑。# 生成月份字段 df[order_month] df[order_date].dt.to_period(M).astype(str) # 生成“是否大额订单”标记 df[is_large_order] df[amount] df[amount].quantile(0.9) # 分组统计按月统计订单数和总金额 monthly_stats ( df.groupby(order_month) .agg( order_count(order_id, nunique), total_amount(amount, sum), avg_amount(amount, mean), ) .reset_index() ) print(monthly_stats.head())分组聚合是数据整理到可视化之间的桥梁。后续图表基本都是从这类统计结果中绘制出来的所以分组统计的字段粒度要提前想清楚。是看月度总趋势还是看每日分布还是按城市分组这都会影响图表的表达效果。4.6 导出清洗后的数据清洗完成的数据应该单独保存方便后续图表脚本直接复用也方便审计。# 导出为 Excel 格式 df.to_excel(data/cleaned/cleaned_data.xlsx, indexFalse, engineopenpyxl) # 同时导出 CSV 格式方便其他程序读取 df.to_csv(data/cleaned/cleaned_data.csv, indexFalse, encodingutf-8-sig) # 单独保存分组统计结果 monthly_stats.to_csv(outputs/reports/monthly_stats.csv, indexFalse, encodingutf-8-sig) print(清洗完成数据已导出到 data/cleaned/ 目录)到这一步数据整理链路已经闭环。后面的图表生成完全基于这里导出的cleaned_data.csv和monthly_stats.csv。5. 图表生成与分析展示5.1 中文字体与全局样式配置图表脚本最容易被忽略的是中文字体配置。如果不处理绘制出的图表所有中文都会变成方框。这一步在项目一开始就必须配置好。import matplotlib.pyplot as plt import matplotlib import seaborn as sns # 在 Windows 下通常使用 Microsoft YaHei 即微软雅黑 plt.rcParams[font.sans-serif] [Microsoft YaHei, SimHei, Arial Unicode MS] # 解决负号显示为方框的问题 plt.rcParams[axes.unicode_minus] False # 设置 seaborn 风格 sns.set_theme(stylewhitegrid, palettemuted)在 Linux 服务器上运行时如果系统没有中文字体需要先安装字体或者使用matplotlib.font_manager动态加载字体文件否则中文显示问题依然存在。这里给出一个通用方案from matplotlib import font_manager # 注册自定义字体文件 font_path /usr/share/fonts/truetype/wqy/wqy-microhei.ttc font_manager.fontManager.addfont(font_path) plt.rcParams[font.family] font_manager.FontProperties(fnamefont_path).get_name()5.2 月度销售趋势图做报表最基础的一张图通常是趋势图。这里我们画一个“订单金额与订单数”的双轴组合图用于同时展示总量和业务量。import pandas as pd import matplotlib.pyplot as plt # 读取分组统计结果 monthly pd.read_csv(outputs/reports/monthly_stats.csv) fig, ax1 plt.subplots(figsize(12, 6)) # 左轴订单总金额 color_amount #2878B5 ax1.plot(monthly[order_month], monthly[total_amount], markero, colorcolor_amount, linewidth2, label订单总金额) ax1.set_xlabel(月份) ax1.set_ylabel(订单总金额, colorcolor_amount) ax1.tick_params(axisy, labelcolorcolor_amount) ax1.set_xticks(range(len(monthly[order_month]))) ax1.set_xticklabels(monthly[order_month], rotation45, haright) # 右轴订单数 ax2 ax1.twinx() color_count #F39B7F ax2.bar(range(len(monthly[order_month])), monthly[order_count], alpha0.4, colorcolor_count, label订单数) ax2.set_ylabel(订单数, colorcolor_count) ax2.tick_params(axisy, labelcolorcolor_count) # 图例合并 lines1, labels1 ax1.get_legend_handles_labels() lines2, labels2 ax2.get_legend_handles_labels() ax1.legend(lines1 lines2, labels1 labels2, locupper left) plt.title(月度订单金额与订单数趋势) plt.tight_layout() plt.savefig(outputs/charts/monthly_trend.png, dpi300, bbox_inchestight) plt.show()判断这张图是否成功有三个标准第一每个月份标签都能正常显示并避免重叠这里通过旋转 45 度和bbox_inchestight来实现第二左右两个坐标轴的量级差异被正确分开金额可能是百万级订单数可能是百级双轴图能避免其中一条线被压扁第三保存出来的图片分辨率足够清晰300 dpi 已经能满足大部分汇报场景。5.3 品类分布饼图当需要展示“哪几个分类贡献了主要业务量”时饼图或环形图是直接的选择。这里用环形图因为比实心饼图更现代也更容易在中心添加汇总信息。# 读取清洗后的数据按品类聚合 df pd.read_csv(data/cleaned/cleaned_data.csv) category_stats df.groupby(category)[amount].sum().sort_values(ascendingFalse) # 如果品类太多只取前 6 个其余归为“其他” top_categories category_stats.head(6) others category_stats.iloc[6:].sum() if others 0: top_categories[其他] others fig, ax plt.subplots(figsize(8, 8)) colors sns.color_palette(Set3, n_colorslen(top_categories)) wedges, texts, autotexts ax.pie( top_categories.values, labelstop_categories.index, autopct%.1f%%, startangle90, colorscolors, wedgeprops{width: 0.4, edgecolor: white}, pctdistance0.8, ) for autotext in autotexts: autotext.set_color(white) autotext.set_fontsize(10) # 中心添加汇总信息 total_amount top_categories.sum() ax.text(0, 0, f总金额\n{total_amount:,.0f}, hacenter, vacenter, fontsize12) ax.set_title(品类销售金额占比) plt.tight_layout() plt.savefig(outputs/charts/category_pie.png, dpi300, bbox_inchestight) plt.show()需要留意的坑是df.groupby(category)如果 category 列存在空值会被 groupby 忽略但实际上这些空值数据可能代表“未知品类”会造成金额缺失。所以在前面的数据清洗阶段就应该决定好分类空值的填充策略。5.4 金额分布直方图与箱线图数值分布类图表适合观察数据是否存在极端值、是否符合正态分布特征。这里同时输出直方图和箱线图。fig, axes plt.subplots(1, 2, figsize(14, 5)) # 直方图 axes[0].hist(df[amount], bins50, color#4DBBD5, edgecolorwhite, alpha0.8) axes[0].set_title(订单金额分布直方图) axes[0].set_xlabel(订单金额) axes[0].set_ylabel(频次) # 箱线图 axes[1].boxplot(df[amount], vertFalse, widths0.6, patch_artistTrue, boxpropsdict(facecolor#E64B35, alpha0.7)) axes[1].set_title(订单金额箱线图) axes[1].set_xlabel(订单金额) plt.tight_layout() plt.savefig(outputs/charts/amount_distribution.png, dpi300, bbox_inchestight) plt.show()这里有一个性能细节当数据量达到几十万行时直接画直方图会消耗较多内存。建议先用pd.qcut或手动分箱做聚合再画柱状图这样速度会快得多。如果数据中存在极端值直方图会把横轴拉得很难看此时可以先用df[amount].clip(upperdf[amount].quantile(0.99))做截断或者直接画在对数坐标下。5.5 相关性热力图如果你想观察多个数值型字段之间的线性关系热力图是最高效的展示方式。这个图在探索性数据分析阶段非常常用。# 选取数值型字段 numeric_df df.select_dtypes(includenumber) corr_matrix numeric_df.corr() plt.figure(figsize(10, 8)) sns.heatmap( corr_matrix, annotTrue, fmt.2f, cmapRdBu_r, center0, squareTrue, cbar_kws{shrink: 0.8}, ) plt.title(数值字段相关性热力图) plt.tight_layout() plt.savefig(outputs/charts/corr_heatmap.png, dpi300, bbox_inchestight) plt.show()相关性热力图对数据质量比较敏感。如果原始字段里有太多缺失值或者字段量纲差异极大直接计算相关性会出现偏差。稳妥的做法是先完成清洗再对需要计算的字段做标准化或归一化最后再画热力图。另外相关系数只反映线性关系不反映非线性关系这个在写结论时要注意措辞。6. 接口 API 与批量任务扩展6.1 批量处理目录下的多个文件如果每天都会产生新的数据文件比如order_20240301.csv、order_20240302.csv手动改文件名再运行脚本是很低效的。这里给出一个批量处理模板。import glob import os input_dir data/raw/ output_dir data/cleaned/ # 匹配所有 CSV 文件 csv_files glob.glob(os.path.join(input_dir, *.csv)) for file_path in csv_files: file_name os.path.basename(file_path).replace(.csv, ) print(f正在处理: {file_name}) df pd.read_csv(file_path, encodingutf-8-sig) # 这里放入前面定义好的清洗函数 # df clean_data(df) output_path os.path.join(output_dir, f{file_name}_cleaned.csv) df.to_csv(output_path, indexFalse, encodingutf-8-sig) print(f已完成: {output_path})这里建议把清洗逻辑封装成函数clean_data(df)这样可以避免代码重复。每个文件的处理状态也要记录最简单的做法是打印日志更正式的做法是输出一个处理汇总表包含文件名、原始行数、清洗后行数、删除行数、处理耗时等字段。6.2 FastAPI 接口封装把数据处理能力暴露成 HTTP 服务可以方便前端、爬虫调度系统或内部定时任务复用。先写一个简单的 FastAPI 应用。from fastapi import FastAPI, UploadFile, File from fastapi.responses import JSONResponse, FileResponse import pandas as pd import io import os app FastAPI(title数据整理与图表生成服务) app.post(/api/clean) async def api_clean(file: UploadFile File(...)): 接收上传的 CSV 文件返回清洗后的数据摘要 contents await file.read() df pd.read_csv(io.BytesIO(contents), encodingutf-8-sig) # 这里调用复用清洗逻辑 # df clean_data(df) summary { filename: file.filename, rows: len(df), columns: df.columns.tolist(), missing_values: df.isnull().sum().to_dict(), } return JSONResponse(contentsummary) app.get(/api/chart/monthly) async def api_monthly_chart(): 返回月度趋势图 PNG chart_path outputs/charts/monthly_trend.png if os.path.exists(chart_path): return FileResponse(chart_path, media_typeimage/png) return JSONResponse(content{error: 图表不存在请先运行生成脚本}, status_code404)启动接口服务uvicorn app:app --host 127.0.0.1 --port 8000 --reload启动后可以通过 curl 测试接口# 测试文件上传接口 curl -X POST -F filedata/raw/sample_data.csv http://127.0.0.1:8000/api/clean # 测试图表获取接口 curl -o monthly_trend.png http://127.0.0.1:8000/api/chart/monthly6.3 定时任务集成如果数据每天固定更新可以用系统自带的定时任务来跑脚本。Windows 下用“任务计划程序”Linux 下用 crontab。这里给出 Linux 的 crontab 示例# 每天凌晨 2 点运行数据整理脚本 0 2 * * * cd /path/to/data_analysis_case python scripts/clean_data.py logs/clean.log 21 # 每天凌晨 3 点生成图表 0 3 * * * cd /path/to/data_analysis_case python scripts/gen_charts.py logs/charts.log 21定时任务一定要记得写日志。如果脚本中途抛异常没有日志的话排查成本会很高。日志文件建议按日期轮转避免单个日志文件无限膨胀。7. 资源占用与性能观察7.1 CPU 与内存观察方法虽然这个案例不涉及 GPU但 CPU 和内存的占用仍然值得观察尤其是处理大型 CSV 文件时。在运行脚本的过程中打开任务管理器或系统监视器重点观察内存曲线的变化。如果 Python 进程的内存占比持续上升且不回落通常说明数据在内存中被多次复制比如反复concat或merge大表。7.2 数据量对性能的影响从实际经验来看pandas 处理几万行的数据几乎无感几十万行的数据在分组聚合时可能感觉到等待到几百万行时内存占用就会明显飙升。数据量达到这个级别后比较务实的方向是用dtype参数指定列类型减小内存或者读取时只选择需要的列用usecols参数。# 只读取需要的列同时指定更节省内存的数据类型 df pd.read_csv( data/raw/large_file.csv, usecols[order_id, order_date, amount, category], dtype{ order_id: string, amount: float32, category: category, }, parse_dates[order_date], )7.3 图表生成的性能开销图表生成本身对 CPU 要求不高但如果一次性生成几十张图或者图片分辨率设置得过高比如 600 dpi 且尺寸很大内存消耗同样不可忽略。批量生成图表时建议单张保存后立刻plt.close()避免大量 figure 对象残留导致内存泄漏。for col in columns_to_plot: fig, ax plt.subplots(figsize(8, 6)) ax.plot(df[col]) plt.savefig(foutputs/charts/{col}_trend.png, dpi300) plt.close(fig) # 关键关闭当前 figure7.4 降低资源占用的实用手段第一尽可能使用category类型代替object类型存储重复度高的字符串列。第二在 merge 之前先对两张表做drop_duplicates减少关联时的笛卡尔积风险。第三如果只是画图直接读取清洗后的统计结果而不是每次都重新加载全量明细数据。第四用pd.read_csv的chunksize参数分块读取超大文件但分块处理会明显增加代码复杂度只在数据确实大到内存装不下的情况下使用。8. 常见问题与排查方法问题现象可能原因排查方式解决方案读取 CSV 报 UnicodeDecodeError编码识别错误用 chardet 检测文件编码切换 encoding 参数或按检测结果读取日期列解析后全是 NaT日期格式不统一或含有无效文本打印原始值集合检查特殊值使用 errorscoerce 定位异常值手动处理格式图表中文显示为方框系统缺少中文字体或未配置字体检查 plt.rcParams 字体设置设置 Microsoft YaHei / SimHei或动态加载字体文件重复列名导致 drop 报错原始数据包含同名列打印 df.columns 检查在读取时指定 mangle_dupe_colsFalse或重命名列merge 后行数暴增关联字段存在重复值分别统计 key 列的唯一值数量先对关联表去重再 merge图表保存后空白或压缩没有调用 tight_layout 或 bbox_inches检查图片尺寸和文件大小设置 bbox_inchestight调大 dpiuvicorn 启动后端口被占用8000 端口被其他进程使用netstat / lsof 查看占用换端口uvicorn app:app --port 8001批量任务中某文件失败导致整体中断脚本缺少 try-except关注错误输出位置在循环内加入异常捕获跳过失败文件并记录日志groupby 后结果缺失部分数据分组字段存在空值检查分组字段 isna 数量清洗阶段先处理分组字段的空值表格里列出的问题都是数据整理与图表生成过程中最高频的坑。如果运行脚本时遇到其他报错一个通用思路是先读最底部一条报错信息找到出错的文件名和行号再回到代码对应位置检查数据形状和 dtype最后将报错信息完整粘贴到搜索引擎或 AI 工具中带上自己的数据样例做验证。9. 最佳实践与使用建议9.1 第一次先小样本验证不要一上来就在几百万行的全量数据上跑完整处理流程。建议先取前 1000 行做验证确认清洗逻辑、字段名、聚合方式都正确后再切换全量数据。这样每一轮迭代的时间成本都会非常低。# 验证阶段只读取前 1000 行 df_sample pd.read_csv(data/raw/large_file.csv, nrows1000)9.2 保留一份最小可运行配置把项目目录、Python 版本、依赖库和关键参数记录到README.md和requirements.txt中。这样即使换一台电脑或者隔了几个月再回来处理同类数据也能快速恢复环境。# 导出当前环境的依赖版本 pip freeze requirements.txt9.3 数据文件分目录管理原始数据、中间清洗结果、最终输出图表和日志文件一定要分开存放。不建议把脚本输出直接覆盖原始文件。一方面是为了审计需要另一方面是可重复实验的需要。改坏了清洗逻辑也能重新从原始数据起跑。9.4 批量任务加日志和失败重试批量处理多个文件时给每个文件增加独立的成功/失败标记。处理失败的场景要单独落日志最后生成一个汇总报告。这样你不用盯着控制台跑完看汇总就知道哪些文件有问题。import traceback from datetime import datetime failed_files [] log_entries [] for file_path in csv_files: try: print(f处理中: {file_path}) df pd.read_csv(file_path) # 清洗逻辑... log_entries.append({file: file_path, status: success, time: str(datetime.now())}) except Exception as e: log_entries.append({file: file_path, status: failed, error: str(e), time: str(datetime.now())}) traceback.print_exc()9.5 接口服务要控制访问范围如果启动了 FastAPI 接口服务要注意监听地址。开发调试时用127.0.0.1即可不要直接监听0.0.0.0并暴露到公网。如果确实需要内网其他机器访问也要放在可控网络环境内并且对上传文件大小做限制防止恶意大文件上传拖垮服务。9.6 涉及敏感数据必须脱敏凡是涉及身份信息、联系方式、地址、财务明细的数据在处理前就要先脱敏。可以在读取数据后立即丢弃敏感列或者用哈希替代真实值。不要等到生成图表、准备发布到内部平台时才开始考虑合规问题。10. 总结与下一步这个“数据整理与图表生成案例”最值得尝试的点在于它把数据清洗和可视化的完整链路串在了一套可复现的脚本里。你不需要记住所有 pandas 和 matplotlib 的函数用法只需要照这套流程跑通一次后面再遇到同类任务时把清洗逻辑替换成目标场景即可。首批建议验证三个核心功能数据加载与缺失值处理是否按预期工作、中文字体配置是否解决了图表乱码、分组聚合后的统计结果是否和 Excel 透视表对得上。最容易踩的坑集中在两个地方一是 CSV 文件编码不一致二是 merge 关联字段重复导致数据膨胀。后续可以继续扩展的方向包括使用 Polars 替代 pandas 提升大数据量下的处理速度将图表封装为企业微信或邮件推送或者接入更复杂的定时调度系统实现全托管数据流程。建议把这份脚本保留成你自己的“数据整理工具箱”每遇到一个新场景就迭代一次大多数重复性报表工作其实都可以用类似方式自动化。
返回列表