选择由其他data.table中的过滤器给出的data.table中的行

时间:2017-07-13 10:05:37

标签: r data.table

我想选择data.table(TARGET)的特定行值(此处为DT1),其中过滤器标准位于其他data.table(DT2)中。 它不是一个精确的过滤器,因为如果DT2中的值为3,则DT1中的此值为最小值和最大值。我还有一个字符串,其中包含一个特定的模式。 例如:A = 3中的DT2DT1中的相应行包含minA = 3中的maxA = 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]

与outout:

   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中有更多行。功能需要更长时间,我想知道是否有更好/更快的方式进行这种过滤? 也许有可能&#34;升级&#34; foo()以便它可以处理整个列而不是单行?

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

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

提前感谢您的帮助。

1 个答案:

答案 0 :(得分:0)

新解决方案

我意识到通过更大的data.table的每一行花费了很多时间,所以我构建了一个新的函数foo_new,它反过来工作:

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

我没有用一行DT1加工DT2的每一行,而是选择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]]
}