R data.table值取决于上一行

时间:2019-05-28 13:01:58

标签: r data.table

我正在尝试解决建议的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

有人可以帮助我改善代码吗?

4 个答案:

答案 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)

您可以将Reduceacumulate = 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,,如下所示,但是如果您已加载tidyversepurrr,请确保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