R Studio - grepl 将数据框中的列与模式列表进行比较

数据挖掘 r 正则表达式
2022-03-04 16:49:23

我在数据框中有一个名为“MATCH”的列和一个名为“PATTERN”的模式列表。

df1.MATCH <- c("ABC", "abc" ,"BCD")
df1 <- as.data.frame(df1.MATCH)
df2.PATTERN <- c("ABC", "abc", "ABC abc")

我想使用 grepl 将 MATCH 列与 PATTERN 进行比较,如果为真,我将应用我的函数。期望的结果是“ABC”匹配“ABC”和“ABC abc”。这是我使用的代码:

df1 %>% filter(grepl(df1.MATCH,df2.PATTERN ))%>% ...

我得到错误:

"Warning message: In grepl(TXN_GROUP, parm[3]) :argument 'pattern' has length > 1 and only the first element will be used"

我知道我不能将 grepl 用于向量列表。有什么办法可以解决吗?

1个回答

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看起来像abcABCABC ABCABC 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.PATTERNsapply然后any对每个列表元素执行一次(每一行)。