如何使用 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.table
或 dtplyr
进行上述操作并进行优化,但也可以按组进行?
编辑:
查看问题的可视化。
它应该像 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
语句。如果前几行之间的差异 s
和 t
值(即 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
由于我找不到以下问题的答案:
我会试着问一个与上述相关的新问题。即,我想根据先前值的输出递归地应用自定义函数到当前行。
带有数据框和 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.table
或 dtplyr
进行上述操作并进行优化,但也可以按组进行?
编辑: 查看问题的可视化。
它应该像 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
语句。如果前几行之间的差异 s
和 t
值(即 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