TT Lab
开始
学习 学习路径 课程

处理客户数据

第 13 周营收变成 0 — 把接收契约写进代码

在 TT Lab 中继续学习

目标

构建工具 schema.py:从每周收到的文件中提取 schema 并固化成指纹,分类列的新增、删除、重命名和类型变更,并按兼容规则自动判定。最后,通过值的分布抓出名称和类型都不变而只有含义变了的列。

为什么重要

别人提供的文件,其 schema 会在不通知的情况下变化。发送方只是整理了自己系统的字段,而接收方一直把它当作契约在使用。我们无法控制修改的一方,所以能做的只有让管道先发现“已经变了”这个事实。 发现之后更重要。因为多出一个不认识的列就停止加载,就没有人再相信这个警告;而必需列消失了却照样放行,数字就会悄悄出错。所以要把哪些会被破坏、哪些不会被破坏写成规则。默认值是两条:不认识的列放行,消失的必需列中止。 还有一样东西,schema 检查永远看不到:金额单位从韩元变成千韩元时,列名和类型都没变。检查全部通过,而销售额只剩下千分之一。这样的变化只有从值的分布才能看出来,而分布不是证据,而是线索。 评分器不会相信你的文字。它在临时目录里摆好评分器生成的每周文件,真实运行你的工具,核对分类和判定。商号和金额每次运行都会变。

步骤

  1. 创建并运行 /root/drift/gen_weeks.py,在 /root/drift/weeks 下生成从 w01.csv 到 w07.csv 的文件。30 条订单始终相同,只有 schema 在变化。
  2. 在 /root/drift/schema.py 中实现 fingerprint,让它给出列名、类型、值的样本和指纹,并把 w01.csv 的指纹保存到 /root/drift/baseline.json。
  3. 增加 diff <기준 JSON> <파일>(占位符依次为基准 JSON、文件),让它分类列的新增和删除。
  4. 让 diff 找出值的样本大量重合的一对,并分类为重命名。被识别为重命名的列,要从新增和删除的列表中去掉。
  5. 让 diff 分类同一列的类型变更,同时包括名称变了、类型也变了的情况。
  6. 在 /root/drift/contract.json 中声明必需列、可选列和规则,并让 gate <기준 JSON> <파일>(占位符依次为基准 JSON、文件)判定 pass、warn、stop。
  7. 增加 watch <기준 CSV> <파일>(占位符依次为基准 CSV、文件),让它测量数值列中位数的变化,并把单位发生变化的周次写入 /root/drift/meaning.json。
  8. 一次性判定七个周次,生成 /root/drift/drift_report.json 和 /root/drift/drift_report.md。

参考

生成七周的接收文件

创建并运行 /root/drift/gen_weeks.py,在 /root/drift/weeks 下生成从 w01.csv 到 w07.csv 的文件。30 条订单始终相同,只有列在每个周次发生变化。

先把基准周次的行做成字典列表,每个周次只调整列的列表和值,再重新写出即可。值必须始终相同,之后才能通过值的样本找出重命名。只有单位变化的那个周次才修改值。

把 schema 固化成指纹

在 /root/drift/schema.py 中实现 fingerprint <파일>(占位符为文件),让它给出 rows、columns、digest,并把 w01.csv 的指纹保存到 /root/drift/baseline.json。

为每一列收集值并推断类型,把不同的值排序后取前 20 个作为样本。指纹只把名称和类型连起来做哈希:把样本也放进去,每次数据变化指纹都不同,就没有用了。

区分新增的列和消失的列

增加 diff <기준 JSON> <파일>(占位符依次为基准 JSON、文件),让它给出 added、removed、renamed、retyped 四个列表。这一步只填写新增和删除也可以。

从基准指纹和新文件的指纹中取出列名集合,求差集,就得到新增和删除。列表要排序后输出:顺序如果每次运行都不同,就无法比较。

找出只改了名称的列

让 diff 在消失的列与新出现的列的值样本的杰卡德相似度达到 0.8 以上时,分类为重命名。被识别为重命名的列,要从 added 和 removed 中去掉。

只看名称,是消失了一个、新增了一个;看值的话,它们是同一列。对每个消失的列,求它与各新出现的列的样本集合的交集大小除以并集大小,选出最高的一对,只在超过阈值时才视为重命名。

找出类型变了的列

让 diff 把同名列的类型变更,以 [이름, 옛타입, 새타입](占位符依次为名称、旧类型、新类型)的形式放入 retyped。名称变了、类型也变了的情况,使用新名称放入。

类型是推断出的值,所以数据只要稍有不同就可能变化。因此要同时放入旧类型和新类型,以便区分整数改成了小数写法,和数字变成了字符串。下一步会把这两种情况当作不同的等级来处理。

把兼容规则写死在代码里

在 /root/drift/contract.json 中声明 required、optional、rules,并让 gate <기준 JSON> <파일>(占位符依次为基准 JSON、文件)给出 {"verdict": …, "reasons": [...]}。不认识的列放行,消失的必需列为 stop。

必需列的清单是由业务决定的。没有 order_id、shop_id、qty、amount_krw、ordered_at,就无法生成统计;而没有 shop_name 和 weight,统计照样能生成。reasons 要排序后输出,并在每条原因里写上是哪一列引起的。

抓住 schema 看不到的变化

增加 watch <기준 CSV> <파일>(占位符依次为基准 CSV、文件),让它测量两个文件中都有的数值列的中位数和比例。并把单位发生变化的周次,以 column、median_before、median_after、ratio、flag、schema_verdict 写入 /root/drift/meaning.json。

这种变化中,列名和类型都没有变,所以前面步骤的判定全部通过,只有值的分布会下沉。比例在 3 以上或在三分之一以下,就要标注需要人来看:这不是证据,而是线索。在 schema_verdict 中,把同一周次的 gate 判定也写上,以留下“schema 检查看不到这一点”的事实。

用一份报告说明七周的情况

把七个周次与基准周次核对,在 /root/drift/drift_report.json 中写入 baseline、weeks、pass、warn、stop,并在 /root/drift/drift_report.md 中分 ## 무엇이 바뀌었나 ## 무엇이 깨지고 무엇이 안 깨지나 ## 자동으로 잡히지 않는 것 ## 보내는 쪽과 맞출 것 四节书写(韩文标题,依次意为“什么变了”“什么会被破坏、什么不会被破坏”“无法自动抓到的内容”“需要与发送方对齐的内容”)。

weeks 是以文件名为键的对象,其中包含 verdict、added、removed、renamed、retyped。把基准周次自己也放进去,判定会得到 pass,便于核对。报告里要用数字写明出现 stop 的周次及其原因。