【问题标题】:Use dynamic (variable) string as regex pattern in R在 R 中使用动态(变量)字符串作为正则表达式模式
【发布时间】:2018-05-24 04:37:01
【问题描述】:

我有一个data.table DT_words(大维度):

DT_words <- data.table(word = c('word1', 'word2', 'word3','word4'))

我有另一个 data.table DT_strings 包含一个包含大量字符串的列

DT_strings <- data.table(string = c('string1 made of word1', 'string2 made of word2 and word2 and word3 and word1ly', 'string3 made of word1 and word2'))

对于 DT_word 中的每个单词,我想计算 DT_string 中所有字符串的出现总数,并将该值保存为 DT_word 中的列。 我正在使用 for 循环,它看起来很丑。

我尝试使用 lapply 和 mapply 但无济于事,因为该函数需要内部输入。

这里是 for-loop that words(但它需要很长时间而且很丑)

require(stringr)

for (i in 1:nrow(DT_words))
{
   DT_words$word_count[i] <-  sum(str_count(DT_strings$string, 
                                  paste0(c("\\b("),paste(DT_words[i, .(word)]),c(")\\b"))))
}

我知道格式更像是 data.frame,但由于我使用的是循环,所以这并不重要,不是吗? 无论如何,我想知道我是否可以在 data.table 中使用 apply 并摆脱这种丑陋。

期望的输出是:

> DT_words
    word word_count
1: word1          2
2: word2          3
3: word3          1
4: word4          0

编辑:我编辑了 DT_strings 以包含更多单词匹配的示例。我只对匹配整个单词感兴趣,因此必须以某种方式包含正则表达式语法。

【问题讨论】:

    标签: r regex dynamic nested data.table


    【解决方案1】:

    如果您的单词确实只是用空格分隔,我会将它们拆分为列,转换为长格式,然后运行与by = .EACHI 结合的二进制连接,例如,使用您的数据:

    library(data.table)
    library(magrittr)                       
    DT_strings[, tstrsplit(string, " ", fixed = TRUE)] %>% 
      melt(., measure.vars = names(.), na.rm = TRUE) %>%
      .[DT_words, on = .(value = word), .N, by = .EACHI]
    #    value N
    # 1: word1 2
    # 2: word2 3
    # 3: word3 1
    # 4: word4 0
    

    附言

    我使用fixed = TRUE 来提高速度,因为我假设每个单词之间总是有一个空格。如果空格数不同,您需要使用 tstrsplit(string, "\\s+") 代替,这可能会更慢。

    【讨论】:

    • 哇。这样做真是太好了。谢谢!
    【解决方案2】:

    假设您所说的字符串和单词来自自然语言,我建议使用以下可能运行得更快的基本 R 解决方案。重点是您必须分隔字符串中的不同单词,但很容易将“strsplit”调整为其他分隔符。

    s <- c('string1 made of word1', 'string2 made of word2 and word2 and word3', 'string3 made of word1 and word2')
    w <- c('word1', 'word2', 'word3','word4')
    
    z <- as.data.frame(table(unlist(strsplit(s,' '))))
    z[z$Var1 %in% w,]
    
    #   Var1 Freq
    #7 word1    2
    #8 word2    3
    #9 word3    1
    

    【讨论】:

      【解决方案3】:

      这是使用 tidyverse 软件包套件的解决方案。

      library(stringr)
      library(purrr)
      
      DT_words$word_count <- map_int(paste0("\\b", DT_words$word,"\\b"),
         ~ str_count(DT_strings$string, .x) %>% sum)
      

      不使用purrr的替代方案:

      DT_words$word_count <- vapply(paste0("\\b", DT_words$word, "\\b"), function(x) {
        sum(str_count(DT_strings$string, x))
      }, 0)
      

      【讨论】:

      • as.character 的用途是什么
      • 如果您正在转换 DT_words,您应该在答案中包含它。 FWIW 您可能不需要进行此转换。正如 OP 特别提到的 DT_words 尺寸很大,效率可能对他的情况很重要:)
      • 是的,确实.... 将有超过 20,000 个 DT_words 和近 500,000 个 DT_strings 条目。我希望能找到一个 data.table + apply 解决方案
      • 诚然,我不知道 data.table - 但apply(可能还有map - 就此而言)被认为是“隐藏循环”而不是“避免循环”。我认为没有办法避免 R 中的循环 - 您可以使用 vapply 而不是 map_int 获得相同的基本效果 - 这可能会更快。
      • @MelissaKey 您的解决方案不起作用,因为它还将单词计为另一个单词的一部分。试试 DT_strings
      猜你喜欢
      • 2022-01-22
      • 2013-07-26
      • 1970-01-01
      • 2014-08-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多