Интерполировать временные ряды (заменить НА), используя скорость роста другого временного ряда - PullRequest
1 голос
/ 02 августа 2020

Допустим, у меня есть такой набор данных:

trt <- data.table(group = rep(c("a","b"), each = 5), 
                  val1= c(60,62,NA,NA,71, NA, 21,22,NA,25),
                  val2 = c(1,1,1,NA,2, 1,1,NA,NA,2),
                  reflev = rep(c(1.01, 1.03, 1.061, 1.104,1.159), 2))
trt[ , ref:= round(reflev/shift(reflev), 2), by = group]


> trt
    group val1 val2 reflev  ref
 1:     a   60    1  1.010   NA
 2:     a   62    1  1.030 1.02
 3:     a   NA    1  1.061 1.03
 4:     a   NA   NA  1.104 1.04
 5:     a   71    2  1.159 1.05
 6:     b   NA    1  1.010   NA
 7:     b   21    1  1.030 1.02
 8:     b   22   NA  1.061 1.03
 9:     b   NA   NA  1.104 1.04
10:     b   25    2  1.159 1.05

В каждой группе я хотел бы заменить значения NA в val1, val2 и нескольких других подобных столбцах (так что в идеале lapply будет задействован ), умножив предыдущее доступное значение (например, shift(val1) или lag(val1)) на значение из столбца ref. Если после значения, отличного от NA, в последовательности появляются несколько NA, все они должны быть интерполированы с использованием ранее интерполированных значений в качестве отправной точки.

Итак, вот как я представляю себе вычисления:

    group val1          val2         reflev  ref
 1:     a   60            1           1.010   NA
 2:     a   62            1           1.030 1.02
 3:     a   62*1.03       1           1.061 1.03
 4:     a   62*1.03*1.04  1*1.04      1.104 1.04
 5:     a   71            2           1.159 1.05
 6:     b   NA            1           1.010   NA
 7:     b   21            1           1.030 1.02
 8:     b   22           1*1.03       1.061 1.03
 9:     b   22*1.04      1*1.03*1.04  1.104 1.04
10:     b   25            2           1.159 1.05

Есть идеи? Все, что я мог придумать, очень грязное и включает два цикла, один для групп, а другой для нужного столбца.

Ответы [ 2 ]

1 голос
/ 03 августа 2020

Вот еще вариант:

cols <- paste0("val", 1L:2L)
trt[, paste0("prev", cols) := lapply(.SD, nafill, type="locf"), group, .SDcols=cols]

trt[, outval1 := fifelse(is.na(val1), prevval1 * cumprod(ref), val1), .(group, rleid(is.na(val1)))]

trt[, outval2 := fifelse(is.na(val2), prevval2 * cumprod(ref), val2), .(group, rleid(is.na(val2)))]

редактировать для нескольких val столбцов. Может быть, что-то вроде этого:

cols <- paste0("V", 1L:30L)
for (x in cols) {
    trt[, c("prev", "ri") := {
            v <- get(x)
            .(nafill(v, "locf"), rleid(is.na(v)))
        }, group]
    trt[, paste0("out", x) := {
            v <- get(x)
            fifelse(is.na(v), prev * cumprod(ref), v)
        }, .(group, ri)]
}

или используя melt, что будет быстрее:

mDT <- melt(trt[, rn := .I], measure.vars=patterns("^V"))
mDT[, pv := nafill(value, "locf"), group]
mDT[, nv := fifelse(is.na(value), pv * cumprod(ref), value),
    .(group, variable, rleid(is.na(value)))]
dcast(mDT, rn + group + reflev + ref ~ variable, value.var="nv")

образцы данных:

library(data.table)
set.seed(0L)
nc <- 30L
nr <- 3e3L
trt <- data.table(group = rep(1:(nr/5L), each=5L), 
    reflev = 1+runif(nr)/10,
    as.data.table(matrix(sample(c(NA,10,20,30), nc*nr, TRUE), ncol=nc)))
trt[ , ref:= round(reflev/shift(reflev), 2), by = group]
0 голосов
/ 02 августа 2020

Вот мое «быстрое грязное» решение. Рад слышать возможности для улучшения:

interpolate <- function(data, int.column){

  for(row in 2:nrow(data)){
    if(is.na(data[row,get(int.column)]) & !is.na(data[row-1, get(int.column)])){
      data[[int.column]][row] <- data[row,ref]*data[[int.column]][row-1]
      }
  }
  return(data[ , get(int.column)])
}

Я использую такой странный вызов int.column (имя столбца для интерполяции), так как мне не удалось вызвать int.column из подходящей среды в противном случае всегда возникала ошибка).

Тогда trt[ , val1:= interpolate(.SD,"val1"), by = group] для интерполяции одного столбца или

columns.to.int <- c("val1", "val2")
trt[ , (columns.to.int):= lapply(columns.to.int, function(x)interpolate(.SD,x)), by = group]

для нескольких.

Есть ли лучший способ?

...