Community编程与开发github.com

ZrobDev/数据清洗

用于在新接触 CSV、parquet 或 xlsx 文件时先进行探查,或在清洗、去重、标准化记录、强制转换日期/货币/ID,或者对照衍生文件(OCR 或扫描文档与系统导出文件对比、上传文件与数据库对比)核对数据源时使用。涵盖行数不匹配、前导零丢失、重复数据、控制总计、反连接和异常报告。

数据清洗 是什么?

该技能提供了一套严谨的结构化流程,用于对表格数据(CSV、Parquet、XLSX 格式)进行探查、清洗、去重和核对。它强调每一步操作(如类型转换、缺失值填充、合并)必须被完整记录并报告,禁止静默修改原始数据。技能要求在对数据做任何修改前,先以文本形式探查所有列,以避免类型推断时出现前导零丢失等问题。数据清洗过程遵循可审计的管道模式,每一步都输出行数变化和原因。在数据核对场景中(例如系统导出文件与 OCR 扫描结果对比),技能提供了从控制总计、多轮模糊匹配到字段级比较的完整审计方法,并要求最终的总数平衡,以确保不遗漏任何错误。该技能支持 DuckDB 和 Polars,并给出了明确的安全规则,例如禁止覆盖原始数据源、将数据库密钥保存在 `/run/secrets/` 中。

兼容平台~Claude Code~Codex CLI~Cursor
Part ofMobile
npx skills add https://github.com/ZrobDev/data-engineering-skills/tree/HEAD/skills/data-cleaning

Installed? Explore more 编程与开发 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

数据清洗与核对

默认工具:本地文件使用 DuckDB 2.0 SQL 或 Polars 2.x 惰性 API(scan_csv、scan_parquet)。仅在库强制要求时使用 Pandas。Python 通过 uv 运行,使用 Ruff 进行代码检查。在依赖 Polars 2.0 或 DuckDB 2.0 中任何已更改的 API 详情前,请对照当前文档进行验证(参见 references/landscape-2026-11.md)。

绝不静默修复

每一次强制转换、丢弃、填充默认值或合并操作都必须被计数并报告。一个清洗步骤应记录为 (输入行数, 输出行数, 变更行数, 原因)。转换失败应变为 NULL 并计数,绝不能丢弃行或使用猜测值。保持原始输入不动;将清洗后的输出写入新路径。如果某个步骤的计数令人意外,请停止并报告,不要继续。

操作流程

  1. 探查(只读)。2. 确定键、粒度和类型。3. 通过由多个小型计数步骤组成的管道进行清洗。4. 如果存在两个数据源,则进行核对。5. 输出异常结果以及包含所有计数的运行摘要。

1. 接触数据前先探查

首先将所有列作为文本读取(DuckDB 中使用 all_varchar=true,Polars 中使用 infer_schema=False),因为基于样本的类型推断会静默地将 00123 转换为 123,并将混合列转换为第一行的类型。 然后对每一列计算:行数、空值/空字符串比率、唯一值数量、最小/最大值(作为文本和解析后)、最短/最长长度,以及匹配某种类型模式(整数、小数、ISO 日期、其他)的值的比例。 DuckDB 的 SUMMARIZE 通过一次调用即可完成大部分工作。

记录清洗前的信息:总行数、不同候选键的数量、每个键的最大行数,以及文件头的确切内容(尾随空格、BOM、重复名称)。候选键仅在标准化后唯一值数量等于行数时才是真正的键;请测试它,不要假设。

检查:整行重复、文件头在文件中重复出现、数据中包含的页脚/总计行、哨兵值(“N/A”、“-”、“0000-00-00”、9999-12-31)、混合编码,以及实际上是标识符的数字列。

2. 类型与解析

  • ID、邮编/邮政编码、账号和电话号码始终是字符串。仅在宽度有文档记录时,才将补零宽度存储为规则(lpad 填充到已知宽度);否则保持原样读取。
  • 日期:对每列使用明确的格式进行解析(try_strptime / str.to_date(format, strict=False))。绝不让引擎猜测日月顺序。对于没有文档记录顺序的模糊值(03/04/2026)属于异常,而非随意选择。明确声明时区处理。
  • 货币:解析为整数最小单位或 DECIMAL(p,s),绝不用浮点数。根据区域规则去除货币符号和千位分隔符:1.234,56 和 1,234.56 属于不同区域,因此需要按来源决定,并断言每个值都匹配所选模式。括号和尾随减号表示负数。按行记录货币;不要跨货币求和。
  • 布尔值和枚举:从显式映射表中转换;未映射的值归入异常。
  • 每次强制转换后,计算“转换前非空值数量”减去“转换后非空值数量”。该数字即为转换失败计数,将包含在报告中。

3. 标准化

应用于派生的匹配键,而不是原始列,以便保留原始值: Unicode NFKC 标准化、去除首尾空格、压缩内部空白、转换为小写、去除零宽空格和不间断空格。 NFKC 是有损的(连字、全角字符、上标),这正适合匹配场景,但不适合显示。 对于标识符,还要去除分隔符(-、/、空格),并明确处理 O/0 和 I/1。 DuckDB 核心 SQL 不直接支持 NFKC;请在 Polars/Python 中处理,或注册一个 UDF(请先验证当前支持情况)。

4. 去重

  • 完全重复:丢弃并计数;保留幸存行的原始行号。
  • 按键重复:首先判断它们是真正的重复记录还是不同的事件(相同的发票号但金额不同,这是冲突,而非重复)。
  • 幸存者选择必须确定并声明: ROW_NUMBER() OVER (PARTITION BY key ORDER BY updated_at DESC NULLS LAST, completeness DESC, source_row DESC) = 1(DuckDB 使用 QUALIFY)。 始终以唯一决胜项(如来源行号)结束 ORDER BY,因为平局情况下会通过任意扫描顺序解决。 在 Polars 中使用 sort(...) 然后 unique(subset, keep="first", maintain_order=True)。
  • 模糊去重只是生成候选项,绝不能自动合并:基于一个廉价键(标准化邮政编码、前 3 个字母、年份)进行分块,在块内评分,仅在阈值较高且有一个精确的辅助字段一致的情况下才自动接受,并将中间范围发给人工处理列表。报告阈值和计数。

5. 核对审计

源头数据 = A,衍生数据 = B(例如系统导出文件与 OCR 扫描文档)。在检查是提取问题还是键问题之前,绝不说“B 是错误的”。

  1. 首先检查控制总计。 A 和 B 中的行数以及每个货币/数量列的总和,加上不同键的数量。不匹配告知您是应该期望缺少行、多余行还是金额漂移。总计匹配而行数不同意味着错误相互抵消;继续检查。
  2. 键匹配。 在两侧构建相同的标准化键(第 3 节)。首先进行精确匹配,然后按严格程度递减的显式轮次进行(例如,仅键,然后键加日期窗口),每个匹配都标记产生它的轮次。切勿混合未经标记的轮次。
  3. 双向反连接。 A 中但不包含在 B 中的(从衍生数据中缺失)和 B 中但不包含在 A 中的(衍生数据中意外出现的,通常是 OCR 键拼写错误或重复)。报告两者;只读取一个方向会隐藏一半的错误。在将某行标记为缺失之前,检查其键是否与任何一侧的重复项冲突(多对多连接会夸大总计)。
  4. 对匹配对进行字段比较。 在相同标准化处理后逐字段比较。 货币容差:以最小单位比较,abs(a - b) <= tol,其中 tol 是提前声明的(分类账为 0;仅当不同数据源舍入方式不同时,每行允许 1 个最小单位的舍入容差,小额金额绝不允许使用百分比)。 日期:精确匹配,或声明的天数窗口。 文本:标准化后相等,然后给出一个相似度评分,报告为分数,而非通过/不通过。
  5. 分类,而不仅仅是差异。 每个异常都有一个原因代码:MISSING_IN_B、EXTRA_IN_B、DUP_KEY_A、DUP_KEY_B、AMOUNT_MISMATCH、FIELD_MISMATCH、UNPARSEABLE_A、UNPARSEABLE_B、AMBIGUOUS_MATCH。不可解析的行是异常,绝不能静默地从总计中排除。
  6. 闭环。 最终的恒等式必须成立:|A| = 匹配项 + 缺失于B项 + A侧多余重复项 + 不可解析A项(以及 B 的等价式)。如果无法平衡,则审计未完成。

用于多轮匹配、反连接、容差和平衡检查的 SQL 模式位于 references/reconciliation-recipes.md 中。

异常报告

每行一个异常,无需工具即可阅读: reason_code、key、A 源文件和行号、B 源文件和行号(以及扫描文档的页码)、field、a_value、b_value、delta、suggested_action。 按金额影响降序排序,然后按原因排序。 在顶部放置运行摘要:带有行数和校验和的输入文件、每个清洗步骤及其计数、双方的控制总计、平衡恒等式、使用的容差。 为机器写入 CSV,为人类编写简短的 markdown 或 xlsx 视图;不要将数千行数据直接转储到聊天中,应按原因汇总计数并显示前几条。

安全护栏

  • 断言,不要目测:连接后的行数、去重后的键唯一性、货币列不能是浮点数。
  • 在非唯一键上进行左连接会成倍增加行数;每次连接前后都要检查计数。
  • 不要覆盖 A。对源头数据的更正属于人工决策,需列在报告中。
  • 数据库的密钥从 /run/secrets/<name> 读取,而不是环境变量或命令行。
  • 一旦规则稳定,值得添加声明式检查(soda-core、great_expectations);但上述分析和计数是优先步骤。

实际应用中的经验教训会累积在 references/gotchas.md 中。

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

相关技能