【问题标题】:What is the most efficient way to replace a vector's values in a data.table's column with correlating values from another data.table?将 data.table 列中的向量值替换为另一个 data.table 中的相关值的最有效方法是什么?
【发布时间】:2020-07-05 01:21:51
【问题描述】:

这是我的问题的缩小示例。我有一个 data.table,其中有一列以矢量形式存在的多个 ID。这些 ID 都对应另一个 data.table 中的单词。

ID.table <- data.table(IDs = list(c(4, 5, 6), c(2, 3, 4)))
word.table <- data.table(ID = c(1, 2, 3, 4, 5, 6), word = c("This", "is", "a", "test", "sentence", "."))

产生

     IDs
1: 4,5,6
2: 2,3,4

   ID     word
1:  1     This
2:  2       is
3:  3        a
4:  4     test
5:  5 sentence
6:  6        .

我需要将 ID.table 中的所有 ID 转换为 word.table 中对应的单词,如下所示。

               IDs
1: test,sentence,.
2:       is,a,test

我知道我可以使用 for 循环并遍历 ID.table 中的每个向量来做到这一点,但我的实际表有数千行,这意味着它运行非常缓慢。

row <- 1
for(ID.row in ID.table[, IDs]){
  word.row <- word.table[ID %in% ID.row]$word
  ID.table[row] <- word.row
  
  row <- row + 1
}

有没有更有效的方法来做到这一点?

编辑:我在 word.table 中列出从 1 开始的顺序 ID 是一个错误。 ID.table 和 word.table 看起来更像这样。

           IDs
1: 608,609,610
2: 606,607,608

     ID     word
1:  605     This
2:  606       is
3:  607        a
4:  608     test
5:  609 sentence
6:  610        .

其中 ID.table 的每一行将是一个不从 1 开始的序列号向量,并且 word.table 的 ID 列将具有不总是不从 1 开始的序列号。

【问题讨论】:

    标签: r data.table tidyverse


    【解决方案1】:

    你可以使用match

    library(data.table)
    
    ID.table[, IDs := lapply(IDs,function(x) word.table$word[match(x,word.table$ID)])]
    ID.table
    
    #               IDs
    #1: test,sentence,.
    #2:       is,a,test
    

    如果您可以使用tidyverse 函数,另一个选项是unnest IDs 并加入word.table

    library(dplyr)
    
    ID.table %>%
      mutate(row = row_number()) %>%
      tidyr::unnest(IDs) %>%
      left_join(word.table, by = c('IDs' = 'ID')) %>%
      group_by(row) %>%
      summarise(Ids = list(word)) %>%
      select(-row)
    

    【讨论】:

      【解决方案2】:

      我们可以传递一个命名向量来匹配和替换,方法是遍历列表列“IDs”并将输出分配 (:=) 给 IDs

      ID.table[, IDs := lapply(IDs, function(x) 
             setNames(word.table$word, word.table$ID)[as.character(x)])]
      

      如果 ID 是按顺序排列的,则更容易,即使用 ID 作为数字索引来替换 'word' 列中的相应值

      ID.table[, IDs := lapply(IDs, function(x) word.table$word[x])]
      ID.table
      #              IDs
      #1: test,sentence,.
      #2:       is,a,test
      

      最好这样做一次而不循环unlisting,替换值,然后relist

      ID.table[, IDs := relist(word.table$word[unlist(IDs)], skeleton= IDs)]
      

      注意:这两种方法都更简单,更直接有效


      或者使用紧凑的 tidyverse 方法

      library(purrr)
      library(dplyr)
      ID.table %>% 
            mutate(IDs = map(IDs, ~ word.table$word[.x]))
      #              IDs
      #1: test,sentence,.
      #2:       is,a,test
      

      这不会改变data.table原来的属性结构

      基准测试

      在稍微大一点的数据集上

      ID.table1 <- ID.table[rep(seq_len(.N), 1e6)]
      ID.table2 <- copy(ID.table1)
      ID.table3 <- copy(ID.table1)
      ID.table4 <- copy(ID.table1)
      
      system.time(ID.table1[, IDs := lapply(IDs, function(x) 
             setNames(word.table$word, word.table$ID)[as.character(x)])])
      #user  system elapsed 
      # 29.971   0.492  30.264 
             
      system.time(ID.table2[, IDs := lapply(IDs, function(x) word.table$word[x])])
      #user  system elapsed 
      #  8.079   0.086   8.097 
      
      system.time(ID.table3[, IDs := relist(word.table$word[unlist(IDs)], skeleton= IDs)])
      # user  system elapsed 
      # 14.085   0.109  14.081 
      
      system.time(ID.table4 %>% 
            mutate(IDs = map(IDs, ~ word.table$word[.x])))
      #user  system elapsed 
      #  3.724   0.018   3.734 
      

      【讨论】:

      • ID 总是连续的,但我意识到我在 word.table 的 id 列使用 1:6 时犯了一个错误。如果 word.table 的 ID 列改为 c(605, 606, 607, 608, 609, 610) 之类的,有没有办法使用 tidyverse 方法?
      • @tmressler 只需将第一种方法用作命名向量即可。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-25
      • 1970-01-01
      • 2016-02-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多