我正在尝试解决建议的here问题。基本上,我需要的是,对于data.table的每一行,取上一行中每个变量的值,并用它们来定义下一行中的变量值。
我尝试使用data.table
,但是结果非常庞大,我认为效率极低(尤其是对于大量行)。我也尝试使用shift()
函数,但无法将其放入我的临时解决方案中。
这是一个玩具示例:
library(data.table)
DT = data.table(a = numeric(10L), b = numeric(10L), c = numeric(10L), iter = 1:10)
for(i in DT[,.I]){
DT[i, c('a','b','c') := {
if(iter == 1) {
a = 1
b = 2
c = 3
} else { # if it is not the first iteration
a = DT[i-1, a + b] # read the values from the previous row to compute the new values
b = DT[i-1, b] - a
c = a / b + DT[i-1, c]
}
.(a, b, c)
}]
}
这是输出:
a b c iter
1: 1 2 3.0000000 1
2: 3 -1 0.0000000 2
3: 2 -3 -0.6666667 3
4: -1 -2 -0.1666667 4
5: -3 1 -3.1666667 5
6: -2 3 -3.8333333 6
7: 1 2 -3.3333333 7
8: 3 -1 -6.3333333 8
9: 2 -3 -7.0000000 9
10: -1 -2 -6.5000000 10
有人可以帮助我改善代码吗?
答案 0 :(得分:4)
注意:这不是对OP的问题的一般回答,只是对张贴的玩具示例。
a和b的迭代是每六个迭代循环一次,而c是累加和。因此,它不必进行迭代计算,但是对于任何迭代#都有封闭形式的解决方案:
f = function(i, a0 = 1, b0 = 2, c0 = 2.5){
trio = c(a0, a0+b0, b0)
a = c(trio, -trio)
b = -c(tail(a, 1L), head(a, -1L))
cs = cumsum(a/b)
c6 = tail(cs, 1L)
k = (i - 1L) %/% 6L
ii = 1L + (i - 1L) %% 6L
list(a = a[ii], b = b[ii], c = c0 + k*c6 + cs[ii])
}
library(data.table)
DT = data.table(iter = 1:10)[, c("a", "b", "c") := f(iter)][]
iter a b c
1: 1 1 2 3.0000000
2: 2 3 -1 0.0000000
3: 3 2 -3 -0.6666667
4: 4 -1 -2 -0.1666667
5: 5 -3 1 -3.1666667
6: 6 -2 3 -3.8333333
7: 7 1 2 -3.3333333
8: 8 3 -1 -6.3333333
9: 9 2 -3 -7.0000000
10: 10 -1 -2 -6.5000000
也就是说,您可以直接跳至任何迭代:
> setDT(f(10))[]
a b c
1: -1 -2 -6.5
> setDT(f(100))[]
a b c
1: -1 -2 -101.5
答案 1 :(得分:3)
您可以将Reduce
与acumulate = T
一起使用
fun <- function(x, junk){
x[1] <- sum(x[1:2])
x[2] <- diff(x[1:2])
x[3] <- x[1]/x[2] + x[3]
x
}
dt <-
as.data.table(do.call(rbind, Reduce(fun, numeric(9L), accumulate = T, init = 1:3)))
setnames(dt, c('a', 'b', 'c'))
dt
# a b c
# 1: 1 2 3.0000000
# 2: 3 -1 0.0000000
# 3: 2 -3 -0.6666667
# 4: -1 -2 -0.1666667
# 5: -3 1 -3.1666667
# 6: -2 3 -3.8333333
# 7: 1 2 -3.3333333
# 8: 3 -1 -6.3333333
# 9: 2 -3 -7.0000000
# 10: -1 -2 -6.5000000
您可以使用transpose
代替do.call(rbind,
,如下所示,但是如果您已加载tidyverse
或purrr
,请确保transpose
为{{1} }
data.table::transpose
dt <-
as.data.table(transpose(Reduce(fun, numeric(9L), accumulate = T, init = 1:3)))
的解释:
每次迭代时,junk
会将先前的输出(或Reduce
)及其init
参数的第i个元素传递给x
。因此,即使您不打算在函数f
中使用x
的{{1}}参数,您仍然需要为其提供一个参数。如果不添加此多余的“垃圾”参数,则在运行时会收到“未使用的参数”错误,因为它试图将额外的参数添加到Reduce
,但是f
只有一个参数。
答案 2 :(得分:2)
另一个选择:
cols <- c('a','b','c')
A <- 1; B <- 2; C <- 3
DT[iter==1, (cols) := .(A, B, C)]
DT[iter>1,
(cols) := {
A = A + B
B = B - A
C = A / B + C
.(A, B, C)
},
by=iter]
答案 3 :(得分:1)
实际上,您可以使用递归函数调用解决问题,在该方法中,您可以将值从函数调用传播到函数调用,而无需使用上一行的值。 在 base 中,您可以这样做:
DT = data.frame(a = numeric(10L), b = numeric(10L), c = numeric(10L), iter = 1:10)
fun <- function(a, b, c, n) {
a <- a + b
b <- b - a
c <- a/b + c
n <- n - 1
if(n<=0) {return(c(a,b,c))}
return(rbind(c(a,b,c),fun(a,b,c,n)))
}
DT[1,1:3] <- 1:3
DT[-1,1:3] <- fun(DT[1,1], DT[1,2], DT[1,3], 9)
DT
a b c iter
1 1 2 3.0000000 1
2 3 -1 0.0000000 2
3 2 -3 -0.6666667 3
4 -1 -2 -0.1666667 4
5 -3 1 -3.1666667 5
6 -2 3 -3.8333333 6
7 1 2 -3.3333333 7
8 3 -1 -6.3333333 8
9 2 -3 -7.0000000 9
10 -1 -2 -6.5000000 10
或者,您可以简单地制作一个for loop
:
DT = data.frame(a = numeric(10L), b = numeric(10L), c = numeric(10L), iter = 1:10)
a <- 1
b <- 2
c <- 3
for(i in seq_len(nrow(DT))) {
DT[i,1:3] <- c(a,b,c)
a <- a + b
b <- b - a
c <- a/b + c
}
DT
a b c iter
1 1 2 3.0000000 1
2 3 -1 0.0000000 2
3 2 -3 -0.6666667 3
4 -1 -2 -0.1666667 4
5 -3 1 -3.1666667 5
6 -2 3 -3.8333333 6
7 1 2 -3.3333333 7
8 3 -1 -6.3333333 8
9 2 -3 -7.0000000 9
10 -1 -2 -6.5000000 10
但这也会很慢。给出了一种快速的解决方案,例如由IceCreamToucan。