stringr包——使用stringr处理字符串

10.1 简介

stringr包中的函数名称更加直观,并且都是str_开头

library(stringr)

10.2 字符串基础

注意,字符串的打印形式与其本身的内容不是相同的,因为打印形式中会显示出转义字符。
如果想要查看字符串的初始内容,可以使用writelines()函数:

> x <- c("\"","\\")
> x
[1] "\"" "\\"
> writeLines(x)
"
\

还有些特殊字符。
以及所有平台都有效的非英文字符写法"\u00b5"

> x <- "\u00b5"
> x
[1] "µ"

10.2.1 字符串长度

> str_length(c("a","R for data science", NA))
[1]  1 18 NA

10.2.2 字符串组合

# 组合两个或多个的字符串
> str_c("x","y","z")
[1] "xyz"

# 使用sep参数来控制字符串间的分隔方式
> str_c("x","y",sep = ",")
[1] "x,y"

# 使用collapse参数将字符串向量进行向量化组合后将每一项组合在一起
> str_c("a",letters, collapse = ", ")
[1] "aa, ab, ac, ad, ae, af, ag, ah, ai, aj, ak, al, am, an, ao, ap, aq, ar, as, at, au, av, aw, ax, ay, az"

# 缺失值可传染的。如果想要将它们输出为“NA”,可以使用str_replace_na()
> x <- c("abc", NA)
> str_c("|-", x, "-|")
[1] "|-abc-|" NA       
> str_c("|-", str_replace_na(x), "-|")
[1] "|-abc-|" "|-NA-|"

# str_c()函数是向量化
> str_c("prefix-", c("a", "b", "c"), "-suffix")
[1] "prefix-a-suffix" "prefix-b-suffix" "prefix-c-suffix"

# 长度为0的对象会 被无声无息地丢弃。这与if结合起来特别游泳:
> name <- "Hadley"
> birthday <- FALSE
> time_of_day <- "morning"
> str_c(
+   "Good ", time_of_day, " ", name,
+   if (birthday) " and HAPPY BIRTHDAY",
+   "."
+ )
[1] "Good morning Hadley."

10.2.3 字符串取子集

可以使用str_sub()函数来提取字符串的一部分。还包括start和end参数,它们给出字符串的位置(包括start和end在内)

> x <- c("Apple", "Banana", "Pear")
> str_sub(x, 1, 3)
[1] "App" "Ban" "Pea"

# 复数表示从后往前数
> str_sub(x, -3, -1)
[1] "ple" "ana" "ear"

# 字符串过短,str_sub()函数也不会出错
> str_sub("a", 1, 5)
[1] "a"

还可以使用str_sub()函数的赋值形式来修改字符串
> str_sub(x, 1, 1) <- str_to_lower(str_sub(x, 1, 1))
> x
[1] "apple"  "banana" "pear" 

10.2.4 区域设置

文本进行大小写转换函数有str_to_lower()、str_to_upper()和str_to_title(),但是,大小写转换更复杂,因为不同的语言转换规则不同,可以通过locale设置选择使用哪些规则。如果没有设置会使用操作系统提供的当前区域设置。

#区域设置可以参考ISO 639语言编码标准
# 土耳其语中的转换
> str_to_upper(c("i"))
[1] "I"
> str_to_upper(c("i"),locale = "tr")
[1] "İ"

受区域影响的另一种重要操作是排序。R基础包中的order()和sort()函数

> x <- c("apple", "eggplant", "banana")
> str_sort(x, locale = "en") #英语
[1] "apple"    "banana"   "eggplant"
> str_sort(x, locale = "haw") #夏威夷语
[1] "apple"    "eggplant" "banana"

10.2.5 练习

(1) 在没有使用 stringr 的那些代码中,你会经常看到 paste() 和 paste0() 函数,这两个函
数的区别是什么? stringr 中的哪两个函数与它们是对应的?这些函数处理 NA 的方式有
什么不同?

paste("foo", "bar")
#> [1] "foo bar"
paste0("foo", "bar")
#> [1] "foobar"

str_c("foo", "bar")
#> [1] "foobar"
str_c("foo", NA)
#> [1] NA
paste("foo", NA)
#> [1] "foo NA"
paste0("foo", NA)
#> [1] "fooNA"

(3) 使用 str_length() 和 str_sub() 函数提取出一个字符串最中间的字符。如果字符串中的
字符数是偶数,你应该怎么做?
扩展
• floor:向下取整,即不大于该数字的最大整数
• ceiling:向上取整,即不小于该数字的最小整数
• trunc:取整数部分
• round:保留几位小数
• signif:保留几位有效数字,常用于科学技术
参考:https://www.jianshu.com/p/c808c8cdf504

#第一种
x <- c("a", "abc", "abcd", "abcde", "abcdef")
L <- str_length(x)
m <- ceiling(L / 2)
str_sub(x, m, m)
#> [1] "a" "b" "b" "c" "c"

#第二种
median(seq_len(str_length(test)))

(4) str_wrap() 函数的功能是什么?应该在何时使用这个函数?
可以看到 str_wrap() 函数就是指定插入字符的位置,比如 width = 10,将会每十个字符(包括空格)加一个换行符 \n。
• width:每一行的宽度,正整数
• indent:每个段落第一行的非负整数缩进,即首行缩进
• exdent:非负整数,在每个段落中给下行作缩进,非首行缩进

> ?str_wrap
> test <- "R would not be what it is today without the invaluable help of these\npeople outside of the R core team"
> cat(str_wrap(test, width = 30), "\n")
R would not be what it is
today without the invaluable
help of these people outside
of the R core team 
> cat(str_wrap(test, width = 30, indent = 2), "\n")
  R would not be what it is
today without the invaluable
help of these people outside
of the R core team 
> cat(str_wrap(test, width = 30, exdent = 2), "\n")
R would not be what it is
  today without the invaluable
  help of these people outside
  of the R core team

用处:当我们在做 GO、KEGG 等情况下,文本内容过长,需要换行展示,这时候就可以配合 str_wrap() 函数。详情可参考 Y 叔的公众号推文:ggplot2画图,文本太长了怎么办?

p + 
  scale_x_discrete(labels=function(x) str_wrap(x, width=10))

5 str_trim() 函数的功能是什么?其逆操作是哪个函数?
用来去除空白的函数:
• str_trim() removes whitespace from start and end of string; 移除首尾
• str_squish() also reduces repeated whitespace inside a string;中间的也移除
都有一参数 side:删除空白的边(left, right or both)

?str_trim()

逆操作函数为 str_pad() 加上空白
• width:加上字符后的字符长度,当指定长度小于本身字符长度时候不会改变
• side:加在哪边
• pad:填充字符,默认为空白

> str_pad("a", 10, pad = c("-", "_", " "))
[1] "---------a" "_________a" "         a"
> str_length(str_pad("hadley", 30, "left"))
[1] 30
当指定长度小于本身字符长度时候不会改变
> str_pad("hadley", 3)
[1] "hadley"

参考:https://www.jianshu.com/p/c808c8cdf504
6 编写一个函数将字符向量转换为字符串,例如,将字符向量 c("a", "b", "c") 转换为字符串 "a, b and c"。仔细思考一下,如果给定一个长度为 0、1 或 2 的向量,那么这个函数应该怎么做?

+   n <- length(x)
+   if (n == 0) {
+     NA
+   }else if (n == 1){
+     x
+   }else if(n == 2){
+     str_c(x[[1]], " and ", x[[2]])
+   }else{
+     not_last <- str_c(x[1:n-1], collapse = ", ")
+     str_c(not_last, x[n], sep = " and ")
+   }
+ }
> str_trans(c("a", "b", "c"))
[1] "a, b and c"

10.3 使用正则表达式进行模式匹配 (区分大小写)

10.3.1 基础匹配

> x <- c("apple", "banana", "pear")
> str_view(x, "an")

更复杂的一些模式是使用.,它可以匹配任意字符(除了换行符):

> str_view(x, ".a.")

使用字符串来表示正则表达式,而且\在字符串中也用作转义字符,所以正则表达式\.的字符串形式应是\\.

> str_view(c("abc","a.c","bef"),"a\\.c")

建立\\的正则表达式,字符串写作"\\\\"

> x <- "a\\b"  # 字符串输入与匹配时所用字符串字符相同
> writeLines(x) 
a\b
> str_view(x, "\\\\")

10.3.2 练习

1 解释一下为什么这些字符串不能匹配一个反斜杠 \ :""、""、""。
"":转义符号 \ 后的一个字符
"\":这将解析到正则表达式中的 \,它将转义正则表达式中的下一个字符。
"\":前两个反斜杠将解析为正则表达式中的文字反斜杠,第三个将转义下一个字符。在正则表达式中,它会转义一些转义的字符。

2 如何匹配字符序列 "'\ ?

> writeLines("\"\'\\\\")
"'\\
> str_view("\"'\\", "\"\'\\\\")

3 正则表达式 ...... 会匹配哪种模式?如何用字符串来表示这个正则表达式?

\..\..\.. 表示匹配 .任意字符.任意字符.任意字符,比如 .x.y.z。
> str_view(c(".a.b.c", ".a.b", "....."), c("\\..\\..\\.."), match = TRUE)

10.3.3 锚点

  • ^从字符串开头进行匹配
  • \(从字符串末尾进行匹配 始于权利,终于金钱 匹配^,使用\\^字符串匹配 匹配\),使用\字符串匹配

10.3.4

1 如何匹配字符串 "\(^\)" ?

> str_view(c("$^$", "ab$^$c"), "^\\$\\^\\$$")

2 给定stringr::words 中的常用单词语料库,创建正则表达式以找出满足下列条件的所有单词。
因为这个列表非常长,所以你可以设置 str_view() 函数的 match 参数,只显示匹配的
单词(match = TRUE)或未匹配的单词(match = FALSE)。

a. 以 y 开头的单词。
以 y 开头的单词
> test <- stringr::words
> str_view(test, "^y", match = TRUE)

b. 以 x 结尾的单词。
以 x 结尾的单词
> str_view(test, "x$", match = TRUE)
image
c. 长度正好为 3 个字符的单词。(不要使用str_length() 函数,这是作弊!)
长度正好为 3 个字符的单词,太多,截取部分图
> str_view(test, "^...$", match = TRUE)

d. 具有 7 个或更多字符的单词。

具有 7 个或更多字符的单词
> str_view(test, ".......", match = TRUE)
或者
> reg1 <- str_c(rep(".", 7), collapse = "")
> str_view(test, reg1, match = TRUE)
或者
{7} 表示重复七次
> str_view(test, ".{7}", match = TRUE)
{7,} 表示重复七次及其以上
> str_view(test, ".{7,}", match = TRUE)
{7,9} 表示重复七次到九次

10.3.5 字符串与字符选项

  • \d可以匹配任意数字
  • \s可以匹配任意空白字符(如空格、制表符和换行符)
  • [abc]可以匹配a、b、c
  • [^abc]可以匹配除a、b、c外的任意字符
  • 字符选项创建多个可选模式。 |的优先级最低
    abc|xyz匹配的是abc或xyz
    ab(c|x)yz匹配的是abcyz或abxyz ( 和 |不用转义

10.3.6 练习

10.3.7 重复

  • ?:0次或1次
  • +:1次或多次
  • *:0次或多次

精确设置匹配次数

  • {n}:匹配n次
  • {n,}:匹配n次或更多次
  • {,m}:最多匹配m次
  • {n, m}:匹配到m次

10.3.8 练习

^.*$ 匹配所有字符

10.3.9 分组与回溯引用

括号可以定义分组,可以的通过\1和\2等回溯引用

str_view("abcbcddd","(bc)\\1")

10.4 工具

10.4.1匹配检测

str_detect()

> x <- c("apple", "banana", "pear")
> str_detect(x, "e")
[1]  TRUE FALSE  TRUE

与mean()和sum()联用有很大用处

> # 有多少个以t开头常用的单词
> sum(str_detect(words, "^t"))
[1] 65
> #以元音字母结尾的常用单词的比例是多少
> mean(str_detect(words, "[aeiou]$"))
[1] 0.2765306

找出不含元音字母所有单词的两种方法
identical判断两者是否相同

> #找出至少包含一个元音字母的所有单词,然后取反
> no_vowels_1 <- !str_detect(words, "[aeiou]")
> #找出仅包含辅音字母的所有单词
> no_vowels_2 <- str_detect(words, "^[^aeiou]+$")
> #判断两者是否相同
> identical(no_vowels_1,no_vowels_2)
[1] TRUE

选区匹配元素

> words[str_detect(words, "x$")]
[1] "box" "sex" "six" "tax"
> str_subset(words, "x$")
[1] "box" "sex" "six" "tax"

结合filter处理数据框
seq_along根据元素次序依次生成1,2,3,4……

> df <- tibble(word = words,
+              i = seq_along(words))
> df
# A tibble: 980 x 2
   word         i
   <chr>    <int>
 1 a            1
 2 able         2
 3 about        3
 4 absolute     4
 5 accept       5
 6 account      6
 7 achieve      7
 8 across       8
 9 act          9
10 active      10
# … with 970 more rows
> df %>%
+ filter(str_detect(words, "x$"))
# A tibble: 4 x 2
  word      i
  <chr> <int>
1 box     108
2 sex     747
3 six     772
4 tax     841

str_detect()是str_count()的变体,后者不是简单的返回是或否,而是返回字符串中匹配的数量。
注意匹配不会重叠

> x <- c("apple", "banana", "pear")
> str_count(x, "a")
[1] 1 3 1
> #平均来看每个单词中有多少个元音字母
> mean(str_count(words, "[aeiou]"))
[1] 1.991837

> #同mutate()函数一同使用
> df %>%
+   mutate(
+     vowels = str_count(words, "[aeiou]"),
+     consonants = str_count(words, "[^aeiou]")
+   )
# A tibble: 980 x 4
   word         i vowels consonants
   <chr>    <int>  <int>      <int>
 1 a            1      1          0
 2 able         2      2          2
 3 about        3      3          2
 4 absolute     4      4          4
 5 accept       5      2          4

stringr中的许多函数都是成对出现的

  • 一个用于单个匹配
  • 一个用于全部匹配 _all()
> str_view("abababa", "aba")

> str_view_all("abababa", "aba")

10.4.3 练习

#找出以x开头或结尾的所有单词
str_subset(words, "^x|x$")
#找出以元音字母开头并以辅音字母结尾的单词
str_subset(words, "^[aeiou].*[^aeiou]$")
words[str_detect(words, "a") &
        str_detect(words, "e") &
        str_detect(words, "i") &
        str_detect(words, "o") &
        str_detect(words, "u")]
#哪个单词包括最多的元音字母?哪个单词包含最大比例的元音字母
d_num <- str_count(words, "[aeiou]")
max_num <- max(d_num)
words[which(d_num == max_num)]
max_num

ratio <- str_count(words, "[aeiou]") / str_length(words)
words[ratio == max(ratio)]

10.4.3 提取匹配内容

找出包含一种颜色的句子
str_extract()只提取第一个匹配

colors <- c("red", "orange", "green", "blue", "purple")
color_match <- str_c(colors, collapse = "|")
has_color <- str_subset(sentences, color_match)
matches <- str_extract(has_color, color_match)
> head(matches)
[1] "blue" "blue" "red"  "red"  "red"  "blue"

可以先提取出多于一种的匹配
str_extract()返回一个列表
simpify = TRUE参数返回矩阵

> more <- sentences[str_count(sentences, color_match) > 1]
> str_extract(more, color_match)
[1] "blue"   "green"  "orange"
> str_extract_all(more, color_match)
[[1]]
[1] "blue" "red" 

[[2]]
[1] "green" "red"  

[[3]]
[1] "orange" "red"

> str_extract_all(more, color_match, simplify = T)
     [,1]     [,2] 
[1,] "blue"   "red"
[2,] "green"  "red"
[3,] "orange" "red"

> x <- c("a", "ab", "abc")
> str_extract_all(x,"[a-z]", simplify = T)
     [,1] [,2] [,3]
[1,] "a"  ""   ""  
[2,] "a"  "b"  ""  
[3,] "a"  "b"  "c" 
posted @ 2020-05-17 21:03  fhn  阅读(661)  评论(0)    收藏  举报