第14讲:文本字段处理简介

数据挖掘与R语言

2026年10月20日

本讲导览

  • 一、前课测验(5 min)→
  • 二、问题导入(4 min)
  • 三、合:unite()(6 min)
  • 四、拆:separate()(8 min)
  • 五、去空格:str_replace_all()(5 min)
  • 六、找:filter() 配合 str_detect()(6 min)
  • 七、输出:cat() 与 glue()(4 min)
  • 八、综合与收官(6 min)

一、前课测验

第 11–13 讲:缺失值、异常值、重复值

1.1 选择题

1. 第 11 讲 company_data 里华东组均值 807、西南组均值 1463。补 revenue 的 4 个空缺时,为什么不能用全表均值 1135 一把填?

A. 全表均值算错了 B. 会把两组的差距抹平,组间结构被人为压低 C. 均值不能用来补缺失值 D. 只能用中位数

2. bill 表 16 行,quantile() 算出 Q1 = 3175、Q3 = 5500。按 1.5×IQR 法,下面哪个值会被判为异常?

A. 5800 B. 8500 C. 8987 D. 12000

1.2 填空题

3. 想一行代码直接抓出一列里被箱线图画成离群点的值,写 ____________(x)$out;想看须线两端与 Q1/中位数/Q3 的位置,取它的 $______。

4. df %>% distinct(company, .keep_all = TRUE) 默认保留每个公司第______次出现的那一行;如果想留下金额最大(或最新上报)的那一条,可以先调用______函数。

二、问题导入——一张报名表为什么汇总不了

不缺失、不异常、不重复,可它照样不好用

2.1 现场发的报名表(教学虚构,代码可见、可复现)

▶️ 查看代码
roster <- data.frame(
  student_id  = c("2024-0512-01", "2024-0512-02", "2024-0615-01",
                  "2024-0615-02-E", "2024-0512-03", "2024-0615-03",
                  "2024-0512-04", "2024-0615-04"),
  surname     = c("王", "李", "张", "赵", "陈", "刘", "杨", "周"),
  given       = c("哲", "小雨", "伟", "婷", " 浩", "琪", "磊", "睿"),
  birth_year  = c("2005", "2004", "2005", "2005", "2004", "2005", "2005", "2004"),
  birth_month = c("03", "11", "07", "01", "09", "05", "12", "02"),
  birth_day   = c("14", "02", "21", "08", "30", "17", "06", "25"),
  phone       = c("13808123456", "13908217733", "13712345678", "135001234",
                  "13698765432", "13811002233", "1372233445", "13455667788"),
  email       = c("wangzhe@cju.edu.cn", "lixiaoyu@qq.com", " ZHANGWEI@QQ.COM ",
                  "zhaoting@cju.edu.cn", "chenhao@qq.com", "liuqi@cju.edu.cn",
                  "yanglei@qq.com", "zhourui@cju.edu.cn"),
  hobbies     = c("篮球,摄影", "编程", "羽毛球,摄影,编程", "唱歌",
                  "篮球", "摄影,唱歌", "编程,羽毛球", "篮球,唱歌"),
  gpa         = c(3.6, 3.2, 3.8, 2.9, 3.4, 3.1, 2.7, 3.5)
)

roster
      student_id surname given birth_year birth_month birth_day       phone
1   2024-0512-01      王    哲       2005          03        14 13808123456
2   2024-0512-02      李  小雨       2004          11        02 13908217733
3   2024-0615-01      张    伟       2005          07        21 13712345678
4 2024-0615-02-E      赵    婷       2005          01        08   135001234
5   2024-0512-03      陈    浩       2004          09        30 13698765432
6   2024-0615-03      刘    琪       2005          05        17 13811002233
7   2024-0512-04      杨    磊       2005          12        06  1372233445
8   2024-0615-04      周    睿       2004          02        25 13455667788
                email          hobbies gpa
1  wangzhe@cju.edu.cn        篮球,摄影 3.6
2     lixiaoyu@qq.com             编程 3.2
3    ZHANGWEI@QQ.COM  羽毛球,摄影,编程 3.8
4 zhaoting@cju.edu.cn             唱歌 2.9
5      chenhao@qq.com             篮球 3.4
6    liuqi@cju.edu.cn        摄影,唱歌 3.1
7      yanglei@qq.com      编程,羽毛球 2.7
8  zhourui@cju.edu.cn        篮球,唱歌 3.5

2.2 班主任提的四件事,每件都被字段格式卡住

想做的事 卡在哪 今天要学的动作
① 把姓名和生日整理成一列 姓和名分两列,出生日期分三列 合:unite()
② 按专业统计人数 学号 2024-0512-01 把三段信息挤在一格 拆:separate()
③ 找出爱好含“摄影”的同学 名字里藏着空格,合并后对不上 去空格与找:str_replace_all()、str_detect()
④ 给每人写一句提醒话术 手工拼字符串,拼一句错一句 说:glue()

提示

前三讲处理的是数值脏数据,今天处理文本脏数据。它更隐蔽:“陈 浩” 和 “陈浩” 都是合法字符串、都不是 NA,但只要一匹配、一分组,它们就是两个人。

三、合——unite() 把两列并成一列

姓 + 名 → 姓名;年 + 月 + 日 → 出生日期

3.1 unite():把几列并成一列

▶️ 查看代码
library(tidyr)
roster %>%
  unite(name, surname, given, sep = "")
      student_id   name birth_year birth_month birth_day       phone
1   2024-0512-01   王哲       2005          03        14 13808123456
2   2024-0512-02 李小雨       2004          11        02 13908217733
3   2024-0615-01   张伟       2005          07        21 13712345678
4 2024-0615-02-E   赵婷       2005          01        08   135001234
5   2024-0512-03  陈 浩       2004          09        30 13698765432
6   2024-0615-03   刘琪       2005          05        17 13811002233
7   2024-0512-04   杨磊       2005          12        06  1372233445
8   2024-0615-04   周睿       2004          02        25 13455667788
                email          hobbies gpa
1  wangzhe@cju.edu.cn        篮球,摄影 3.6
2     lixiaoyu@qq.com             编程 3.2
3    ZHANGWEI@QQ.COM  羽毛球,摄影,编程 3.8
4 zhaoting@cju.edu.cn             唱歌 2.9
5      chenhao@qq.com             篮球 3.4
6    liuqi@cju.edu.cn        摄影,唱歌 3.1
7      yanglei@qq.com      编程,羽毛球 2.7
8  zhourui@cju.edu.cn        篮球,唱歌 3.5

提示

unite(新列名, 要合并的列, sep = 中间的连接符)。sep = "" 表示直接连在一起,sep = "-" 则在中间加一个横杠。

3.2 合并出生日期

▶️ 查看代码
roster %>%
  unite(birth_date, birth_year, birth_month, birth_day, sep = "-")
      student_id surname given birth_date       phone               email
1   2024-0512-01      王    哲 2005-03-14 13808123456  wangzhe@cju.edu.cn
2   2024-0512-02      李  小雨 2004-11-02 13908217733     lixiaoyu@qq.com
3   2024-0615-01      张    伟 2005-07-21 13712345678    ZHANGWEI@QQ.COM 
4 2024-0615-02-E      赵    婷 2005-01-08   135001234 zhaoting@cju.edu.cn
5   2024-0512-03      陈    浩 2004-09-30 13698765432      chenhao@qq.com
6   2024-0615-03      刘    琪 2005-05-17 13811002233    liuqi@cju.edu.cn
7   2024-0512-04      杨    磊 2005-12-06  1372233445      yanglei@qq.com
8   2024-0615-04      周    睿 2004-02-25 13455667788  zhourui@cju.edu.cn
           hobbies gpa
1        篮球,摄影 3.6
2             编程 3.2
3 羽毛球,摄影,编程 3.8
4             唱歌 2.9
5             篮球 3.4
6        摄影,唱歌 3.1
7      编程,羽毛球 2.7
8        篮球,唱歌 3.5

警告

注意看第 5 条:given 前面有一个看不见的空格,合并后名字变成 “陈 浩”,中间多了一个空格。这个问题我们留到第五节解决。

四、拆——separate() 把一列拆成几列

拆对了,才能按专业分组统计

4.1 separate():按分隔符拆成多列

▶️ 查看代码
roster %>%
  separate(student_id, 
           into = c("grade", "major", "seq"), 
           sep = "-", 
           remove = FALSE)
      student_id grade major seq surname given birth_year birth_month birth_day
1   2024-0512-01  2024  0512  01      王    哲       2005          03        14
2   2024-0512-02  2024  0512  02      李  小雨       2004          11        02
3   2024-0615-01  2024  0615  01      张    伟       2005          07        21
4 2024-0615-02-E  2024  0615  02      赵    婷       2005          01        08
5   2024-0512-03  2024  0512  03      陈    浩       2004          09        30
6   2024-0615-03  2024  0615  03      刘    琪       2005          05        17
7   2024-0512-04  2024  0512  04      杨    磊       2005          12        06
8   2024-0615-04  2024  0615  04      周    睿       2004          02        25
        phone               email          hobbies gpa
1 13808123456  wangzhe@cju.edu.cn        篮球,摄影 3.6
2 13908217733     lixiaoyu@qq.com             编程 3.2
3 13712345678    ZHANGWEI@QQ.COM  羽毛球,摄影,编程 3.8
4   135001234 zhaoting@cju.edu.cn             唱歌 2.9
5 13698765432      chenhao@qq.com             篮球 3.4
6 13811002233    liuqi@cju.edu.cn        摄影,唱歌 3.1
7  1372233445      yanglei@qq.com      编程,羽毛球 2.7
8 13455667788  zhourui@cju.edu.cn        篮球,唱歌 3.5

提示

separate(要拆的列, into = c(新列名…), sep = 分隔符),into 给几个名字就拆成几列,顺序从左到右。

4.2 拆不干净时,R 只是“警告”你一下

第 4 条学号 2024-0615-02-E 有 4 段,而 into 只给了 3 个名字。上面这段代码跑完,seq 变成 02,交换生后缀 -E 被悄悄扔掉,同时弹出一句警告:

Expected 3 pieces. Additional pieces discarded in 1 rows [4].

警告

这是警告,不是报错,程序照常跑完。所以拆分之后,第一件事就是看有没有这句警告。

加上 extra = "merge",多出来的段会并进最后一列:

▶️ 查看代码
roster %>%
  separate(student_id, into = c("grade", "major", "seq"),
           sep = "-", extra = "merge", remove = FALSE)
      student_id grade major  seq surname given birth_year birth_month
1   2024-0512-01  2024  0512   01      王    哲       2005          03
2   2024-0512-02  2024  0512   02      李  小雨       2004          11
3   2024-0615-01  2024  0615   01      张    伟       2005          07
4 2024-0615-02-E  2024  0615 02-E      赵    婷       2005          01
5   2024-0512-03  2024  0512   03      陈    浩       2004          09
6   2024-0615-03  2024  0615   03      刘    琪       2005          05
7   2024-0512-04  2024  0512   04      杨    磊       2005          12
8   2024-0615-04  2024  0615   04      周    睿       2004          02
  birth_day       phone               email          hobbies gpa
1        14 13808123456  wangzhe@cju.edu.cn        篮球,摄影 3.6
2        02 13908217733     lixiaoyu@qq.com             编程 3.2
3        21 13712345678    ZHANGWEI@QQ.COM  羽毛球,摄影,编程 3.8
4        08   135001234 zhaoting@cju.edu.cn             唱歌 2.9
5        30 13698765432      chenhao@qq.com             篮球 3.4
6        17 13811002233    liuqi@cju.edu.cn        摄影,唱歌 3.1
7        06  1372233445      yanglei@qq.com      编程,羽毛球 2.7
8        25 13455667788  zhourui@cju.edu.cn        篮球,唱歌 3.5

重要

拆出来的 grade、major、seq 都是文字。学号、专业代码这类编码千万不要转成数字,否则前导零会丢掉(0512 变成 512)。

五、去空格——先去掉空格,再使用

把所有空格去掉

5.1 str_replace_all():去掉所有空格

▶️ 查看代码
library(stringr)
str_replace_all("  陈 浩  ", " ", "")     # 首尾和中间的空格全部去掉
[1] "陈浩"

提示

str_replace_all(x, " ", "") 的意思是:把 x 里所有的空格,替换成空字符串,也就是删掉。

警告

姓名、学号这类字段中间的空格没有意义,可以放心去掉。地址、英文名等字段中间的空格可能有意义,不能一律去掉。

5.2 回到合并的问题:合并后去空格

▶️ 查看代码
library(dplyr)
ok <- roster %>%
  unite(name, surname, given, sep = "") %>%
  mutate(name = str_replace_all(name, " ", "")) #合并后去空格
ok
      student_id   name birth_year birth_month birth_day       phone
1   2024-0512-01   王哲       2005          03        14 13808123456
2   2024-0512-02 李小雨       2004          11        02 13908217733
3   2024-0615-01   张伟       2005          07        21 13712345678
4 2024-0615-02-E   赵婷       2005          01        08   135001234
5   2024-0512-03   陈浩       2004          09        30 13698765432
6   2024-0615-03   刘琪       2005          05        17 13811002233
7   2024-0512-04   杨磊       2005          12        06  1372233445
8   2024-0615-04   周睿       2004          02        25 13455667788
                email          hobbies gpa
1  wangzhe@cju.edu.cn        篮球,摄影 3.6
2     lixiaoyu@qq.com             编程 3.2
3    ZHANGWEI@QQ.COM  羽毛球,摄影,编程 3.8
4 zhaoting@cju.edu.cn             唱歌 2.9
5      chenhao@qq.com             篮球 3.4
6    liuqi@cju.edu.cn        摄影,唱歌 3.1
7      yanglei@qq.com      编程,羽毛球 2.7
8  zhourui@cju.edu.cn        篮球,唱歌 3.5

六、查找字符串——filter() 配合 str_detect()

按字符串查找

6.1 str_detect():查找包含某个字符串的行

▶️ 查看代码
ok %>% filter(str_detect(name, "浩"))  # 名字里有“浩”
    student_id name birth_year birth_month birth_day       phone          email
1 2024-0512-03 陈浩       2004          09        30 13698765432 chenhao@qq.com
  hobbies gpa
1    篮球 3.4
▶️ 查看代码
ok %>% filter(str_detect(hobbies, "摄影")) # 爱好里有“摄影”
    student_id name birth_year birth_month birth_day       phone
1 2024-0512-01 王哲       2005          03        14 13808123456
2 2024-0615-01 张伟       2005          07        21 13712345678
3 2024-0615-03 刘琪       2005          05        17 13811002233
               email          hobbies gpa
1 wangzhe@cju.edu.cn        篮球,摄影 3.6
2   ZHANGWEI@QQ.COM  羽毛球,摄影,编程 3.8
3   liuqi@cju.edu.cn        摄影,唱歌 3.1

提示

filter(str_detect(列名, "要找的字符串")):返回这一列里包含该字符串的行。

6.2 用同一个函数找开头与结尾

▶️ 查看代码
ok %>% filter(str_detect(name, "^王")) # 以“王”开头
    student_id name birth_year birth_month birth_day       phone
1 2024-0512-01 王哲       2005          03        14 13808123456
               email   hobbies gpa
1 wangzhe@cju.edu.cn 篮球,摄影 3.6
▶️ 查看代码
ok %>% filter(str_detect(student_id, "E$")) # 以“E”结尾
      student_id name birth_year birth_month birth_day     phone
1 2024-0615-02-E 赵婷       2005          01        08 135001234
                email hobbies gpa
1 zhaoting@cju.edu.cn    唱歌 2.9

提示

^ 表示开头,$ 表示结尾。它们只是 str_detect() 里字符串的写法,不需要单独学习其他函数。

七、输出文本——cat() 与 glue()

把结果变成一句人话

7.1 cat():输出不带引号

▶️ 查看代码
cat("全班共有", nrow(roster), "名同学")
全班共有 8 名同学

提示

cat() 的输出不带引号,参数之间默认用空格连接;不会自动换行,需要换行时,句尾要自己写 "\n"。

7.2 glue():把变量放进 {} 里

▶️ 查看代码
n <- nrow(roster)
glue("全班共有 {n} 名同学。")
全班共有 8 名同学。

提示

glue() 里用 {} 把变量直接写进句子,不用再用逗号和引号拼接。

八、综合与收官

合 → 拆 → 去空格 → 验证

8.1 报名表一条龙整理

▶️ 查看代码
roster_clean <- roster %>%
  unite(name, surname, given, sep = "") %>%                            # 合:姓名
  mutate(name = str_replace_all(name, " ", "")) %>%   # 去空格
  separate(student_id, into = c("grade", "major", "seq"),
           sep = "-", extra = "merge")                                 # 拆:学号

roster_clean
  grade major  seq   name birth_year birth_month birth_day       phone
1  2024  0512   01   王哲       2005          03        14 13808123456
2  2024  0512   02 李小雨       2004          11        02 13908217733
3  2024  0615   01   张伟       2005          07        21 13712345678
4  2024  0615 02-E   赵婷       2005          01        08   135001234
5  2024  0512   03   陈浩       2004          09        30 13698765432
6  2024  0615   03   刘琪       2005          05        17 13811002233
7  2024  0512   04   杨磊       2005          12        06  1372233445
8  2024  0615   04   周睿       2004          02        25 13455667788
                email          hobbies gpa
1  wangzhe@cju.edu.cn        篮球,摄影 3.6
2     lixiaoyu@qq.com             编程 3.2
3    ZHANGWEI@QQ.COM  羽毛球,摄影,编程 3.8
4 zhaoting@cju.edu.cn             唱歌 2.9
5      chenhao@qq.com             篮球 3.4
6    liuqi@cju.edu.cn        摄影,唱歌 3.1
7      yanglei@qq.com      编程,羽毛球 2.7
8  zhourui@cju.edu.cn        篮球,唱歌 3.5
▶️ 查看代码
c(行数 = nrow(roster_clean), 人数 = length(unique(roster_clean$name)))
行数 人数 
   8    8 
▶️ 查看代码
roster_clean %>% count(major)
  major n
1  0512 4
2  0615 4

重要

验收三件事:行数没变(8)、人数算对了(8 个不同的人)、按专业分组能分开(0512 与 0615 各 4 人)。清洗的最后一步是验证,不是代码跑完不报错就算完。

8.2 今天的五个动作

  • 合:unite() 把几列并成一列
  • 拆:separate() 把一列拆成几列,注意 extra = "merge"
  • 去空格:str_replace_all(x, " ", ""),先去空格再合并
  • 找:filter() 配合 str_detect(),包含、开头、结尾都用它
  • 输出:cat() 与 glue() 输出结果

8.3 Exit Ticket —— 写给自己的两个问题

  1. 用 separate() 拆一列时,最容易悄悄丢数据的是哪种情况?怎么防?
  2. 为什么最好先去空格,再合并和分组?

课后作业:实验报告6

数据清洗与文本字段整理

  • 文件命名 学号_姓名_实验报告6.docx提交。

下讲预告

项目四:数据可视化——用图表讲故事

  • 第 15 讲:base R 作图——plot()、hist()、boxplot() 怎么画,怎么加标题和标签
  • 第 16 讲:ggplot2 的语法——数据、映射、图层怎么组合
  • 第 17 讲:直方图、散点图与箱形图各适合什么问题

谢谢!

第14讲:文本字段的合并与拆分

“Clean the text before you trust the count.” 单位名称多一个空格、日期分成三个格子,单看每一条都无所谓,可一旦开始分组汇总、跨表匹配,它们就会让同一个人变成两个人。

朱 奇 | 锦城大学 · 财会学院