【问题标题】:Use a value from the previous row in an R data.table calculation在 R data.table 计算中使用前一行的值
【发布时间】:2013-01-19 07:27:50
【问题描述】:

我想在 data.table 中创建一个新列,该列是根据一列的当前值和另一列的前一个值计算得出的。是否可以访问以前的行?

例如:

> DT <- data.table(A=1:5, B=1:5*10, C=1:5*100)
> DT
   A  B   C
1: 1 10 100
2: 2 20 200
3: 3 30 300
4: 4 40 400
5: 5 50 500
> DT[, D := C + BPreviousRow] # What is the correct code here?

正确答案应该是

> DT
   A  B   C   D
1: 1 10 100  NA
2: 2 20 200 210
3: 3 30 300 320
4: 4 40 400 430
5: 5 50 500 540

【问题讨论】:

  • 我通常给我的data.tables设置一个key:DT &lt;- data.table(A=..., key = "A")

标签: r data.table


【解决方案1】:

v1.9.6 中实现shift(),这非常简单。

DT[ , D := C + shift(B, 1L, type="lag")]
# or equivalently, in this case,
DT[ , D := C + shift(B)]

来自NEWS

  1. 新函数 shift() 实现 vectorlistdata.framesdata.tableslead/lag /em>。它需要一个type 参数,可以是“lag”(默认)或“lead”。它与:=set() 一起使用非常方便。例如:DT[, (cols) := shift(.SD, 1L), by=id]。请查看?shift 了解更多信息。

查看历史以获取以前的答案。

【讨论】:

  • .N 是否保存当前行号或其他内容?很抱歉在这里问,但我似乎无法在帮助文件中找到它...
  • @SlowLearner:您可能还会发现 .I 很有用,它保存当前组中行的行索引。
  • 使用 seq_len(.N - 1) 而不是 1:(.N-1)。这避免了与 1:0 相关的问题。
  • +1 用于.SD 示例——我试图使用lapply 并获得了时髦的结果。这要简单得多。
  • 在哪里可以找到包含所有这些新信息的更新 pdf?官方 1.9.4 小插曲和网络研讨会不包括它。而且 Rmd 1.9.5 小插曲不舒服,也不包括在内。
【解决方案2】:

使用dplyr 你可以这样做:

mutate(DT, D = lag(B) + C)

这给出了:

#   A  B   C   D
#1: 1 10 100  NA
#2: 2 20 200 210
#3: 3 30 300 320
#4: 4 40 400 430
#5: 5 50 500 540

【讨论】:

    【解决方案3】:

    几个人已经回答了具体问题。请参阅下面的代码,了解我在这种情况下使用的通用功能,它可能会有所帮助。您可以根据需要在“过去”或“未来”中查看尽可能多的行,而不仅仅是获取前一行。

    rowShift <- function(x, shiftLen = 1L) {
      r <- (1L + shiftLen):(length(x) + shiftLen)
      r[r<1] <- NA
      return(x[r])
    }
    
    # Create column D by adding column C and the value from the previous row of column B:
    DT[, D := C + rowShift(B,-1)]
    
    # Get the Old Faithul eruption length from two events ago, and three events in the future:
    as.data.table(faithful)[1:5,list(eruptLengthCurrent=eruptions,
                                     eruptLengthTwoPrior=rowShift(eruptions,-2), 
                                     eruptLengthThreeFuture=rowShift(eruptions,3))]
    ##   eruptLengthCurrent eruptLengthTwoPrior eruptLengthThreeFuture
    ##1:              3.600                  NA                  2.283
    ##2:              1.800                  NA                  4.533
    ##3:              3.333               3.600                     NA
    ##4:              2.283               1.800                     NA
    ##5:              4.533               3.333                     NA
    

    【讨论】:

    • 这是一个绝妙的答案,我很生气我已经对其他答案投了赞成票,因为这是一个更普遍的答案。事实上,我将在我的geneorama 包中使用它(如果你不介意的话)。
    • 当然,去吧。我希望有一些空闲时间并将其作为拉取请求提交给data.table 包,但是唉......
    • 从 1.9.5 版开始,data.table 中添加了一个名为 shift 的类似函数。请参阅来自@Arun 的updated answer
    【解决方案4】:

    根据上面@Steve Lianoglou 的评论,为什么不只是:

    DT[, D:= C + c(NA, B[.I - 1]) ]
    #    A  B   C   D
    # 1: 1 10 100  NA
    # 2: 2 20 200 210
    # 3: 3 30 300 320
    # 4: 4 40 400 430
    # 5: 5 50 500 540
    

    并避免使用seq_lenhead 或任何其他函数。

    【讨论】:

    • 很好 - 但是如果你想在一个组中找到前一个,这将不起作用。
    • @Matthew 你是对的。如果按组进行子集化,我会将.I 替换为seq_len(.N)
    【解决方案5】:

    按照Arun的方案,不用参考.N也可以得到类似的结果

    > DT[, D := C + c(NA, head(B, -1))][]
       A  B   C   D
    1: 1 10 100  NA
    2: 2 20 200 210
    3: 3 30 300 320
    4: 4 40 400 430
    5: 5 50 500 540
    

    【讨论】:

    • 有理由选择一种方法而不是另一种方法吗?还是仅仅是审美差异?
    • 我认为在这种情况下(即.N 随时可用),它主要是审美选择。我不知道有什么重要的区别。
    【解决方案6】:

    我添加了一个填充参数并更改了一些名称并将其命名为shifthttps://github.com/geneorama/geneorama/blob/master/R/shift.R

    【讨论】:

    • 非常感谢您的来信。我会留意它,很可能会使用它并弃用我的基因模型版本。
    【解决方案7】:

    这是我的直观解决方案:

    #create data frame
    df <- data.frame(A=1:5, B=seq(10,50,10), C=seq(100,500, 100))`
    #subtract the shift from num rows
    shift  <- 1 #in this case the shift is 1
    invshift <- nrow(df) - shift
    #Now create the new column
    df$D <- c(NA, head(df$B, invshift)+tail(df$C, invshift))`
    

    这里invshift,行数减1,为4。nrow(df)为您提供数据框或向量中的行数。同样,如果您想取更早的值,请从 nrow 2、3、...等中减去,并将 NA 相应地放在开头。

    【讨论】:

      【解决方案8】:

      它可以循环完成。

      # Create the column D
      DT$D <- 0
      # for every row in DT
      for (i in 1:length(DT$A)) {
        if(i==1) {
          #using NA at first line
          DT[i,4] <- NA
        } else {
          #D = C + BPreviousRow
          DT[i,4] <- DT[i,3] + DT[(i-1), 2]   
        }
      }
      

      使用for,你甚至可以使用这个新列DT[(i-1), 4]的行的前一个值

      【讨论】:

        猜你喜欢
        • 2019-07-25
        • 2018-09-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-12-15
        相关资源
        最近更新 更多