代码之家  ›  专栏  ›  技术社区  ›  mr.bjerre

跟踪R中时间序列数据表的变化

  •  3
  • mr.bjerre  · 技术社区  · 8 年前

    data.table 这样地

    library(data.table)
    df = data.table(
      id = c(rep(1, 3), rep(2, 4), rep(3, 2)),
      time = c(seq(1, 3, 1), seq(1, 4, 1), seq(3, 4)),
      value1 = c(0, 0, 0, 0, 2, 0, 0, 0, 1),
      value2 = c(0, 1, 0, 1, 0, 0, 0, 0, 1)
    )
    

    哪一张像这样

        id   time   value1 value2
    1:  1    1      0      0
    2:  1    2      0      1
    3:  1    3      0      0
    4:  2    1      0      1
    5:  2    2      2      0
    6:  2    3      0      0
    7:  2    4      0      0
    8:  3    3      0      0
    9:  3    4      1      1
    

    现在我想创建两个新列,跟踪每个列的值列中最后一个非零值的时间 id .Ie.我想要一个结果数据表,如下所示

        id   time   value1  last_change1  value2  last_change2
    1:  1    1      0       NA            0       NA
    2:  1    2      0       NA            1       0
    3:  1    3      0       NA            0       1
    4:  2    1      0       NA            1       0
    5:  2    2      2       0             0       1
    6:  2    3      0       1             0       2
    7:  2    4      0       2             0       3
    8:  3    3      0       NA            0       NA
    9:  3    4      1       0             1       0
    

    有没有人有一个好的解决方案,而且效果也很好?

    2 回复  |  直到 8 年前
        1
  •  3
  •   MKR    8 年前

    一种选择是使用 zoo::na.locf 填写最后一行的行号(特定于组) non-zero 现有价值观持有者 0 在中的相应行中 value 柱。最后,减去 row-number 最后的 非零 从当前行号(按组,例如。 .I-.I[1]+1 ).

    library(data.table)
    library(zoo)
    
    df[, c("last_change1", "last_change2") := 
         lapply(.SD, function(x){as.integer((.I-.I[1]+1) - na.locf(as.integer(ifelse(x == 0, NA_integer_, .I-.I[1]+1)), na.rm = FALSE))}),
       .SDcols = value1:value2, by=id]
    

    已编辑:根据客户的建议添加了简化版 @DavidArenburg

    df[, c("last_change1", "last_change2") := 
       lapply(.SD, function(x){.I - na.locf(ifelse(x == 0, NA_integer_, .I), na.rm = FALSE)}),
       .SDcols = value1:value2, by=id]
    
    
    #Modified df
    df
    #    id time value1 value2 last_change1 last_change2
    # 1:  1    1      0      0           NA           NA
    # 2:  1    2      0      1           NA            0
    # 3:  1    3      0      0           NA            1
    # 4:  2    1      0      1           NA            0
    # 5:  2    2      2      0            0            1
    # 6:  2    3      0      0            1            2
    # 7:  2    4      0      0            2            3
    # 8:  3    3      0      0           NA           NA
    # 9:  3    4      1      1            0            0
    
        2
  •  1
  •   Andre Elrico    8 年前

    data.table 解决方案:现在可以使用尽可能多的非零值。感谢@Davidernburg指出。我必须说,这个例子应该考虑到这个情况。

    fun1 <- function(x) {
        split(x,cumsum(x)) %>% lapply(function(x) {
        if(any(x!=0)){ IND2<-(min(which(x!=0)):length(x));x<-NA;x[IND2]<-0:(length(IND2)-1);return(as.numeric(x))} else {x[]<-NA;return(as.numeric(x))}
        }) %>% unlist %>% as.numeric
    }
    
    
    df[,`:=`(last_change1 = fun1(value1), last_change2 = fun1(value2)),by="id"]
    

    结果:

       id time value1 value2 last_change1 last_change2
    1:  1    1      0      0           NA           NA
    2:  1    2      0      1           NA            0
    3:  1    3      0      0           NA            1
    4:  2    1      0      1           NA            0
    5:  2    2      2      0            0            1
    6:  2    3      0      0            1            2
    7:  2    4      0      0            2            3
    8:  3    3      0      0           NA           NA
    9:  3    4      1      1            0            0