r语言 - stringer 中的 str_detect 函数与 grepl 和 grep 有什么区别?



我开始在我的工作中做很多字符串匹配,我很好奇这三个函数之间的区别是什么,以及在什么情况下有人会使用一个。

stringr是"围绕梦幻般的'stringi'软件包的一组一致,简单且易于使用的包装器"(来自软件包描述(。与基本R相比,stringi的主要优点是软件包的速度令人难以置信 -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 添加新列,其中包含有关不同列是否包含模式的信息,我通常会使用greplgrepl使这更容易,因为它与输入变量具有相同的长度:

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而不是 base,我认为有两个论点:不同的语法使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()包裹在图案周围对我来说有点尴尬。其他例子是grepvalue = 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,总体而言,您拥有更多功能。因此,如果您决心进行字符串操作,您可能会立即开始学习该包 - 尽管不可否认的教程较少,并且弄清楚某些事情可能会有点困难。

最新更新