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

洗内容之前先定标准:姓名只留汉字、电话只留 11 位数字、城市只去前后空格。 两步替换把 7 行带 ^、4 行带空格的姓名洗干净,再做一遍复查:各剩 0 行。 同样是空格,前后的要去,中间的要留——替换要盯着「这个字符在这里代表什么」。

📝 本章和前面几章的关系

上一篇把列拆开了。这一篇开始洗内容,用的全是最朴素的字符串替换。 书里用 Excel 的 SUBSTITUTE。这里用 replace,逐字符对应,动作一模一样。

一、先定「干净」的标准

姓名:只留汉字,不留 ^ 和空格。 电话:只留 11 位数字,不留 +86 和横杠。 城市:去掉前后空格,中间那个空格保留——「北京 朝阳」是一个整体。

标准要写在纸上,不能一边改一边想。标准含糊,改法就一定不统一。 这三条标准看起来琐碎,但它们决定了后面所有匹配能不能对上。

二、姓名里的 ^ 和空格

姓名带 ^ 的 7 行,带空格的 4 行。 两步替换:先去掉 ^,再去掉空格。收工后再查一遍,两个都是 0 行。 这个「收工后再查一遍」不能省。替换是盲操作,你只能靠复查知道有没有漏。

清洗的每一步都要有一步对应的复查。没有复查的清洗,等于没有清洗。

三、电话里的 +86 和横杠

去掉 +86 和横杠之后,63 行全是 11 位数字。 这一步有个陷阱:+86 是「国家码」,横杠是「排版」,两者性质完全不同,但处理方式一样——删。 因为我们要的是「能唯一标识一个号码的 11 位数字」,其余都是装饰。

但如果业务上需要保留国家码(比如打国际电话),那就不能删,得单独拆成一列。 删不删不取决于它是什么,取决于你后面要拿它干什么。

四、城市只去前后空格

用 strip 去掉前后空格,中间的保留。 如果中间那个也删了,「北京 朝阳」会变成「北京朝阳」,反而更难和别的表匹配。 同样是空格,在两个位置代表两种东西:一个是排版残留,一个是「市 区」的分隔。

替换要盯着「这个字符在这里代表什么」,不是盯着「这是个空格」。 这一条是整章最容易被忽略、也最容易造成静默错误的地方。

五、替换的边界

这一节做的全是「删掉某个固定字符」:^、+86、横杠、前后空格。 这类活用 replace 一行一个,够用、好懂、不会写错。 但姓名里「姓和名之间的分隔」不是固定字符——它可能是 ^、可能是空格、可能是全角空格。

那就不是替换单个字符了,是匹配一类模式。下一篇讲这个区别。

六、动手 Demo:替换掉那些碍事的字符

拿几行真实样例,逐个字段替换:姓名的 ^ 和空格、电话的 +86 和横杠、城市的前后空格。 每替换一次都复查一遍,看还剩几行没干净。

NAMES = ["王^小明", "李娜", "张 静", "刘艳", "陈^芳", "杨 强"]
TELS = ["+8613800001000", "139-0000-3271", "13800005626",
        "13800005939", "+8613800006252", "13800006565"]
CITIES = [" 北京 朝阳 ", "上海 浦东", " 广州 天河 ", "深圳 南山",
          "杭州 西湖", " 成都 武侯 "]

print("姓名带 ^ %d 个,带空格 %d 个"
      % (sum(1 for n in NAMES if "^" in n), sum(1 for n in NAMES if " " in n)))
clean_n = [n.replace("^", "").replace(" ", "") for n in NAMES]
print("清洗后:带 ^ %d 个,带空格 %d 个"
      % (sum(1 for n in clean_n if "^" in n), sum(1 for n in clean_n if " " in n)))
print(clean_n)

print()
clean_t = [t.replace("+86", "").replace("-", "") for t in TELS]
print("电话清洗后都是 11 位:%s" % all(len(t) == 11 for t in clean_t))
print(clean_t)

print()
clean_c = [c.strip() for c in CITIES]
print("城市去前后空格后:", clean_c)
print("城市写法去重后 %d 种" % len(set(clean_c)))

实跑输出:

--------------------------------------------------------------------
替换掉那些碍事的字符
--------------------------------------------------------------------

  ① 先定「干净」的标准
  --------------------------------------------------------------------
  姓名:只留汉字,不留 ^ 和空格。
  电话:只留 11 位数字,不留 +86 和横杠。
  城市:去掉前后空格,保留中间那个(北京 朝阳 是个整体)。
  标准要写在纸上,不能一边改一边想。标准含糊,改法就一定不统一。

  ② 姓名里的 ^ 和空格
  --------------------------------------------------------------------
  姓名带 ^ 的 7 行,带空格的 4 行。
  两步替换:先去掉 ^,再去掉空格。做完再查一遍:
  还剩 ^ 的 0 行,还剩空格的 0 行。

  ③ 电话里的 +86 和横杠
  --------------------------------------------------------------------
  去掉 +86 和横杠之后,63 行全是 11 位数字。
  这一步有个陷阱:+86 是「国家码」,横杠是「排版」。两种东西性质不同,但处理方式一样——删。

  ④ 城市只去前后空格
  --------------------------------------------------------------------
  前后空格用 strip 去掉,中间的保留。去重后有 8 种写法,另有 5 行城市是空的。
  中间那个空格如果也删了,「北京 朝阳」就变成「北京朝阳」,反而更难匹配。
  ! 替换要盯着「这个字符在这里代表什么」。同样是空格,前后的要去,中间的要留。

  ⑤ 替换单个字符 vs 盯着模式
  --------------------------------------------------------------------
  这一节做的全是「删掉某个固定字符」:^、+86、-、前后空格。
  这类活用 replace 一行一个,够用、好懂。
  但如果要处理「姓和名之间的分隔」这种模式,replace 就不够用了。下一篇讲为什么。

每一步后面都跟一句复查,这不是啰嗦——替换是盲操作,只有复查能证明它生效了。 城市那一行注意看:strip() 只动两头,「北京 朝阳」中间那个空格原样保留。 这就是「盯着含义」和「盯着字符」的区别。

30 秒实验

  1. 如果电话里出现「+86 13 0000 1000」这种中间带空格的写法,上面的两步替换还能洗干净吗?
  2. 把姓名里的空格也删掉,会不会误删少数民族名字里的间隔号附近的东西?
  3. 想一想:为什么「删之前先问这个字符代表什么」比「先删了再说」安全?

常见误区

  1. 替换完不复查:漏了哪一行你永远不会知道,直到统计出错。
  2. 把所有空格都删掉:会把「北京 朝阳」压成「北京朝阳」,反而更难匹配。
  3. 一律删国家码:要打国际电话的场景里,+86 是有用信息。
  4. 一边改一边想标准:同一批数据会被洗成两三种口径,比不洗还乱。
自测三问(点开对答案) 1. 洗内容之前要先做什么? —— 定标准:每个字段「干净」是什么样。标准定了才能批量替换,也才能复查。 2. 为什么每步替换后面都要复查? —— 替换是盲操作,不复查就不知道有没有漏行。复查一行代码,能挡住整批错误。 3. 同样是空格,为什么前后要去、中间要留? —— 前后的空格是排版残留,没有意义;中间的空格是「市 区」的分隔,是有信息的部分。

固定字符能这么删。可姓名里的分隔有时是 ^、有时是空格,那就得换一种办法了。

相关笔记