【问题标题】:grep from different columns in r来自 r 中不同列的 grep
【发布时间】:2018-12-23 05:31:20
【问题描述】:

我有一个非常简单的问题,但我不知道如何得到想要的结果。

我有一个包含多个列的 data.frame,我想在其中的四个列中 grep 一个值以获取 data.frame 的子集。

这是一个虚拟示例

>df1
V1  V2           V3           V4           V5
 a  abc|ccc|ggg  ttt|ccc|shg  yyy|lmn|trs  abc|ggt|hgy
 b  atc|cjc|ggg  ttt|ccc|shg  abc|lmn|trs  abc|opq|sss
 c  auc|chc|ggg  abc|ccc|shg  gtc|lmn|trs  hyt|lki|ddd
 d  aoc|cfc|ggg  ttt|ccc|shg  yyy|lmn|trs  rmn|wde|tre

我想根据 abc 模式为列 V2,V3,V4,V5 子集 data.frame

我知道对于一栏我可以做到

 df2 <- df[grep('abc`, df$V1),]

但是如何使用多列得到这个结果呢?

>df2
V1  V2           V3           V4           V5
 a  abc|ccc|ggg  ttt|ccc|shg  yyy|lmn|trs  abc|ggt|hgy
 b  atc|cjc|ggg  ttt|ccc|shg  abc|lmn|trs  abc|opq|sss
 c  auc|chc|ggg  abc|ccc|shg  gtc|lmn|trs  hyt|lki|ddd

我不想在这个问题 grep one pattern over multiple columns 中获得额外的列,我想根据模式对 data.frame 进行子集化

谢谢

【问题讨论】:

  • @RonakShah,抱歉,复制和粘贴出错
  • 试试df1 %&gt;% filter_at(vars(V2:V5), any_vars(grepl("abc", .)))
  • @StevenBeaupré,这种方法比发布的解决方案运行得更快。我还考虑首先对我的 data.frame 进行子集化,获取与我的 data.frame 子集上的模式匹配的行索引,然后使用该索引对大 data.frame 进行子集化。虽然我不知道这样会不会更快
  • 如果您真的关心速度,那么尽可能摆脱 grep 可能是有意义的。您是否总是在搜索整个术语?或者您正在搜索部分术语?例如,您是一直在搜索“abc”还是有时只搜索“ab”?此外,您是否收到“abc|ccc|ggg”格式的数据,或者您是否正在执行将其置于该格式的过程?如果您以这种方式获取数据是有意义的,但如果您以不同的格式获取数据,可能会有更快的处理方式。
  • @AdamSampson,我的格式总是这样,还有另外 100 列,但我对 grep 管道中包含的模式感兴趣,有时是第一个术语,但有时不是,我当然想如果我能摆脱 grep,加快这个过程。任何帮助将不胜感激,我的 data.frame 看起来像这样 dropbox.com/s/jfmv6npiiu8n6zv/big_df.txt?dl=0>

标签: r


【解决方案1】:

你可以试试:

df1 %>% filter_at(vars(V2:V5), any_vars(grepl("abc", .)))

如果您想要比grepl() 更快的东西,请使用stringi::stri_detect_fixed()

big_df1 <- bind_rows(replicate(10e5, df1, simplify = FALSE))

mbm <- microbenchmark::microbenchmark(
  grepl = big_df1 %>% 
    filter_at(
      vars(V2:V5), 
      any_vars(grepl("abc", .))),
  stringi = big_df1 %>% 
    filter_at(
      vars(V2:V5), 
      any_vars(stringi::stri_detect_fixed(., "abc"))),
  times = 5L
)

这给出了:

#Unit: milliseconds
#    expr       min        lq      mean    median        uq      max neval
#   grepl 2603.2713 2613.4157 2665.3730 2646.4757 2709.6653 2754.037     5
# stringi  823.3735  832.9813  888.5228  901.2059  911.8805  973.173     5

【讨论】:

    【解决方案2】:

    只需使用sapply(),它适用于grep() 列。这些值必须是未列出和排序的,因此您可以获得行。

    df1[sort(unique(unlist(sapply(df1, function(x) grep('abc', x))))), ]
    
    #   V1          V2          V3          V4          V5
    # 1  a abc|ccc|ggg ttt|ccc|shg yyy|lmn|trs abc|ggt|hgy
    # 2  b atc|cjc|ggg ttt|ccc|shg abc|lmn|trs abc|opq|sss
    # 3  c auc|chc|ggg abc|ccc|shg gtc|lmn|trs hyt|lki|ddd
    

    数据

    df1 <- structure(list(V1 = structure(1:4, .Label = c("a", "b", "c", 
    "d"), class = "factor"), V2 = structure(c(1L, 3L, 4L, 2L), .Label = c("abc|ccc|ggg", 
    "aoc|cfc|ggg", "atc|cjc|ggg", "auc|chc|ggg"), class = "factor"), 
        V3 = structure(c(2L, 2L, 1L, 2L), .Label = c("abc|ccc|shg", 
        "ttt|ccc|shg"), class = "factor"), V4 = structure(c(3L, 1L, 
        2L, 3L), .Label = c("abc|lmn|trs", "gtc|lmn|trs", "yyy|lmn|trs"
        ), class = "factor"), V5 = structure(1:4, .Label = c("abc|ggt|hgy", 
        "abc|opq|sss", "hyt|lki|ddd", "rmn|wde|tre"), class = "factor")), class = "data.frame", row.names = c(NA, 
    -4L))
    

    【讨论】:

      【解决方案3】:

      我们可以使用sapply 循环列,这将为每个元素返回一个逻辑向量,指示模式“abc”是否存在,然后过滤掉至少有一个“abc”的行

      cols <- c("V2", "V3", "V4", "V5")
      df[rowSums(sapply(df[cols], function(x) grepl("abc", x))) > 0, ]
      
      #   V1          V2          V3          V4          V5
      #1   a abc|ccc|ggg ttt|ccc|shg yyy|lmn|trs abc|ggt|hgy
      #2   b atc|cjc|ggg ttt|ccc|shg abc|lmn|trs abc|opq|sss
      #3   c auc|chc|ggg abc|ccc|shg gtc|lmn|trs hyt|lki|ddd
      

      不是真正的data.table 专家,但遵循我们可以做的相同逻辑

      library(data.table)
      dt[rowSums(dt[, lapply(.SD, function(x) grepl("abc", x))]) > 0, ]
      
      
      #   V1          V2          V3          V4          V5
      #1:  a abc|ccc|ggg ttt|ccc|shg yyy|lmn|trs abc|ggt|hgy
      #2:  b atc|cjc|ggg ttt|ccc|shg abc|lmn|trs abc|opq|sss
      #3:  c auc|chc|ggg abc|ccc|shg gtc|lmn|trs hyt|lki|ddd
      

      【讨论】:

      • 谢谢,这个例子可以翻译成data.table格式吗?我的真实数据集非常庞大,子集化的速度至关重要
      • @user2380782 用data.table 解决方案更新了答案。
      【解决方案4】:

      这里有一些方法。

      在第一个中,sapply 通过使用grepl 为指示的模式返回一个逻辑矩阵,每行一行df1。然后使用rowSums 查找哪些行为TRUE。最后我们以此为子集。

      在第二个中,我们将df1 的指定列粘贴在一起,然后运行grepl,最后按其子集。

      第三个和第二个一样,但是使用data.table。

      第四个使用Reduce逐列工作。

      # 1
      df1[ rowSums(sapply(df1[-1], grepl, pattern = "abc")) > 0, ]
      
      # 2
      df1[grepl("abc", do.call("paste", c(df1[-1]))), ]
      
      # 3
      library(data.table)
      dt1 <- as.data.table(df1)
      dt1[grepl("abc", do.call("paste", dt1[, -1]))]
      
      # 4
      df1[Reduce(function(x, y) x | grepl("abc", y), init = FALSE, df1), ]
      

      注意

      可重现形式的输入是:

      Lines <- "V1  V2           V3           V4           V5
       a  abc|ccc|ggg  ttt|ccc|shg  yyy|lmn|trs  abc|ggt|hgy
       b  atc|cjc|ggg  ttt|ccc|shg  abc|lmn|trs  abc|opq|sss
       c  auc|chc|ggg  abc|ccc|shg  gtc|lmn|trs  hyt|lki|ddd
       d  aoc|cfc|ggg  ttt|ccc|shg  yyy|lmn|trs  rmn|wde|tre"
      df1 <- read.table(text = Lines, header = TRUE, as.is = TRUE)
      

      【讨论】:

      • 这个例子可以翻译成data.table格式吗?我的真实数据集非常庞大,子集化的速度至关重要
      • 添加为第三个解决方案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-26
      • 1970-01-01
      • 1970-01-01
      • 2017-07-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多