【问题标题】:Select rows in a data.table given by a filter in an other data.table选择由另一个 data.table 中的过滤器给出的 data.table 中的行
【发布时间】:2017-07-13 10:05:37
【问题描述】:

我想选择一个 data.table (DT1) 的特定行值(此处为 TARGET),其中过滤条件位于另一个 data.table (DT2) 中。 它不是一个精确的过滤器,因为如果我在 DT2 中有值 3,我在 DT1 中就有这个值的最小值和最大值变量。我还有一个包含特定模式的字符串。 例如:DT2 中的A = 3DT1 中的对应行包含minA = 3maxA = 6C = "Mon",即在C = "Mon,Tue"

DT1
    INDEX1 minA maxA       C TARGET
9 :      9    3    6 Mon,Tue    109

DT2
   A   C INDEX2
1: 3 Mon      1

我正在寻找该值所在范围以及最大目标值的行。

我有以下简化示例:

# version 1.9.6
library(data.table)

DT1 <- data.table(INDEX1 = 1:12,
                  minA = c(1,1,1,2,2,2,3,3,3,4,4,4),
                  maxA = c(4,5,6),
                  C = c("Mon,Tue", "Mon,Wed", "Tue,Thu", "Wed,Thu"),
                  TARGET = c(101:112))
size <- 2
DT2 <- data.table(A = rep(c(3,4), size),
                  C = rep(c("Mon", "Thu"), size),
                  INDEX2 = 1:(2*size))

看起来像这样:

DT1
   INDEX1 minA maxA       C TARGET
1 :      1    1    4 Mon,Tue    101
2 :      2    1    5 Mon,Wed    102
3 :      3    1    6 Tue,Thu    103
4 :      4    2    4 Wed,Thu    104
5 :      5    2    5 Mon,Tue    105
6 :      6    2    6 Mon,Wed    106
7 :      7    3    4 Tue,Thu    107
8 :      8    3    5 Wed,Thu    108
9 :      9    3    6 Mon,Tue    109
10:     10    4    4 Mon,Wed    110
11:     11    4    5 Tue,Thu    111
12:     12    4    6 Wed,Thu    112

DT2
   A   C INDEX2
1: 3 Mon      1
2: 4 Thu      2

我包含 size 只是为了扩展和测试。

到目前为止,我的解决方案如下:

我写了一个函数foo(),它接受过滤器输入值并返回DT1的索引(或其他更有用的变量)。

foo <- function(i.A, i.C){
  DT1[INDEX1 %in% grep(i.C,C) & minA <= i.A & maxA >= i.A,][TARGET == max(TARGET),]
}

我为DT2的每一行调用这个函数

DT2[, foo(i.A = A, i.C = C), by = INDEX2]

与输出:

   INDEX2 INDEX1 minA maxA       C TARGET
1:      1      9    3    6 Mon,Tue    109
2:      2     12    4    6 Wed,Thu    112

这是我的问题:

这适用于小型 data.tables,但我在 DT2 中有更多行。这些功能需要更长的时间,我想知道这种过滤是否有更好/更快的方法? 也许可以“升级”foo() 以便它可以处理整列而不是单行?

如果可能的话,我想避免像这里一样扩展我的 DT1:

我认为,我有一个比这些问题更复杂的过滤器:

提前感谢您的帮助。

【问题讨论】:

    标签: r data.table


    【解决方案1】:

    新解决方案

    我意识到遍历更大的 data.table 的每一行需要很长时间,所以我构建了一个新函数 foo_new,它可以反过来工作:

    foo_new <- function(data, i.A, i.C){
      data[C %in% i.C & A %between% i.A, INDEX2]
    }
    

    我没有将 DT2 的每一行与 DT1 的每一行匹配,而是选择 DT2 中与 DT1 的一行的值匹配的每一行。 DT2 的排序已完成,因为我需要具有最高 TARGET 值的行。此外,如果 DT2 中的一行已被选中,则在下一次迭代中将其删除。

    整个过程加快了很多:

       function   user  system elapsed 
       foo      61.511   0.327  62.052
       foo_new   0.045   0.003   0.047 
    

    这可能只是这种情况,当 DT1 小于 DT2 时 - 这是我的情况。


    这是我的整个模拟代码:

    rm(list = ls())
    library(data.table)
    
    DT1 <- data.table(INDEX1 = 1:12,
                      minA = c(1,1,1,2,2,2,3,3,3,4,4,4),
                      maxA = c(4,5,6),
                      C = c("Mon,Tue", "Mon,Wed", "Tue,Thu", "Wed,Thu"),
                      TARGET = c(101:112))
    
    size <- 20000
    DT2 <- data.table(A = rep(c(3,4), size),
                      C = rep(c("Mon", "Thu"), size),
                      INDEX2 = 1:(2*size))
    
    foo <- function(i.A, i.C){
      DT1[INDEX1 %in% grep(i.C, C) &
            minA <= i.A &
            maxA >= i.A,
          ][TARGET == max(TARGET),]
    }
    
    foo_new <- function(data, i.A, i.C){
      data[C %in% i.C & A %between% i.A, INDEX2]
    }
    
    # with foo
    DT2[, foo(i.A = A, i.C = C), by = INDEX2])
    
    # with foo_new
    DT1.ordered <- copy(DT1[order(TARGET, decreasing = TRUE)])
    tmp.index <- list()
    DT2[, TARGET := as.numeric(NA)]
    for (i in c(1:dim(DT1.ordered)[1])) {
      # i <- 1
      restdata <- copy(DT2[is.na(TARGET),])
      tmp.index <- foo_new(data = restdata,
                           i.A  = unlist(DT1.ordered[i, list(minA, maxA)]),
                           i.C  = DT1.ordered[i, strsplit(C, ",")[[1]]])
      DT2[INDEX2 %in% tmp.index, TARGET := DT1.ordered[i, TARGET]]
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-04-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多