如何使用 data.table 应用自定义递归函数并按组遍历每个索引?

How to apply a custom recursive function with data.table and loop over each index group-wise?

由于我找不到以下问题的答案:

我会试着问一个与上述相关的新问题。即,我想根据先前值的输出递归地应用自定义函数到当前行。

带有数据框和 for 循环的示例:

for(i in 2:nrow(df1)) df1$z[i] <- df1$z[i-1] + df1$x[i-1] - df1$y[i-1] 

带有数据框和带有自定义函数的 for 循环的示例:

for(i in 2:nrow(df1)) df1$z[i] <- ifelse(df1$z[i-1] == df1$z[i],
                                          df1$z[i-1] + df1$x[i-1] - df1$y[i-1],
                                          df1$z[i-1] - df1$x[i-1] - df1$y[i-1])

然而,对于 >1 的轧机行,data.frames 和 for 循环不是最优的。

有什么方法可以用 data.tabledtplyr 进行上述操作并进行优化,但也可以按组进行?

编辑: 查看问题的可视化。

它应该像 for(i in 2:nrow(df) 一样首先从第二行开始,它应该使用 the custom function if and only if group[i]==group[i-1]

这样使用 Reduce 可以吗?

tmp = data.table(
  grp = c(rep(0,6), rep(1,6)),
  x=c(10,20,30,40,50,60,1,2,3,4,5,6),
  y=c(1,2,3,4,5,6, 10,20,30,40,50,60)
)
tmp[, z:=Reduce(f=function(z,i) z + x[i-1] - y[i-1],
                x=(1:.N)[-1],
                init=0,
                accumulate = T)
    ,by=grp
]

输出:

    grp  x  y    z
 1:   0 10  1    0
 2:   0 20  2    9
 3:   0 30  3   27
 4:   0 40  4   54
 5:   0 50  5   90
 6:   0 60  6  135
 7:   1  1 10    0
 8:   1  2 20   -9
 9:   1  3 30  -27
10:   1  4 40  -54
11:   1  5 50  -90
12:   1  6 60 -135

以第 4 行为例。z 列中的值为 54,等于前一行的 z-value + 前一行的 x-value,减去前一行的 y-value .

Reduce 中的函数 f 可以采用任何复杂的形式,包括 ifelse 语句。这是一个示例,其中我创建了一个名为 func 的函数,它是 Reduce 的包装器。请注意,在 Reduce 语句中,f 是一个采用 prev 的函数(感谢@r2evans 的建议),并且此函数首先计算前一行的 s 值减去前一行的 t 值(这类似于您的 x[-1]-y[-1]。然后有一个 ifelse 语句。如果前几行之间的差异 st 值(即 k) >20,那么这一行的新值就是之前的z值减去20-4k的乘积(即prev-(20-4k)),否则就是之前的z值+ k(即等于您的原始公式:z[i-1]+x[i-1]-y[i-1]

func <- function(s,t) {
  Reduce(
    f=function(prev,i) {
      k=s[i-1] - t[i-1]
      ifelse(k>10, prev -(20-4*k), prev+k)
    },
    x=2:length(s),
    init=0,
    accumulate = TRUE
  )
}

然后您可以将 func(x,y) 的值分配给 z,如下所示:

tmp[, z:=func(x,y), by=.(grp)][]

输出:

    grp  x  y    z
 1:   0 10  1    0
 2:   0 20  2    9
 3:   0 30  3   61
 4:   0 40  4  149
 5:   0 50  5  273
 6:   0 60  6  433
 7:   1  1 10    0
 8:   1  2 20   -9
 9:   1  3 30  -27
10:   1  4 40  -54
11:   1  5 50  -90
12:   1  6 60 -135