Silent Data Errors That Survive Every Cleaning Pipeline
TL;DR · AI 摘要
Silent Data Errors That Survive Every Cleaning Pipeline - KDnuggets publ: 30-Oct, 2026 - Blog Top Posts About - Topics A...
核心要点
- 主题聚焦:Silent Data Errors That Survive Every Cleaning P
- 来源:KDnuggets,建议结合原文判断细节。
- AI 分析暂不可用,本条为保底评分与摘要。
能绕过所有清洗流程的静默数据错误 - KDnuggets
发布日期:2026年10月30日
- 博客热门文章
- 主题 人工智能 职业建议 计算机视觉 数据工程 数据科学 语言模型 机器学习 MLOps 自然语言处理 编程 Python SQL
- 数据集
- 活动
- 资源 快速参考指南 推荐 技术简报
- 广告
订阅电子报
#header end
/ad_wrapper
能绕过所有清洗流程的静默数据错误
4种能通过所有空值检查、重复检查和模式验证的错误
作者:
Nate Rosidi,KDnuggets市场趋势与SQL内容专家,2026年10月30日发布于
数据科学
<div class="addthis_native_toolbox"></div>
Forrester的一项研究显示,四分之一的组织表示数据质量差每年给他们造成超过500万美元的损失,7%的组织认为这一数字超过2500万美元,IBM在其数据质量研究中引用了这一数据。这些损失的大部分永远不会在仪表板上显示出来。
标准的数据清洗流程能捕捉空值、重复行和类型不匹配。这些检查运行速度快且能发现真实问题,但一旦所有列都通过验证就会停止检查。
这四个错误仍然能绕过这些检查,最终破坏模型、仪表板或财务报告:实体以不同形式重复、值在范围内但语境不合理、包含未来信息的时间戳、以及分母中遗漏了会影响结果的人群。
每个错误都出现在StrataScratch的真实面试题中。这里展示的是我们对每个问题的粗略解读会遗漏什么,以及应该检查什么。
语法、语义和语用学的鸿沟
我们可以将数据质量检查分为三个层次,借鉴语言学的概念。
语法检查值是否格式正确:邮政编码是否5位数、日期是否可解析、列是否非空。
语义检查值是否符合其代表的含义:214岁的客户是否可能、凌晨3点的登录是否可信(如果商店在晚上10点关门)。
语用学检查相同类型的值是否需要根据使用者的不同进行不同测试:例如制药公司中,研发团队和生产团队可能对相同类型的记录有不同的有效性判断标准。
我们看到大多数生产流程只停留在语法层面。
Pandera和dbt测试(检查数据质量的SQL查询)默认进行类型、空值和范围检查,这些是通用库在不了解业务时也能实现的检查。语义检查需要理解数据含义的人。这个差距正是下面4个错误存在的地方。
静默数据错误 #1:重复实体
重复检查验证两行是否完全相同。
drop_duplicates() 会捕获两次 "John Smith, 123 Main St",但无法识别 "John Smith, 123 Main St" 和 "J. Smith, 123 Main Street" 这两个不同写法的相同客户,生命周期价值模型仍会将它们视为两个不同的人。
检查通过是因为行没有重复,但实体重复了。
这个亚马逊面试题故意要求你做相反的事:给定一个员工表,输出每个HR员工的名字和部门两次。
数据:员工表包含HR、行政和会计部门共13名员工。
#### 代码:
SELECT first_name, department
FROM worker
WHERE department LIKE 'HR'
UNION ALL
SELECT first_name, department
FROM worker
WHERE department LIKE 'HR';#### 输出:
官方答案使用的是 UNION ALL 。
当两个源系统无意中将相同的表发送到数据仓库,且在连接前没有进行去重处理时,也会出现类似问题。下游的每个 SUM 或 COUNT 都会翻倍,但不会触发任何空值检查。
该错误在平台上的其他两种方式中也会以相同方式出现,本节中的三个示例展示了重复如何影响下游指标:重复行、在更粗粒度下被重复计数的实体,以及因宽松连接而匹配过多的行。
微软、苹果和戴尔的案例需要使用 COUNT(DISTINCT user_id) 来统计月活跃用户数:如果在相同表中改用 COUNT(*),结果会改变,因为一个用户触发 10 次事件会被视为 10 个用户。
#### 修复此错误的方法
修复方法可以从两个方向入手:规范化字符串(转换为小写、去除标点、标准化地址),通过相似度评分代替等值判断,并将中间结果路由到审核队列。
隐性数据错误 #2:看似合理但无效的值
一个值可能符合其列的数据类型和声明范围,但仍然可能与所在行的实际内容不符。
一个 214 岁的客户通过了整数检查;交付数量为负数通过了数值检查。要发现这些错误,需要理解数字实际含义的规则,而非仅仅依赖其类型。
亚马逊、沃尔玛和Best Buy的案例要求找出每位经理的工资低于其下属员工平均工资两倍的人员,并展示该错误如何隐藏在常规聚合中。
数据:dim_employee 表包含 9 名员工,map_employee_hierarchy 表将每个 manager_empl_id 与直接下属的 empl_id 进行关联。此处我们仅使用 dim_employee 表的部分数据:
SELECT
h.manager_empl_id,
managers.salary AS manager_salary,
AVG(employees.salary) AS avg_employee_salary
FROM map_employee_hierarchy h
JOIN dim_employee managers
ON h.manager_empl_id = managers.empl_id
JOIN dim_employee employees
ON h.empl_id = employees.empl_id
GROUP BY 1, 2
HAVING managers.salary < 2 * AVG(employees.salary);这个查询本身没有问题。
dim_employee 表中单个错误的薪资(零、负数或以分而非美元输入)会改变平均值,并在结果中静默地移除或添加经理。该查询通过了所有语法检查。错误仅存在于单个单元格中。
修复方法是在数据摄入时应用业务感知约束和同行组合理性检查。
年龄在 0 到 120 之间、订单数量大于 0 是业务约束:它们定义了业务中可能的范围,而不仅仅是数据类型允许的范围。
将薪资与相同职级和地区的分布进行比较,而非仅仅与 0 比较,是更严格的检查:它可以发现单独看起来合理但与所在行实际内容不符的值。
隐性数据错误 #3:时间戳泄露
泄露发生在特征携带了本应描述时间点之后的信息时。
维基百科的定义最为清晰:本应在预测时不可用的信息仍然出现在训练数据中。
微软的案例要求你从一个完全没有时间戳的表中找出每位员工的当前薪资,表中仅包含每个员工的重复薪资记录,因此"当前薪资"必须通过假设薪资只会增加来推断:这是问题最纯粹的版本。
数据:ms_employee_salary 表包含 76 名员工的 98 条记录,其中多名员工有多个薪资记录,但没有任何信息表明哪个记录先发生。
SELECT
id,
first_name,
last_name,
department_id,
salary
FROM (
SELECT
*,
ROW_NUMBER() OVER (
PARTITION BY id
ORDER BY salary DESC, department_id DESC
) AS rn
FROM ms_employee_salary
) s
WHERE rn = 1
ORDER BY id ASC;ROW_NUMBER() 的这种用法是这里的正确解决方案。
这也是数据仓库在处理没有有效日期列的缓慢变化维度时的处理方式:"最新"会变成一种猜测,任何下游通过 employee_id 连接的特征都会继承这种猜测。如果使用这种推断出的"当前"薪资去重建6个月前的预测,历史记录会从它自己的未来获取信息。这就是数据泄露。
一个相关的亚马逊问题属于时间窗口错误,而非数据泄露错误:统计用户在首次购买后1至7天内进行第二次购买的情况(忽略同一天的购买)。
将严格的 BETWEEN 1 AND 7 改为宽松的 <= 7 后,同一天购买多个商品的购物车会被计入回头客,导致留存率被从未发生过第二次访问的行所虚增。这个查询在这里定义了"回头访问"的范围;模型不会接收到任何未来的数据。
结构性修复方案是:同时存储事件发生时间和行写入时间,并通过严格的 event_time <= feature_time 不等式连接特征,永远不要使用单纯的 MAX() 。
隐性数据错误 #4:幸存者偏差
幸存者偏差是一个分母问题:在你的查询运行之前,那些能告诉你真相的行已经被删除了。经典案例是仅用仍在交易的对冲基金计算基金指数回报率,每个已关闭的基金都会被剔除。同样的模式出现在任何人报告"2024年购买的客户"时,却未注意到这已经排除了所有在第一季度流失的客户。
这个 Meta 问题要求计算来自美国且状态为"open"的用户占比,使用名为 fb_active_users 的表。
数据:23 行,status 列标记每个用户为"open"或"closed"。
SELECT
SUM(
CASE
WHEN country = 'USA' AND status = 'open' THEN 1
ELSE 0
END
) * 100.0 / COUNT(*) AS us_active_share
FROM fb_active_users;此处的危险假设是将 fb_active_users 表视为完整的用户群体。
该表的 status 列同时包含"open"和"closed"行,因此这里没有进行任何数据裁剪。表名本身仍然是那种未经验证就被信任的信号。
如果像这样的表完全排除了被删除或停用的账户,而不是用标志位标记它们,COUNT(*) 会变成仅包含"幸存者"的分母,且没有空值检查能发现这个问题:缺失的行在数据管道看到数据之前就已经消失了。
一种低成本的早期检测方法:对查询中的每个群体过滤条件,绘制每个步骤的行数统计,并标记任何在信任最终结果前导致群体大幅减少的步骤。
对于时间戳泄露问题:使用严格的 event_time <= feature_time 连接条件代替单纯的 MAX() 函数,确保特征永远无法由尚未存在的数据构建生成。
针对幸存者偏差问题:在每个队列过滤步骤记录行数,并在某一步骤异常剔除大量样本时触发预警。
结论
我们仍然会对每个数据管道执行空值、重复值和模式检查。这些属于数据质量的低成本检测手段,用于验证值的格式是否正确。而值的真实性则是另一个需要单独判断的问题。
这里提到的四个模式——重复实体、看似有效实则不可能的值、时间戳泄露和幸存者偏差——都能通过第一层基础检查,但仍会破坏其上层构建的任何内容。要捕捉这些问题,需要由理解业务的人编写规则,这些规则不仅要了解数据的形态,更要理解数据的含义。没有现成的通用库能提供这种能力。
Nate Rosidi 是数据科学家兼产品战略专家,同时担任数据分析课程的兼职教授,也是 StrataScratch 平台的创始人。该平台帮助数据科学家通过顶尖公司的真实面试题准备面试。Nate 会撰写职业市场最新趋势分析,提供面试建议,分享数据科学项目,并全面覆盖 SQL 相关内容。
更多相关内容
- 为什么大多数数据科学笔记本在第一天后就失效了:如何构建…
- 数据科学家调试常见 Pandas 错误指南
- 7 个实际上是特性的 Python 错误
- 我如何使用一个混乱的 DoorDash 数据集构建数据清洗管道
- 用不到 50 行 Python 代码构建数据清洗与验证管道
- 5 个用于自动化数据科学管道的智能工作流
<hr class="grey-line"><br> <div><h3>我们推荐的 5 门免费课程</h3><br> </div>
Mailchimp for WordPress v4.14.1 - https://wordpress.org/plugins/mailchimp-for-wp/
/ Mailchimp for WordPress 插件
您可以从此处开始编辑。
如果评论已关闭。
<= 上一篇
下一篇 =>
#content end
<script type="text/javascript">kda_sid_write(kda_sid_n);</script>
最新文章
- 用 AI 助手逐步构建经典游戏:开发 Breakout 的全过程
- 静默数据错误:那些在所有清洗管道中存活下来的错误
- 目前在职位招聘中实际出现的 AI 技能
- 家用 AI 实验室必备的 5 个 Docker 容器
- 如何使用 OpenRouter 在 Python 应用中访问多个 AI 模型
- 这个开源模型能否击败 AI 检测器?让我们本地测试一下
热门文章
- 家用 AI 实验室必备的 5 个 Docker 容器
- 哪些本地语言模型最适合不同任务
- 数据科学家必备的 7 个高级 Python 设计模式
- 使用 OpenAI Agents SDK 在 Python 中构建你的第一个 AI 代理
- AI 代理技能必备的 5 个最佳 GitHub 仓库
- 2026 年你应该尝试的 5 个免费 AI 编程代理
- 2026 年你可以使用的 5 个免费 LLM API 提供商
- AI 软件克隆的兴起:为什么 REA 和 ArtCraft 正在病毒式传播
- 7 个可替代付费订阅的开源 AI 工具
- Google 开源 Harness²:无需重新训练的自我改进 AI 代理
#content_wrapper end
© 2026
Guiding Tech Media
|
关于
联系我们
广告合作
隐私政策
服务条款
2026 年 10 月 30 日由 Nate Rosidi 发布
blank
不,谢谢!
/.main_wrapper
<script defer type="text/javascript" src="https://s7.addthis.com/js/300/addthis_widget.js#pubid=gpsaddthis"></script>
noptimize
/noptimize