📈 第13章 整理数据
📄 一句话概括

排序不改变数据,但它让「重复」从看不见变成看得见——相同的记录会挨在一起。 63 行去重后剩 60 行,去掉 3 行完全重复的。只差一个字段的两条不能删。 去重是一面照妖镜:去完之后还剩 4 行城市空、10 行复姓要特别处理。收尾不等于干净。

📝 本章和前面几章的关系

上一篇把姓名拆开了。这一篇做收尾:排序、去重、把剩下的问题列清楚。 书里最后那句「可能尚未大功告成」,是整章最该记住的一句。

一、排序是为了让重复挨在一起

去重最快的办法不是一条条查,是先排序——相同的记录会排在一起,扫一遍就够。 这一步在没有程序的年代是手动的:排序、往下翻、删掉重复行。 现在一行代码,但道理没变:先让同类相邻,再动手。

这个思路在别处也用得上:找重复的订单号、找同一秒内的日志、找相邻的异常值。

二、排序之后看什么

按「姓名 + 电话」排序,第一条和最后一条各是谁,一眼就看到数据的边界。 排序本身不改变任何数据,只改变顺序。但它改变了「你能看见什么」。 没排序的时候,重复的两行可能隔了三万字,你永远发现不了。

排序是一种「让问题自己冒出来」的手段。

三、去重的标准要写死

这里的「完全重复」指五个字段全一样:姓名、电话、城市、日期、等级。 63 行 → 60 行,去掉 3 行。 只差一个字段的两条不能删——那可能是同一个人换了等级,也可能是两个人同名。

去重之前必须说清「按哪几列判重」。这个标准不同,结果就不同。 按姓名判重会删掉同名的不同人;按电话判重会删掉一家人共用一个号。

四、去重是一面照妖镜

去完重,再查一遍还剩什么:城市为空的 4 行,复姓 10 行要特别处理。 这些不是重复行,是「记录本身就不完整」或者「规则覆盖不到」。 删不掉,也补不了。能做的只有标出来,交出去的时候说清楚。

排序去重不只是收尾,它会把前面几步留下的问题一次性全翻出来。 翻出来不等于解决。收尾这一节能做的是「知道还剩什么」,不是「什么都没有了」。

五、交货,以及还没做完的事

最终交付:60 行 × 5 列,姓名字段只有汉字,电话只有 11 位数字,城市写法统一。 但书里最后一句是「可能尚未大功告成」——因为这份名单的来源本身有问题。 数据干净不等于可以用。来源不合法、授权不明确的数据,越干净越危险。

这一章教的是技术,最后一句提醒的是边界。两件事都得记住。 技术上做到 60 行全干净,法律上这 60 行可能一行都不该有。

六、动手 Demo:排序、去重与收尾

拿一份带重复的短名单,先排序,再去重,最后查还剩什么不干净。 每一步都打印行数,确认去重去了几条、剩下的问题有几条。

ROWS = [
    ("王小明", "13800001000", "北京 朝阳", "2023-01-01", "VIP"),
    ("李娜", "13800002000", "上海 浦东", "2023-02-02", "A"),
    ("王小明", "13800001000", "北京 朝阳", "2023-01-01", "VIP"),
    ("张静", "13800003000", "", "2023-03-03", "B"),
    ("欧阳明", "13800004000", "广州 天河", "2023-04-04", "C"),
    ("李娜", "13800002000", "上海 浦东", "2023-02-02", "A"),
    ("司马光", "13800005000", "", "2023-05-05", "A"),
]

print("原始 %d 行" % len(ROWS))

srt = sorted(ROWS)
print("排序后第一条 %s,最后一条 %s" % (srt[0][0], srt[-1][0]))

seen = set()
keep = []
for r in srt:
    if r not in seen:
        seen.add(r)
        keep.append(r)
print("去重后 %d 行,去掉 %d 行" % (len(keep), len(ROWS) - len(keep)))

print()
print("城市为空 %d 行" % sum(1 for r in keep if not r[2]))
print("复姓 %d 行" % sum(1 for r in keep if r[0][:2] in ("欧阳", "司马")))

实跑输出:

--------------------------------------------------------------------
排序、去重与收尾
--------------------------------------------------------------------

  ① 排序是为了让重复挨在一起
  --------------------------------------------------------------------
  去重最快的办法不是一个个查,是先排序——相同的记录会排在一起,扫一遍就够。
  这一步在没有程序的年代是手动的:排序、往下翻、删掉重复行。
  现在一行代码,但道理没变:先让同类相邻,再动手。

  ② 排序之后看什么
  --------------------------------------------------------------------
  按「姓名 + 电话」排序,第一条是 刘伟,最后一条是 黄艳。
  排序本身不改变数据,只改变顺序。但它让「重复」从看不见变成看得见。

  ③ 去重
  --------------------------------------------------------------------
  63 行 → 60 行,去掉了 3 行完全重复的记录。
  「完全重复」的标准是五个字段全一样。只差一个字段的两条,不能删——那是两条不同的记录。

  ④ 去重是一面照妖镜
  --------------------------------------------------------------------
  去重之后,城市为空的还有 4 行,等级为空的还有 0 行。
  这些不是重复行,是「记录本身就不完整」。删不掉,也补不了,只能标出来。
  还有 10 行是复姓,姓名字段虽然干净了,但「姓」怎么切仍要特别处理。
  排序去重不只是收尾,它会把前面几步留下的问题一次性全翻出来。
  翻出来不等于解决。这一节能做的是「知道还剩什么」,不是「什么都没有了」。

  ⑤ 交货,以及还没做完的事
  --------------------------------------------------------------------
  最终交付:60 行 × 5 列,姓名字段只有汉字,电话只有 11 位数字,城市写法统一。
  但书里最后一句是「可能尚未大功告成」——因为这份名单的来源本身有问题。
  数据干净不等于可以用。来源不合法、授权不明确的数据,越干净越危险。
  这一章教的是技术,最后一句提醒的是边界。两件事都得记住。

注意排序后的第一行是「司马光」——按 Unicode 码位排,不是按拼音。 中文排序的规则要单独说清,默认行为往往不是你想要的那个。 这份短样例去重后还剩 2 行城市是空的。这不是去重没做好,是这批数据本来就没填。收尾不等于修补。

30 秒实验

  1. 如果按「姓名」单独判重,这份名单会剩几行?会误删谁?
  2. 如果按「电话」判重呢?一家人共用号码的情况会被怎么处理?
  3. 想一想:为什么排序能让人发现重复,而不排序就很难?

常见误区

  1. 不排序直接去重:人工核对时重复行散在全文里,根本看不出来。
  2. 判重标准不写明:按姓名判还是按五列判,结果完全不同。
  3. 去重后宣布数据干净:去重只解决重复,不解决缺失和规则覆盖不到。
  4. 忽略数据来源:技术上清洗完成,不等于可以合法使用。
自测三问(点开对答案) 1. 为什么去重之前要先排序? —— 排序让相同的记录相邻,扫一遍就能发现重复。不排序的话重复散落在各处,靠人眼找不可能。 2. 「完全重复」的标准为什么要写清楚? —— 按几列判重决定删掉哪些行。标准不同,结果就不同,甚至可能删掉真实的不同记录。 3. 为什么说收尾不等于干净? —— 去重只处理重复。缺失字段、规则覆盖不到的特殊情况,去重之后依然存在。

第 13 章到此收口,主体十三章全部写完。接下来是三个附录:尾声、安装说明、术语与索引。

相关笔记