转换文本

许多情况下我们需要把文本转换为其他形式,所幸,对文本进行多种形式的转换并非
难事。
1.转换大小写
当我们处理文本数据时,输入可能不符合我们设定的标准。例如,我们希望所有产品
都用大写字母(从 A~F)进行分级,但实际输入的字母可能既有大写也有小写。转换大小
写有利于确保输入的字符串在大小写上保持一致。
tolower( )函数将文本转换为小写字母,而 toupper( )则相反:
tolower("Hello")
## [1] "hello"
toupper("Hello")
## [1] "HELLO"
这在函数接收字符输入时特别有用。例如,我们可以定义一个函数,无论"add"是
大写还是小写,只要 type 的内容是 add,函数就返回x+y;同理,只要 type 的内容是 times,
函数就返回 x*y。对于这个函数,不管输入值是什么,最好总是将 type 转换成小写:
calc <- function(type, x, y) {
type <- tolower(type)
if (type == "add") {
x + y
}else if (type == "times") {
x * y
} else {
stop("Not supported type of command")
}
}
c(calc("add", 2, 3), calc("Add", 2, 3), calc("TIMES", 2, 3))
## [1] 5 5 6
type 对大小写不敏感,这使得函数对于仅在大小写上有区别的相似输入有更高的容忍度。
另外,进行大小写转换的这两个函数是向量化操作的,它们可以改变给定字符向量中
每个字符串元素的大小写:
toupper(c("Hello", "world"))
## [1] "HELLO" "WORLD"
2.字符计数
另一个很有用的函数是 nchar( ),这个函数可以简单地计算一个字符向量中每个元
素的字符数量:
nchar("Hello")
## [1] 5
同 toupper( )和 tolower( )一样,nchar( )也是向量化的:
nchar(c("Hello", "R", "User"))
## [1] 5 1 4
这个函数经常用于检查一个输入参数是不是一个有效的字符串。例如,下面这个函数
整理了一个学生的部分个人信息并存储到数据库中:
store_student <- function(name, age) {
stopifnot(length(name) == 1, nchar(name) >= 2,
is.numeric(age), age >0)
# 将信息存储到数据库中
}
在将信息存储到数据库中之前,这个函数使用 stopifnot( )检查 name 和 age 的
值是否有效。如果用户没有提供有意义的姓名(例如,不少于两个字母),这个函数就会停
止运行并输出错误信息:
store_ _student("James", 20)
store_ _student("P", 23)
## Error: nchar(name) >= 2 is not TRUE
注意到 nchar(x) == 0 和 x == ""是等价的,两种方法都可以用来检测空字符串。
3.消除首末空白
前面的例子中,我们使用 nchar( )检查 name 是不是有效的。然而,有时候,输入
的数据包含一些无用的空白。这向数据中加入了更多的噪声,需要仔细地检查字符串参数。
例如,上一节中的 store_student( )会通过像"P"这样的名字,而认定"P"是无效参数,
因为 nchar(" P")返回 2:
store_ _student(" P", 23)
为了将这种可能情况考虑进去,我们需要改进 store_student 函数。R 3.2.0 版本中
加入了 trimws( )函数来消除给定字符串开头和结尾的空白:
store_student2 <- function(name, age) {
stopifnot(length(name) == 1, nchar(trimws(name)) >= 2,
is.numeric(age), age >0)
# 将信息存储到数据库中
}
现在,这个函数对噪声数据更加稳健了:
store_ _student2(" P", 23)
## Error: nchar(trimws(name)) >= 2 is not TRUE
这个函数默认消除开头和结尾的空白,包括空格和制表符。你可以指定"left"或
"right"以消除字符串单侧的空白:
trimws(c(" Hello", "World "), which ="left")
## [1] "Hello" "World "
4.子字符串
在前面的章节中,我们学习了如何对向量和列表构建子集。这里,也可以通过调用
substr( )对字符向量中的文本构建子集。假设有许多如下形式的日期:
dates <- c("Jan 3", "Feb 10", "Nov 15")
所有的月份都被缩写为 3 个字母(对应每个字符串的起止元素:第 1~3 个)。使用
substr( )提取月份:
substr(dates, 1, 3)
## [1] "Jan" "Feb" "Nov"
为了提取日期(对应每个字符串的起止元素:第 5 个到最后),我们需要组合使用
substr( )和 nchar( ):
substr(dates, 5, nchar(dates))
## [1] "3" "10" "15"
现在,我们可以提取输入字符串中的月份和日期了,那么写一个函数来将这种格式的字符
串转换为表示相同日期的数值就很有用。接下来的函数使用了许多我们学习过的函数和想法:
get_month_day <- function(x) {
months <- vapply(substr(tolower(x), 1, 3), function(md) {
switch(md, jan = 1, feb = 2, mar = 3, apr = 4, may = 5,
jun = 6, jul = 7, aug = 8, sep = 9, oct = 10, nov = 11, dec = 12)
}, numeric(1), USE.NAMES = FALSE)
days <- as.numeric(substr(x, 5, nchar(x)))
data.frame(month = months, day = days)
}
get_ _month_ _day(dates)
## month day
## 1 1 3
## 2 2 10
## 3 11 15
substr( )函数也可以用来替换一个给定的字符向量中的字符串子集:
substr(dates, 1, 3) <- c("Feb", "Dec", "Mar") dates
## [1] "Feb 3" "Dec 10" "Mar 15"
5.切分文本
许多情况下,需要提取的字符串的长度是不固定的。像“Mary Johnson”或“Jack Smiths”
这样的人名,姓和名的长度就是不固定的。这使得上一节中学习的函数 substr( )难以
分离和提取这两个部分。这种格式的文本具有一个规则的分隔符,例如空格或逗号。为了
提取有用的部分,我们需要将文本切分,并使各部分都可访问。strsplit( )函数可以通
过指定分隔符将字符向量中的文本切分开:
strsplit("a,bb,ccc", split = ",")
## [[1]]
## [1] "a" "bb" "ccc"
这个函数返回了一个列表。将一个初始字符串进行切分,切分后的元素构成一个字符
向量,这个字符向量构成返回列表的一个成分。因此,有几个初始字符串,返回的列表中
就有几个对应的成分。与我们前面介绍的字符串函数一样,strplit( )也是向量化的,
即它返回一个由字符向量构成的列表作为切分的结果:
students <- strsplit(c("Tony, 26, Physics", "James, 25, Economics"),
split = ", ")
students
## [[1]]
## [1] "Tony" "26" "Physics"
##
## [[2]]
## [1] "James" "25" "Economics"
strsplit( )函数通过这种基于元素的切分方式,返回一个由字符向量构成的列表。
实际上,切分只是提取或重组数据的第 1 步。接下来,我们使用 rbind( )将数据放进矩
阵中,并为矩阵赋予合适的列名:
students_matrix <- do.call(rbind, students)
colnames(students_matrix) <- c("name", "age", "major")
students_matrix
## name age major
## [1,] "Tony" "26" "Physics"
## [2,] "James" "25" "Economics"
然后将这个矩阵转换为数据框,以便将每一列转换为更合适的类型:
students_df <- data.frame(students_matrix, stringsAsFactors = FALSE)
students_df$age <- as.numeric(students_df$age)
students_df
## name age major
## 1 Tony 26 Physics
## 2 James 25 Economics
现在,原始的字符串数据 students 已经被转换成组织良好且便于访问的数据框
students_df。
这里有一个小技巧,可以将一整个字符串切分成单个字母,就是设定参数 split 为空:
strsplit(c("hello", "world"), split = "")
## [[1]]
## [1] "h" "e" "l" "l" "o"
##
## [[2]]
## [1] "w" "o" "r" "l" "d"
事实上,strsplit( )的强大尚未充分展示。它还支持正则表达式,一个非常强大的
处理文本数据的框架。我们将会在本章的最后一节讨论这个主题。

posted @ 2019-01-22 13:53  NAVYSUMMER  阅读(308)  评论(0编辑  收藏  举报
交流群 编程书籍