用Pandas+Pydantic搭建数据清洗与验证管道
数据清洗是每个和数据打交道的人都绕不开的日常。无论是做分析、跑模型还是给业务方出报表拿到的原始数据往往都带着重复记录、缺失值、格式错误甚至一些明显违背常识的条目。我以前习惯每次遇到问题就临时写几行drop_duplicates()或fillna()但下次换一份数据同样的脏法又得重写一遍。与其这样反复救火不如建一条可复用的数据管道清洗、验证、报告一步到位让脏数据经过流水线后变成可信的输入。本文将用Pandas和Pydantic搭一条不到100行核心代码的数据清洗与验证管道。读完之后希望能让朋友们理解管道的设计思路还能把它直接套用到平时的生活和工作场景中。1. 管道的三条职责清洗、验证、报告制造业的流水线之所以高效是因为每个工位只做一件事上一步的输出就是下一步的输入。数据管道也可以这样设计。​我们让管道承担三项职责清洗负责去重、处理缺失值并为后续扩展留出空间验证确保每一行数据都满足业务规则比如年龄不能是负数、手机号必须是 11 位报告追踪整个过程中到底去掉了多少重复、补了多少空、拦下了多少错误。这样一来数据质量不再是模糊的感觉而是可以量化的指标。要搭建这条管道只需要两个库pandas用来处理表格pydantic用来定义“什么算有效数据”。如果你还没安装在终端里执行pip install pandas pydantic即可。2. 先定义“有效”再谈清洗很多人的清洗习惯是先把数据改得“看起来能看”再去想验证的事。但更好的顺序是反过来先明确什么样的数据才算合格然后再让清洗服务于这个标准。Pydantic的BaseModel正好适合做这件事。我们以一个社区团购订单为例定义OrderValidator字段包括姓名、年龄、手机号和订单金额。from pydantic import BaseModel, field_validator, ValidationError from typing import Optional import pandas as pd import numpy as np class OrderValidator(BaseModel): name: str age: Optional[int] None phone: Optional[str] None amount: Optional[float] None field_validator(age) classmethod def validate_age(cls, v): if v is not None and (v 0 or v 100): raise ValueError(年龄必须在0到100之间) return v field_validator(phone) classmethod def validate_phone(cls, v): if v and (not v.isdigit() or len(v) ! 11 or not v.startswith(1)): raise ValueError(手机号必须是11位数字且以1开头) return v field_validator(amount) classmethod def validate_amount(cls, v): if v is not None and v 0: raise ValueError(金额不能为负数) return v这里有几个关键点field_validator必须和classmethod搭配使用年龄限制在 0 到 100 之间手机号要求 11 位数字且以 1 开头这很符合国内手机号的格式金额不能为负。有了这个模式任何一行数据只要不满足规则Pydantic就会抛出ValidationError我们就能把它拦下来。3. 给管道一个骨架并让它学会记账定义好验证模式后我们创建一个DataPipeline类。它的构造函数里初始化一个统计字典用来记录去重数量、空值处理数量和验证错误数量。别小看这个字典它让管道有了“记账”的能力每次运行后你都能清楚知道数据被动了哪些手脚。class DataPipeline: def __init__(self): self.cleaning_stats { duplicates_removed: 0, nulls_handled: 0, validation_errors: 0 }4. 清洗去重、补缺但要有章法清洗方法clean_data接收一个 DataFrame返回处理后的版本。第一步是去重而且必须在填充缺失值之前做——如果先填充再取中位数重复记录会把中位数拉偏。去重之后数值列用中位数填充因为中位数比均值更抗离群值文本列则统一填成Unknown给后续人工排查留个标记。每处理一处空值统计字典里的nulls_handled就累加一次。def clean_data(self, df: pd.DataFrame) - pd.DataFrame: initial_rows len(df) df df.drop_duplicates() self.cleaning_stats[duplicates_removed] initial_rows - len(df) numeric_columns df.select_dtypes(include[np.number]).columns for col in numeric_columns: null_count df[col].isnull().sum() if null_count 0: df[col] df[col].fillna(df[col].median()) self.cleaning_stats[nulls_handled] null_count string_columns df.select_dtypes(include[object]).columns for col in string_columns: null_count df[col].isnull().sum() if null_count 0: df[col] df[col].fillna(Unknown) self.cleaning_stats[nulls_handled] null_count return df5. 验证逐行检查但不让坏数据中断管道清洗完成后数据看起来完整了但未必正确。validate_data方法会逐行迭代把每一行转成字典后传给OrderValidator。有效行收集到valid_rows无效行则捕获ValidationError记录下行号和错误详情。这样做的好处是单条坏数据不会让整个管道崩溃——能处理的尽量处理同时把问题记录在案供后续人工审查。def validate_data(self, df: pd.DataFrame): valid_rows [] errors [] for idx, row in df.iterrows(): try: validated OrderValidator(**row.to_dict()) valid_rows.append(validated.model_dump()) except ValidationError as e: errors.append({row: idx, errors: str(e)}) self.cleaning_stats[validation_errors] len(errors) return pd.DataFrame(valid_rows), errors6. 编排让清洗和验证串起来最后用一个process方法把清洗和验证串联起来返回一个包含清洗后数据、验证错误列表和统计信息的综合报告。这样调用方只需要一个入口就能拿到全部结果。def process(self, df: pd.DataFrame): cleaned_df self.clean_data(df.copy()) validated_df, validation_errors self.validate_data(cleaned_df) return { cleaned_data: validated_df, validation_errors: validation_errors, stats: self.cleaning_stats }7. 在社区团购订单场景中试一试社区团购在国内非常普遍订单数据通常来自微信群接龙、小程序下单或团长手动录入。这类数据往往存在一些典型问题同一用户重复下单、手机号少填几位、年龄栏被乱填、金额出现负数或空白。我们构造一份示例数据来模拟这种情况if __name__ __main__: # 模拟社区团购订单数据包含重复、缺失、非法年龄、非法手机号 sample_data pd.DataFrame( { name: [张伟, 李娜, 王芳, None, 刘洋, 李娜], age: [28, -5, 35, 42, 150, -5], phone: [ 13812345678, 12345, 13987654321, 13712345678, 13612345678, 12345, ], amount: [45.5, 60.0, 45.5, None, 88.8, 60.0], } ) print( * 60) print(原始数据) print(sample_data) print( * 60) pipeline DataPipeline() result pipeline.process(sample_data) print(\n清洗后的有效数据) print(result[cleaned_data]) print(\n验证错误清单) if result[validation_errors]: for err in result[validation_errors]: print(f 行号 {err[row]}{err[errors]}) else: print( 无) print(\n统计信息) for key, value in result[stats].items(): print(f {key}: {value}) print(\n * 60) print( 处理完成。有效数据 {} 行错误 {} 行。.format( len(result[cleaned_data]), len(result[validation_errors]) ) )运行后管道会先去掉一条完全重复的“李娜”记录把缺失的姓名填成Unknown用中位数填补缺失的金额。接着逐行验证李娜的年龄 -5 和手机号 12345 会被拦下刘洋的年龄 150 也会被标记为错误。最终输出的有效数据里张伟、王芳和那位姓名被补为Unknown的用户顺利通过错误清单则清晰列出问题所在的行和原因。​团长拿到这份报告就能直接知道哪些订单需要人工核对手机号哪些年龄明显是误填而不必在一堆脏数据里反复翻找。8. 管道之外还能怎么扩展这条管道只有不到100行核心代码但它已经具备了可维护系统的雏形。你可以根据具体场景继续扩展比如增加自定义清洗规则比如标准化地址、脱敏手机号也可以让 Pydantic 模式可配置使同一管道适配不同来源的数据如果数据量很大还可以把逐行验证换成向量化操作或并行处理。关键不在于一次写得多完美而在于拥有一个可复用的骨架把重复性的清洗和验证工作交给管道自己则把精力放在从干净数据中提取洞察上。​

相关新闻

RAG文档切分避坑指南:切太大检索不准,切太小答非所问,到底切多大?

RAG文档切分避坑指南:切太大检索不准,切太小答非所问,到底切多大?

RAG 的完整工作流程与文档切分:数据怎么进系统,切多大才合理 作者:利威尔xu | CSDN 专栏《RAG 保姆级实战:从原理到落地》 前言 在本专栏第二篇《降低模型幻觉汇总》里,咱聊了降幻觉的几种手段,重点区分了…

2026/10/13 12:58:33 阅读更多 →
题解:洛谷 P10113 [GESP202312 八级] 大量的工作沟通

题解:洛谷 P10113 [GESP202312 八级] 大量的工作沟通

本文分享的必刷题目是从蓝桥云课、洛谷、AcWing等知名刷题平台精心挑选而来,并结合各平台提供的算法标签和难度等级进行了系统分类。题目涵盖了从基础到进阶的多种算法和数据结构,旨在为不同阶段的编程学习者提供一条清晰、平稳的学习提升路径。 欢迎大…

2026/10/12 11:45:55 阅读更多 →
题解:洛谷 P3015 [USACO11FEB] Best Parenthesis S

题解:洛谷 P3015 [USACO11FEB] Best Parenthesis S

本文分享的必刷题目是从蓝桥云课、洛谷、AcWing等知名刷题平台精心挑选而来,并结合各平台提供的算法标签和难度等级进行了系统分类。题目涵盖了从基础到进阶的多种算法和数据结构,旨在为不同阶段的编程学习者提供一条清晰、平稳的学习提升路径。 欢迎大…

2026/10/12 13:58:12 阅读更多 →

最新新闻

从零搭建ELK日志分析平台:架构设计、组件选型与实战调优

从零搭建ELK日志分析平台:架构设计、组件选型与实战调优

说实话,我在接手公司日志平台之前,排查故障靠的是“三件套”组合拳:登录服务器、grep关键字、tail -f盯屏幕。最开始那几年业务量不大,这招勉强够用,可一旦服务器数量超过两位数、日志格式五花八门,这套土办…

2026/10/13 12:58:10 阅读更多 →
数论基础实战:从紫书第10章到Kuangbin专题14的题型拆解

数论基础实战:从紫书第10章到Kuangbin专题14的题型拆解

如果你正卡在紫薯第10章数学,准备转战Kuangbin专题14数论基础,我给你一句忠告——书上的公式全会了,不代表你看到题目能动手。我当年啃完紫书第10章,带着一堆“看起来都能推导”的公式点开专题14,第一题就把我按在地上…

2026/10/13 12:58:10 阅读更多 →
Conda报错排查指南:从repodata失败到依赖冲突的完整解决

Conda报错排查指南:从repodata失败到依赖冲突的完整解决

两行红字一贴,小白当场懵,老手也得先深吸一口气。Collecting package metadata (current_repodata.json): failed和Solving environment: failed这对组合,几乎每个用过 Anaconda 的人都撞见过:刚装完环境准备conda install pandas…

2026/10/13 12:58:10 阅读更多 →
基于Halton序列的图像加密解密算法详解与Matlab实现

基于Halton序列的图像加密解密算法详解与Matlab实现

做图像加密解密这个方向的,八成都有过这种感受:教材里的经典算法讲得头头是道,真到自己动手复现,却总在“看起来像那么回事”和“实际效果经不起推敲”之间反复横跳。我之前在做基于Halton序列的图像加密解密项目时,最…

2026/10/13 12:58:10 阅读更多 →
Kettle 9.3 源码编译实战:从 GitHub 构建可部署的 PDI 生产环境

Kettle 9.3 源码编译实战:从 GitHub 构建可部署的 PDI 生产环境

简介:本资源为Kettle(Pentaho Data Integration)最新9.3版本的官方安装包百度网盘直链,面向数据工程师、ETL开发人员及大数据初学者,解决国内用户因网络限制难以获取国际开源ETL工具的问题。资源以1个14KB的docx文档形…

2026/10/13 12:58:10 阅读更多 →
Agent上线最后一公里:评测门禁、可观测性埋点与AgentOps闭环落地指南

Agent上线最后一公里:评测门禁、可观测性埋点与AgentOps闭环落地指南

1. 从"能跑"到"敢上线":Agent 交付的最后一公里到底卡在哪做过 Agent 项目的人大概都有过这种体验:本地 Demo 跑得飞起,工具调用、多轮对话、RAG 检索全都正常,演示给同事看的时候掌声一片。可真到了要往生产…

2026/10/13 12:57:09 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →