TL;DR:grepl期望它的第一个参数是一个字符串(长度为 1),而不是一个向量。sapply您可以使用and的组合来解决这个问题lapply(见下文),但最好使用一个正则表达式来捕获您想要匹配的内容,df1.MATCH而根本不使用它df2.PATTERN。对于大型数据集,第二个选项要快得多(如果不太智能)。对于这类工作,值得学习如何充分利用正则表达式。
df1 %>% filter(grepl(pattern = "^((ABC)( )*)+$", x = df1.MATCH, ignore.case = TRUE))
解释
的文档grepl显示了以下用法:
grepl(pattern, x, ignore.case = FALSE, perl = FALSE,
fixed = FALSE, useBytes = FALSE)
参数是第pattern一个,这个参数应该是一个字符串(一个元素)。您正在提供df1.MATCH这个参数,它是一个向量。
我们可以使用sapply应用于grepl的每个元素df1.MATCH。
sapply(df1.MATCH, grepl, x = df2.PATTERN)
ABC abc BCD
[1,] TRUE FALSE FALSE
[2,] FALSE TRUE FALSE
[3,] TRUE TRUE FALSE
但是,看看输出!您可能不想要矩阵。当我们grepl只运行你的第一个元素时会发生df1.MATCH什么?
grepl("ABC",df2.PATTERN)
[1] TRUE FALSE TRUE
我们得到一个向量,因为grepl它正在检查ABC的每个元素df2.PATTERN。要获得一个有用的过滤逻辑向量,您需要返回一个与 相同长度的逻辑向量df1.MATCH。我看到了两种方法。
方法一:使用any
由于您想知道 中的哪些元素与 中的df1.MATCH任何元素匹配df2.PATTERN,因此可以使用,如果其参数中的任何元素为any,则返回。我们需要一些不同的语法来完成这项工作。我们需要 wrap in来制作一个包含三个向量的列表(每个元素一个),这些向量会输入到Wrapped中。如果我们只使用,将只返回一个值,因为我们有一个矩阵输入。TRUETRUEgrepllapplydf1.MATCH1sapplyanysapplyany
any(grepl("ABC", df2.PATTERN))
[1] TRUE
sapply(
lapply(df1.MATCH, grepl, x = df2.MATCH),
any)
[1] TRUE TRUE FALSE
方法二:写一个更好的正则表达式。
您希望将 的内容与df1.MATCH看起来像abc、ABC、ABC ABC或ABC abc等的可能值进行匹配。您可以将所有这些包含在单个正则表达式字符串中。你想要的字符串是
"^((ABC)( )*)+$"
^ # Nothing else before this
(ABC) # Must contain ABC together as a group
( )* # followed by any number of spaces (including 0)
((ABC)( )*)+ # Look for the ABC (space) pattern repeated one or more times
$ # And nothing else after it
然后grepl使用ignore.case = TRUE:
grepl("^((ABC)( )*)+$", df1.MATCH, ignore.case = TRUE)
[1] TRUE TRUE FALSE
基准测试
在大型数据集中,其中一个将执行得更快。让我们来了解一下。您的基准测试结果会因您机器的资源而异。
df1.MATCH <- sample(c("ABC", "abc" ,"BCD"), size = 100000, replace = TRUE)
df1 <- data.frame(df1.MATCH)
df2.PATTERN <- c("ABC", "abc", "ABC abc")
library(rbenchmark)
benchmark("any lapply" = {
df1 %>%
filter(sapply(lapply(df1.MATCH, grepl, x=df2.PATTERN), any) )
},
"better regex" = {
df1 %>%
filter(grepl("^((ABC)( )*)+$", df1.MATCH, ignore.case = TRUE))
}
)
test replications elapsed relative user.self sys.self user.child sys.child
1 any lapply 100 149.13 70.678 147.67 0.39 NA NA
2 better regex 100 2.11 1.000 2.10 0.02 NA NA
看起来改进的正则表达式方法明显更快。grepl这是因为它在过滤之前每行 ( ) 只执行一个操作。另一种方法是每行执行四次操作:lapply执行grepl三次(对 的每个元素执行一次df2.PATTERN,sapply然后any对每个列表元素执行一次(每一行)。