stringer 中的 str_detect 函数与 grepl 和 grep 有什么区别?
What's the difference between the str_detect function in stringer and grepl and grep?
我开始在我的工作中做很多字符串匹配,我很好奇这三个函数之间的区别是什么,以及在什么情况下有人会使用一个而不是另一个。
stringr
是“一套一致、简单且易于使用的包装器,围绕着奇妙的 'stringi' 包”(from package description)。 stringi
的主要优势是与基础 R
相比,包的速度令人难以置信 - stringr
大部分继承了基础。函数的输出在 base 中与在 stringr 中相同。
我使用 stringi
生成一些随机文本进行演示:
library(stringr)
sample_small <- stringi::stri_rand_lipsum(100)
grep
提供模式在字符向量中的位置,就像它等效的 str_which
一样:
grep("Lorem", sample_small)
#> [1] 1 9 14 32 45 50 65 93 94
str_which(sample_small, "Lorem")
#> [1] 1 9 14 32 45 50 65 93 94
另一方面,grepl
/str_detect
为您提供向量中每个元素的信息,无论它是否包含字符串。
grepl("Lorem", sample_small)
#> [1] TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE
#> [12] FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [23] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE
#> [34] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [45] TRUE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
#> [56] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE
#> [67] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [78] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [89] FALSE FALSE FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE
#> [100] FALSE
str_detect(sample_small, "Lorem")
#> [1] TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE
#> [12] FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [23] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE
#> [34] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [45] TRUE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
#> [56] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE
#> [67] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [78] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [89] FALSE FALSE FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE
#> [100] FALSE
在很多情况下,不同的结果可能会对您产生影响。如果我有兴趣向 data.frame 添加一个新列,其中包含有关不同列是否包含模式的信息,我通常使用 grepl
。 grepl
使这更容易,因为它具有与输入变量相同的长度:
df <- data.frame(sample = sample_small,
stringsAsFactors = FALSE)
df$lorem <- grepl("Lorem", sample_small)
df$ipsum <- grepl("ipsum", sample_small)
这样,一些更复杂的测试是可能的:
which(df$lorem & df$ipsum)
#> [1] 1 5 15 53 71 75
或直接作为 filter
规则:
df %>%
filter(str_detect("Lorem", sample_small) & str_detect("ipsum", sample_small))
现在关于为什么要在基础上使用 stringr
,我认为有两个论点:不同的语法使得使用 stringr
和管道
更容易一些
library(dplyr)
sample_small %>%
str_detect("Lorem")
相比于:
sample_small %>%
grepl("Lorem", .)
并且 stringr
大约比 base 快 5 倍(对于我们正在查看的两个函数):
sample_big <- stringi::stri_rand_lipsum(100000)
bench::mark(
base = grep("Lorem", sample_big),
stringr = str_which(sample_big, "Lorem")
)
#> # A tibble: 2 x 6
#> expression min median `itr/sec` mem_alloc `gc/sec`
#> <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl>
#> 1 base 674ms 674ms 1.48 415KB 0
#> 2 stringr 141ms 142ms 6.99 806KB 0
bench::mark(
base = grepl("Lorem", sample_big),
stringr = str_detect(sample_big, "Lorem")
)
#> # A tibble: 2 x 6
#> expression min median `itr/sec` mem_alloc `gc/sec`
#> <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl>
#> 1 base 679ms 679ms 1.47 391KB 0
#> 2 stringr 146ms 148ms 6.76 391KB 0
当我们查找完全匹配时(默认是查找正则表达式),差异更加显着
bench::mark(
base = grepl("Lorem", sample_big, fixed = TRUE),
stringr = str_detect(sample_big, fixed("Lorem"))
)
#> # A tibble: 2 x 6
#> expression min median `itr/sec` mem_alloc `gc/sec`
#> <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl>
#> 1 base 336ms 338.1ms 2.96 391KB 0
#> 2 stringr 12.4ms 12.6ms 79.1 417KB 0
不过,我认为基函数对它们有一定的魅力,这就是为什么我在快速编写代码时仍然经常使用它们的原因。选项 fixed = TRUE
就是一个例子。在模式周围环绕 fixed()
对我来说感觉有点尴尬。其他示例是 grep
中的 value = TRUE
选项(我让您自己计算),最后是 ignore.case = TRUE
,在 stringr
中看起来有点尴尬:
str_which(sample_small, regex("Lorem", ignore_case = TRUE))
#> [1] 1 5 6 8 9 11 12 14 15 17 22 27 30 32 34 35 42 48 51 53 58 64 69
#> [24] 74 76 80 83 86 89 91 92 94 97
然而,这对我来说很尴尬的原因可能只是因为我在学习 stringr
.
之前使用了一段时间的基础 R
另一点需要考虑的是,使用 stringi
,您总体上拥有更多功能。因此,如果您下定决心要学习字符串操作,您可能会立即开始学习该软件包 - 尽管公认的教程较少,而且弄清楚一些事情可能会有点困难。
我开始在我的工作中做很多字符串匹配,我很好奇这三个函数之间的区别是什么,以及在什么情况下有人会使用一个而不是另一个。
stringr
是“一套一致、简单且易于使用的包装器,围绕着奇妙的 'stringi' 包”(from package description)。 stringi
的主要优势是与基础 R
相比,包的速度令人难以置信 - stringr
大部分继承了基础。函数的输出在 base 中与在 stringr 中相同。
我使用 stringi
生成一些随机文本进行演示:
library(stringr)
sample_small <- stringi::stri_rand_lipsum(100)
grep
提供模式在字符向量中的位置,就像它等效的 str_which
一样:
grep("Lorem", sample_small)
#> [1] 1 9 14 32 45 50 65 93 94
str_which(sample_small, "Lorem")
#> [1] 1 9 14 32 45 50 65 93 94
另一方面,grepl
/str_detect
为您提供向量中每个元素的信息,无论它是否包含字符串。
grepl("Lorem", sample_small)
#> [1] TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE
#> [12] FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [23] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE
#> [34] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [45] TRUE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
#> [56] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE
#> [67] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [78] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [89] FALSE FALSE FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE
#> [100] FALSE
str_detect(sample_small, "Lorem")
#> [1] TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE
#> [12] FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [23] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE
#> [34] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [45] TRUE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
#> [56] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE
#> [67] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [78] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [89] FALSE FALSE FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE
#> [100] FALSE
在很多情况下,不同的结果可能会对您产生影响。如果我有兴趣向 data.frame 添加一个新列,其中包含有关不同列是否包含模式的信息,我通常使用 grepl
。 grepl
使这更容易,因为它具有与输入变量相同的长度:
df <- data.frame(sample = sample_small,
stringsAsFactors = FALSE)
df$lorem <- grepl("Lorem", sample_small)
df$ipsum <- grepl("ipsum", sample_small)
这样,一些更复杂的测试是可能的:
which(df$lorem & df$ipsum)
#> [1] 1 5 15 53 71 75
或直接作为 filter
规则:
df %>%
filter(str_detect("Lorem", sample_small) & str_detect("ipsum", sample_small))
现在关于为什么要在基础上使用 stringr
,我认为有两个论点:不同的语法使得使用 stringr
和管道
library(dplyr)
sample_small %>%
str_detect("Lorem")
相比于:
sample_small %>%
grepl("Lorem", .)
并且 stringr
大约比 base 快 5 倍(对于我们正在查看的两个函数):
sample_big <- stringi::stri_rand_lipsum(100000)
bench::mark(
base = grep("Lorem", sample_big),
stringr = str_which(sample_big, "Lorem")
)
#> # A tibble: 2 x 6
#> expression min median `itr/sec` mem_alloc `gc/sec`
#> <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl>
#> 1 base 674ms 674ms 1.48 415KB 0
#> 2 stringr 141ms 142ms 6.99 806KB 0
bench::mark(
base = grepl("Lorem", sample_big),
stringr = str_detect(sample_big, "Lorem")
)
#> # A tibble: 2 x 6
#> expression min median `itr/sec` mem_alloc `gc/sec`
#> <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl>
#> 1 base 679ms 679ms 1.47 391KB 0
#> 2 stringr 146ms 148ms 6.76 391KB 0
当我们查找完全匹配时(默认是查找正则表达式),差异更加显着
bench::mark(
base = grepl("Lorem", sample_big, fixed = TRUE),
stringr = str_detect(sample_big, fixed("Lorem"))
)
#> # A tibble: 2 x 6
#> expression min median `itr/sec` mem_alloc `gc/sec`
#> <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl>
#> 1 base 336ms 338.1ms 2.96 391KB 0
#> 2 stringr 12.4ms 12.6ms 79.1 417KB 0
不过,我认为基函数对它们有一定的魅力,这就是为什么我在快速编写代码时仍然经常使用它们的原因。选项 fixed = TRUE
就是一个例子。在模式周围环绕 fixed()
对我来说感觉有点尴尬。其他示例是 grep
中的 value = TRUE
选项(我让您自己计算),最后是 ignore.case = TRUE
,在 stringr
中看起来有点尴尬:
str_which(sample_small, regex("Lorem", ignore_case = TRUE))
#> [1] 1 5 6 8 9 11 12 14 15 17 22 27 30 32 34 35 42 48 51 53 58 64 69
#> [24] 74 76 80 83 86 89 91 92 94 97
然而,这对我来说很尴尬的原因可能只是因为我在学习 stringr
.
R
另一点需要考虑的是,使用 stringi
,您总体上拥有更多功能。因此,如果您下定决心要学习字符串操作,您可能会立即开始学习该软件包 - 尽管公认的教程较少,而且弄清楚一些事情可能会有点困难。