数据清洗和预处理Agent在数据工程里承担的角色,不只是写几行pandas那么简单。很多数据平台会把清洗脚本、特征脚本和模型训练脚本放在同一个仓库里,每次数据源结构变化都要改三处。本文通过一个订单数据清洗案例,拆解如何把清洗逻辑封装成可配置的Agent,让它自动完成类型修复、缺失值填补、异常值标记和特征初筛。这里的Agent不是一个黑盒模型,而是一组由规则和统计方法驱动的确定性流水线,配合质量报告和回滚机制,能显著降低人工排查成本。

一、清洗与预处理的边界为什么要先划清
数据清洗解决的是数据本身的质量缺陷,比如空值、重复记录、格式不一致、超出合理范围的数值。预处理则是在清洗之后,为了让模型更好吸收信息而做的编码、缩放、离散化和特征组合。两者的目标不同,出错后的处理方式也不同:清洗错误意味着信息失真,预处理策略不当则主要影响模型性能。把这两个阶段写进同一个函数里,最大的问题是无法独立测试和替换,比如想从独热编码切换到目标编码,就必须重写整个清洗流程。
边界清晰的第一个好处是规则可以沉淀。清洗规则通常来自业务约束,例如订单金额不能为负、状态字段的合法枚举值、时间戳必须晚于某个业务起点。这些规则与模型无关,可以长期复用。预处理步骤则更偏向实验,同一个特征集给线性模型和树模型时,需要的缩放和编码方式完全不同。拆开后,Agent可以只负责清洗,输出一份干净的宽表,而预处理交给下游的特征工程模块。
以订单数据为例,amount字段如果是负数,这是清洗问题,需要标记或修正;如果amount的分布高度右偏,是否取对数则是预处理问题。Agent在清洗阶段不应该对分布做变换,否则数据血缘会变得模糊,后续排查模型输入时会非常被动。因此,在设计Agent时,第一件事就是约定好输出数据的契约:类型正确、缺失已处理、异常已标记,但不做特征变换。
二、Agent的模块化架构与执行流程
一个可用的数据清洗Agent通常由五个模块组成:数据探查器、规则引擎、统计检测器、修复执行器和报告生成器。数据探查器负责读取原始表并推断列的类型、缺失率、唯一值数量和基本分布特征。规则引擎执行确定性的业务规则,比如删除重复主键、修正非法枚举值、截断超长文本。统计检测器则用分位数、标准差等方式发现软异常,比如金额突然是均值的50倍。修复执行器根据策略对缺失值和异常值进行处理,报告生成器输出一份数据质量摘要。
执行流程可以设计成一条可重复运行的流水线:接入原始表后,先做列元信息推断,再依次执行规则修复和统计标记,接着生成数据质量报告,最后输出清洗后的数据和报告。每次数据源更新时,Agent重新跑一遍,报告会对比本次和上次的缺失率、异常数量变化,帮助判断数据源是否出现漂移。流水线里的每一步都可以单独开关,比如某段时间只需要跑规则引擎,就可以关闭统计检测来加快速度。
模块化之后,Agent的配置可以放在独立的YAML或JSON文件里,每个字段的修复策略都用键值对描述。这样新增一个数据源时,只需要写一份配置,不需要再改Python代码。下面是一个简化的配置片段,展示了订单金额字段的规则定义。
# 清洗配置示例
{
"amount": {
"type": "float",
"min": 0.0,
"max": 100000.0,
"missing_strategy": "median",
"outlier_method": "iqr",
"outlier_action": "mark"
},
"status": {
"type": "category",
"allowed_values": ["paid", "pending", "cancelled"],
"missing_strategy": "mode"
}
}
上面的配置中,amount字段如果出现负数或超过十万的值,会被规则引擎拦截;缺失值用中位数填补;离群值用IQR方法标记但不删除。这样的配置化方式让Agent的扩展成本大幅下降,业务方也能直接审核规则是否合理。
三、核心实现:列类型推断与缺失值策略
列类型推断是Agent的第一步,也是很容易被低估的一步。直接读取CSV时,pandas可能会把带数字的ID列识别成整数,把带空格的文本列识别成object,但如果列里混入了少量字符串,整数列会被整体升级为object。因此Agent需要做语义层推断,而不是单纯依赖dtype。常用的做法是采样前1000行非空值,用正则匹配日期、整数、浮点、布尔和分类模式。例如,如果超过95%的样本匹配YYYY-MM-DD格式,则可以推断为日期;如果去重后的唯一值数量占总行数比例低于5%,则可以推断为分类列。
缺失值处理最忌一刀切。直接删除所有含缺失值的行,可能丢掉大量有效样本;全部填0或均值,则可能扭曲分布,尤其是对高基数分类变量。工程上要先判断缺失是否随机。对于订单表里的note备注字段,缺失本身就代表没有备注,可以填充为空字符串;对于user_id这种主键字段,如果缺失则只能删除该行;对于amount这种数值字段,缺失率低于5%时可以用中位数填补,高于20%时则建议增加一列amount_is_missing,让后续模型知道这个值是人为填充的。下面是一个列类型推断和缺失值处理的实现示例。
import pandas as pd
import re
def infer_column_type(series):
sample = series.dropna().astype(str).head(1000)
if sample.str.match(r'^\d{4}-\d{2}-\d{2}').all():
return 'date'
if sample.str.replace('.', '', 1).str.isdigit().all():
return 'integer'
if sample.str.match(r'^-?\d+\.\d+$').all():
return 'float'
if series.nunique() / len(series) < 0.05:
return 'category'
return 'text'
def handle_missing(df, col, col_type):
if col_type in ('integer', 'float'):
df[f'{col}_is_missing'] = df[col].isna().astype(int)
df[col].fillna(df[col].median(), inplace=True)
elif col_type == 'category':
df[col].fillna('unknown', inplace=True)
else:
df[col].fillna('', inplace=True)
return df
这段代码先用正则判断列类型,再根据类型选择缺失值填充策略。需要注意的是,数值列填充中位数时同时创建了缺失标记列,这样后续模型可以区分真实中位数和填充值。这种标记方式在风控和推荐场景中尤其重要,因为缺失本身可能就是强特征。
四、异常检测与离群值修复的工程化方法
离群值检测常用的方法有IQR、Z-score和孤立森林。Z-score假设数据近似正态分布,对有偏数据效果一般;孤立森林适合高维但计算量大;IQR基于分位数,对偏态分布更稳健,适合大多数表格数据。Agent在实现时可以把IQR做成默认方法,但阈值不能写死。因为不同数据源的业务含义不同,比如订单金额的合理上限可能是两万,而用户积分的合理上限可能是五十万。因此阈值需要从配置中读取,并支持按历史分位数动态调整。
离群值不一定都要删除。在订单场景中,一笔超大金额可能是企业采购,也可能是系统录入错误,直接删除会丢失真实样本。更稳妥的做法是标记离群值,同时保留原始值,并增加一列记录离群原因。后续训练模型时,既可以选择只使用正常样本,也可以把离群标记作为特征输入。Agent的修复执行器应该支持三种动作:删除、标记、截断。删除适用于明显不可能出现的值,比如负数年龄;截断适用于超出物理上限的采集误差;标记适用于可能真实但需要单独分析的值。
下面的代码展示了基于IQR的标记方法,并返回上下界,方便报告模块记录本次检测使用的阈值。
import numpy as np
def mark_outliers_iqr(df, column, k=1.5):
q1 = df[column].quantile(0.25)
q3 = df[column].quantile(0.75)
iqr = q3 - q1
lower = q1 - k * iqr
upper = q3 + k * iqr
df[f'{column}_is_outlier'] = ((df[column] < lower) | (df[column] > upper))
return df, lower, upper
调用后,Agent会在报告中输出每个数值列的上下界和离群点数量。如果某次数据源更新后离群点数量突然增加50%,报告会触发告警,提示数据可能出现了系统性变化。这种可观测性设计比单纯修复更能帮助团队提前发现问题。
五、案例复盘:从订单明细到可训练特征表
以一个真实的订单导出数据为例,原始表包含order_id、user_id、amount、created_at、status和note六个字段。Agent运行时,先通过数据探查器发现order_id有重复,user_id存在少量空值,amount有负数,created_at格式不一致,status出现了拼写为canceled的非法值。这些信息会汇总到数据质量报告中,然后规则引擎开始处理。
规则引擎删除重复的order_id,把status中的canceled统一修正为cancelled,把created_at统一解析为北京时间。统计检测器对amount运行IQR检测,标记出17条离群记录,其中10条金额远高于正常范围,7条为0。根据配置,0元订单可能是测试数据,规则引擎将其标记为无效;高金额订单保留并打上离群标签。缺失值处理模块对user_id的缺失行进行删除,因为主键缺失无法准确归因;对note字段填充空字符串。
处理完成后,Agent输出一张清洗后的订单表,以及一份包含缺失率对比、修复记录、离群点分布的报告。下游特征工程模块可以直接读取这张表做聚合和编码,不需要再关心原始数据的脏乱情况。如果数据源每周更新一次,Agent的定时任务会自动重新执行,报告会记录本次相比上次的差异。比如某周amount的缺失率从2%升到8%,报告会提示业务方检查订单接口是否变更,而不是等模型指标下滑后才被动排查。
六、总结与落地建议
构建数据清洗与预处理Agent的核心在于模块化和可观测。把清洗与预处理分开,把规则引擎和统计检测分开,把修复动作和报告生成分开,才能让Agent长期稳定运行。列类型推断、缺失值策略和离群值标记是三个必须谨慎处理的环节,尤其是缺失值标记和离群值保留,往往比简单填充或删除带来更多信息。
落地时建议从一个小数据源开始,先实现规则引擎和数据质量报告,跑通后再逐步加入统计检测和动态阈值。清洗逻辑要独立版本管理,每次修改都记录在案,这样出现数据问题时可以快速回滚到上一个版本。另外,Agent输出的报告要能被业务方看懂,尽量用表格和对比数字,而不是堆砌日志。
最终目标不是把数据清洗做到完美,而是让清洗过程变得透明、可重复、可审计。当数据质量下降时,团队能第一时间从报告中看到信号,而不是等到模型效果变差才回头查数据。这个Agent的价值也就体现在这里。