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

处理客户数据

「没有」不止一种写法,「相同」也是

在 TT Lab 中继续学习

一句话总结

在同一个文件里,“没有”有五种写法,“相同”有三种含义。两者都必须按规则而不是按写法来处理,数字才不会晃动。

为什么需要它

拿到客户名单,算出了平均购买额,结果是 57,500 韩元。客户公司说是 46,000 韩元。看的是同一个文件,答案却不同。

原因通常只有一处:金额列没有值的行,一方把它排除在外来计算,另一方则填成 0 来计算。而更麻烦的是,双方都不知道自己是这么做的。电子表格的平均函数会跳过空白格,而用 Python 写的代码往往会用 float(x or 0) 把它填成 0。两者都不会报错。

再叠加上写法的问题。在同一个文件里,“没有”会以空白、NULL、NA、-、0,以及 9999-12-31 这样的哨兵日期混在一起出现。系统有三个,写法就有三种,而合并的时候没有人去统一。

工作原理

第一件事是计数。修改之前,要先得出每种写法各有多少条。没有这张表,无论做什么处理,都无法判定这个处理是否正确。

第二件事是区分 0 和没有。这是两个不同的事实。购买额为 0 韩元的客户是“已经注册但还没买过”,购买额为空的客户是“不知道是多少”。计算平均值时,前者要计入分母,后者要排除。可是如果文件里写的是 0,那么是哪一种,光看文件是无法知道的。所以要把 0 的条数另行统计并报告,至于是哪一种,则去问业务方。

같은 400행, 같은 금액 칸
  결측을 0 으로 채운 평균     46,000원   분모 400
  결측을 뺀 평균              57,500원   분모 320
  0 까지 뺀 평균              61,333원   분모 300

该代码块中的韩文依次说明:同样的 400 行、同样的金额列;把缺失值填成 0 后的平均值为 46,000 韩元,分母 400;去掉缺失值后的平均值为 57,500 韩元,分母 320;连 0 也去掉后的平均值为 61,333 韩元,分母 300。

三个数字都是对的。不同的是统计了什么,而没有说明这一点的报告,就是错误的报告。

第三件事是哨兵值。9999-12-31 是把“还没结束”硬塞进日期列的写法。如果把它当作日期来接受,并计算处理周期的平均值,会得出几千年。看起来可笑,但实际上只要混进几条这样的值,平均值就会悄悄地被抬高,而且不会引人注意。

在现场相遇的样子

接下来是重复。“相同”有三种含义。

类型 是什么 应如何处理
完全重复 一字不差的行出现了两次 只保留一条。没有需要判断的东西
键重复 标识符相同,内容却不同 无法自动修复。向业务方询问哪一个才是对的
只在人眼里相同 只是写法不同的同一个对象 规范化,并建立规则来判定

第三种最难。문샵、(주)문샵、문샵 주식회사(韩文,依次意为“Moonshop”“(株)Moonshop”“Moonshop 股份有限公司”)对人来说是同一个,对机器来说却是三个。所以比较之前要先规范化:去掉前后的空格,去掉公司类型的写法,统一大小写,从电话号码中只留下数字。

最常见的事故就出在这里:规范化是为了比较,而不是合并的依据。名称规范化之后两个值变得相同,并不意味着它们是同一家公司。可能是不同的分店,也可能商号碰巧相同。所以判定规则要同时看多个列,例如“规范化后的邮箱相同,就是同一个人”,或者“姓名和电话号码都相同,就是同一个人”。

而且,含有缺失值的列不能用作判定的键。如果把邮箱为空的行,按“邮箱相同”归为一组,几十个不同的客户就会变成一个人。这种事故是悄无声息的,光看结果,还会觉得去重做得非常好。

最后是分块(blocking)。如果把 1 万行全部两两配对比较,大约有 5000 万对。所以先用成本低的键把行分组,只在同一个块内比较。比如电话号码的后四位,就可以作为块键。分块不是免费的:块不同,就永远碰不到一起。所以选定块键之后,要在小样本上用全量比较来测量“这个键漏掉了多少对”,并把这个损失写进文档。

实际工作中真正重要的事

下一项实验要做什么

亲手生成一份合并自三个系统的客户名单,按类型统计“没有”的写法,并把缺失值填成 0 后的平均值、去掉缺失值后的平均值、连 0 也去掉后的平均值,这三个并排列出。还要用数字看哨兵日期如何破坏平均处理周期。接着区分完全重复和键重复,通过规范化和分块缩小需要比较的候选,再用判定规则把同一个人归为一组。最后,用全量比较测量分块漏掉的配对,并把这个损失一并报告。评分器会在每次生成不同的名单,真实运行你的工具并核对结果。