基于条件的动态偏移

Dynamic offset based on condition

我的 data.table 如下所示。

library(data.table)

my_dt <- data.table(time = c(1000, 1111, 2000, 3000, 4500, 4600, 5000, 7000, 8500, 9000),
                    var1 = 1:10,
                    var2 = c(0.5, 0.6, 2, 0.8, 2.1, 1, 7, 8, 0.1, 0.3))
#printed table:

   time var1 var2
 1: 1000    1  0.5
 2: 1111    2  0.6
 3: 2000    3  2.0
 4: 3000    4  0.8
 5: 4500    5  2.1
 6: 4600    6  1.0
 7: 5000    7  7.0
 8: 7000    8  8.0
 9: 8500    9  0.1
10: 9000   10  0.3

对于 var1 中的每个值,我需要找到 var2 中第一个值的行索引等于 to/greater 然后是 var1 中的值。然后我需要通过减去 time col 中的相应值来计算 time 的差异。 例如:var1 中的第一个值是 1(也是行索引 1)。 var2 中等于 to/greater 然后 1 的第一个值是 2.0 并且在行索引 3 处。因此相应的时间差是 2000 - 1000 = 1000:

my_dt$time[3] - my_dt$time[1]

所需的结果如下所示。我实际上不需要 idx 列,但它可能有助于作为中间结果。

my_result <- data.table(time = c(1000, 1111, 2000, 3000, 4500, 4600, 5000, 7000, 8500, 9000),
                        var1 = 1:10,
                        var2 = c(0.5, 0.6, 2, 0.8, 2.1, 1, 7, 8, 0.1, 0.3),
                        idx = c(3, 3, 7, 7, 7, 7, 7, 8, 0, 0),
                        time.diff = c(1000, 889, 3000, 2000, 500, 400, 0, 0, NA, NA))
#printed result:
    time var1 var2 idx time.diff
 1: 1000    1  0.5   3      1000
 2: 1111    2  0.6   3       889
 3: 2000    3  2.0   7      3000
 4: 3000    4  0.8   7      2000
 5: 4500    5  2.1   7       500
 6: 4600    6  1.0   7       400
 7: 5000    7  7.0   7         0
 8: 7000    8  8.0   8         0
 9: 8500    9  0.1   0        NA
10: 9000   10  0.3   0        NA

我可以通过结合使用 for 和 while 循环来实现这个结果。然而,执行速度非常缓慢。我想知道是否有更好的解决方案 a) 使用 data.table 语法创建 col idx 和 b) 使用 idx 中的值作为shift() 中的偏移参数 (n)?非常感谢!

my_dt[,idx := max.col(outer(var1, var2, '<='), 'first')][,
       idx := idx * NA^(var2[idx]<var1)][,
       time_diff := time[idx] - time][]

    time var1 var2 idx time_diff
 1: 1000    1  0.5   3      1000
 2: 1111    2  0.6   3       889
 3: 2000    3  2.0   7      3000
 4: 3000    4  0.8   7      2000
 5: 4500    5  2.1   7       500
 6: 4600    6  1.0   7       400
 7: 5000    7  7.0   7         0
 8: 7000    8  8.0   8         0
 9: 8500    9  0.1  NA        NA
10: 9000   10  0.3  NA        NA

正在将我的评论转换为答案以便回答问题

my_dt[, time.diff := .SD[my_dt, time - i.time, on = .(var2 >= var1), mult = "first"]]

基本上,

  • my_dt[my_dt, on = .(var2 >= var1)]是自non-equi加入。

  • mult = "first" 仅用于选择第一个匹配项,否则它将把每个 var1 连接到所有 <= var2

  • 因为它是一个self-join,两个table有相同的列,因此i.前缀代表table中的列第 i 位(如 DT[i, j, by]

  • 关于 .SD 部分,只是因为赋值运算符 (:=) 打乱了顺序,意思是,以下行有效(没有赋值),但是搞砸它。可能与data.table[i, j, by]

    内部的操作顺序有关
    my_dt[my_dt, time - i.time, on = .(var2 >= var1), mult = "first"]
    # [1] 1000  889 3000 2000  500  400    0    0   NA   NA