使用分组提取数据
在字符串模式中,我们使用括号对想要从文本中提取的部分做标记。在这个问题中,
我们修改模式为:(\w+):\s(\d+),其中有两组被标记:一个是通过"\w+"匹配的水果
名,另一个是通过"\d+"匹配的水果数量。
现在我们用改进后的模式来提取想要的信息。尽管 R 的内置函数已经完全能够胜任这
项工作,我仍然强烈推荐使用 stringr 包中的函数。这个包大幅简化了正则表达式的使
用。我们调用 str_match( ),配合分组改进后的匹配模式:
library(stringr)
matches <- str_ _match(fruits, "^(\\w+):\\s(\\d+)$")
matches
## [,1] [,2] [,3]
## [1,] "apple: 20" "aplle" "20"
## [2,] NA NA NA
## [3,] "banana: 30" "banana" "30"
## [4,] NA NA NA
## [5,] "watermelon: 2" "watermelon" "2"
## [6,] "blueberry: 12" "blueberry" "12"
## [7,] NA NA NA
这次匹配的结果是一个多列矩阵。括号组内的信息被从文本中提取出来,并放到第 2 列
和第 3 列。现在我们将这个字符矩阵转换为数据框,并且保证其表头和数据类型准确无误:
# 转换为数据框
fruits_df <- data.frame(na.omit(matches[, -1]), stringsAsFactors = FALSE)
# 添加表头
colnames(fruits_df) <- c("fruit","quantity")
# 将 quantity 的类型从字符转换为整数
fruits_df$quantity <- as.integer(fruits_df$quantity)
现在,fruits_df 是一个具有正确表头和数据类型的数据框:
fruits_df
## fruit quantity
## 1 apple 20
## 2 banana 30
## 3 watermelon 2
## 4 blueberry 12
如果不确定上述代码的中间结果,可以一行一行地运行它们,并逐步查看输出结果。
最后,我们使用正则表达式完美地解决了这个问题。
从前面的例子中,我们可以看到,正则表达式的魔力在于它将表达不同类型字母和符
号的指示器分组使用。除了上述已经提到的元符号,下面这些也很有用。
• [0-9]:这个符号表示 0 到 9 的单个整数。
• [a-z]:这个符号表示从 a 到 z 的单个小写字母。
• [A-Z]:这个符号表示从 A 到 Z 的单个大写字母。
• .:这个符号表示任意单个符号。
• *:这个符号表示一个模式出现零次、一次或者更多次。
• +:这表示一个模式出现一次或者更多次。
• {n}:这表示一个模式重复出现 n 次。
• {m,n}:这表示一个模式出现至少 m 次,至多 n 次。
使用这些元符号,我们可以轻松地检查和筛选字符串数据。举个例子,假设我们有混
合在一起的两个国家的一些电话号码。如果两个国家的电话号码模式不同,正则表达式可
以帮助我们将它们分成两个类别:
telephone <- readLines("data/telephone.txt")
telephone
## [1] "123-23451" "1225-3123" "121-45672" "1332-1231" "1212-3212" "123456789"
注意到数据中存在一个异常:有一个电话号码的数字中间没有-。正常情况下,我们可
以轻松地指出两类电话号码的模式:
telephone[grep("^\\d{3}-\\d{5}$", telephone)]
## [1] "123-23451" "121-45672"
telephone[grep("^\\d{4}-\\d{4}$", telephone)]
## [1] "1225-3123" "1332-1231" "1212-3212"
为了找出异常情况,grepl( )函数更好用,因为它返回一个逻辑向量,指出每个元
176 第 6 章 字符串的使用
素是否匹配模式。因此可以使用这个函数来选出所有不符合给定模式的记录:
telephone[!grepl("^\\d{3}-\\d{5}$", telephone) & !grepl("^\\d{4}-\\d{4}$",
telephone)]
## [1] "123456789"
基本上可以这样认为,上述代码将所有不符合这两个模式的记录都当成了异常。假设
我们有 100 万行记录需要检查,异常情况可能存在于任意格式中。那么,这种方法更加稳
健,即排除掉全部有效记录,找出无效记录。