Отслеживание изменений в таблице данных временных рядов в R - PullRequest
0 голосов
/ 28 мая 2018

У меня есть 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.То есть.Я хочу получить итоговую таблицу данных следующим образом

    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 ]

0 голосов
/ 29 мая 2018

Один из вариантов - использовать zoo::na.locf для заполнения номера строки (специфичной для группы) последних non-zero значений в заполнителе для 0 в соответствующих строках в столбцах value.Наконец, вычтите row-number из последних non-zero из номера текущей строки (по группам, например, .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
0 голосов
/ 28 мая 2018

data.table решение: теперь оно работает с таким количеством ненулевых значений, которое вы можете найти.Спасибо @DavidArenburg за указание.Я должен сказать, что пример должен был рассмотреть этот случай.

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
...