Функция скользящего окна для нерегулярных временных рядов, которые могут обрабатывать дубликаты - PullRequest
0 голосов
/ 09 октября 2018

У меня есть следующие data.frame:

    grp  nr   yr
 1:   A 1.0 2009
 2:   A 2.0 2009
 3:   A 1.5 2009
 4:   A 1.0 2010
 5:   B 3.0 2009
 6:   B 2.0 2010
 7:   B  NA 2011
 8:   C 3.0 2014
 9:   C 3.0 2019
10:   C 3.0 2020
11:   C 4.0 2021

Желаемый вывод:

   grp  nr   yr nr_roll_period_3
1    A 1.0 2009               NA
2    A 2.0 2009               NA
3    A 1.5 2009               NA
4    A 1.0 2010               NA
5    B 3.0 2009               NA
6    B 2.0 2010               NA
7    B  NA 2011               NA
8    C 3.0 2014               NA
9    C 3.0 2019               NA
10   C 3.0 2020               NA
11   C 4.0 2021         3.333333

Логика:

  • Я хочу вычислить прокатсреднее значение для периода длины k (скажем, 3), где 3 включает текущий месяц / год / день (по группам)
  • Однако это не должно вычислять что-либо, если нет 3 последовательных лет / месяцев/ days
  • Аналогично, когда в столбце для расчета в течение этого периода есть NA для вывода, результатом должно быть NA.

В настоящее время у меня есть эта функция:

calculate_rolling_window <-

  function(dt, date_col, calc_col, id, k) {

    require(data.table)

    return(setDT(dt)[, paste(calc_col, "roll_period", k, sep = "_") := 

    ifelse(
    sapply(get(date_col), function(x) length(get(calc_col)[between(get(date_col), x - k + 1, x)])) < k,
    NA_real_,
    sapply(get(date_col), function(x) mean(get(calc_col)[between(get(date_col), x - k + 1, x)]))
    ),

   by = mget(id)])

  }

Она отлично работает для обычных случаев, когда в столбце даты нет дубликатов.Тем не менее, с дубликатами это не удается:

    grp  nr   yr nr_roll_period_3
 1:   A 1.0 2009         1.500000
 2:   A 2.0 2009         1.500000
 3:   A 1.5 2009         1.500000
 4:   A 1.0 2010         1.375000
 5:   B 3.0 2009               NA
 6:   B 2.0 2010               NA
 7:   B  NA 2011               NA
 8:   C 3.0 2014               NA
 9:   C 3.0 2019               NA
10:   C 3.0 2020               NA
11:   C 4.0 2021         3.333333

Есть идеи, как с этим справиться?Нет необходимости исключительно data.table подхода.

1 Ответ

0 голосов
/ 26 декабря 2018

Эту проблему можно решить с помощью группировки в неэквивалентном объединении для агрегирования по скользящему окну длины k, фильтрации по k последовательным годам и объединению обновлений :

library(data.table)
k <- 3L
# group by join parameters of a non-equi join
mDT <- setDT(DT)[.(grp = grp, upper = yr, lower = yr - k), 
                 on = .(grp, yr <= upper, yr > lower), 
                 .(uniqueN(x.yr), mean(nr)), by = .EACHI]
# update join with filtered intermediate result
DT[mDT[V1 == k], on = .(grp, yr), paste0("nr_roll_period_", k) := V2]
DT

, который возвращает ожидаемый результат ОП:

    grp  nr   yr nr_roll_period
 1:   A 1.0 2009             NA
 2:   A 2.0 2009             NA
 3:   A 1.5 2009             NA
 4:   A 1.0 2010             NA
 5:   B 3.0 2009             NA
 6:   B 2.0 2010             NA
 7:   B  NA 2011             NA
 8:   C 3.0 2014             NA
 9:   C 3.0 2019             NA
10:   C 3.0 2020             NA
11:   C 4.0 2021       3.333333

Промежуточный результат mDT содержит скользящее среднее V2 за k периодови количество уникальных / отличных лет V1 в каждом периоде.Он создается с помощью неэквивалентного объединения из DT с таблицей данных, содержащей верхнюю и нижнюю границы, которая создается на лету .(grp = grp, upper = yr, lower = yr - k).

mDT
    grp   yr   yr V1       V2
 1:   A 2009 2006  1 1.500000
 2:   A 2009 2006  1 1.500000
 3:   A 2009 2006  1 1.500000
 4:   A 2010 2007  2 1.375000
 5:   B 2009 2006  1 3.000000
 6:   B 2010 2007  2 2.500000
 7:   B 2011 2008  3       NA
 8:   C 2014 2011  1 3.000000
 9:   C 2019 2016  1 3.000000
10:   C 2020 2017  2 3.000000
11:   C 2021 2018  3 3.333333

Фильтруется для строк, которые содержат ровно k различных лет:

mDT[V1 == k]
   grp   yr   yr V1       V2
1:   B 2011 2008  3       NA
2:   C 2021 2018  3 3.333333

Наконец, к этому добавляется DT для добавления нового столбца к DT.

Обратите внимание, что mean() возвращает NA по умолчанию, если во входных данных есть NA.

Данные

library(data.table)
DT <- fread(text = "rn    grp  nr   yr
 1:   A 1.0 2009
 2:   A 2.0 2009
 3:   A 1.5 2009
 4:   A 1.0 2010
 5:   B 3.0 2009
 6:   B 2.0 2010
 7:   B  NA 2011
 8:   C 3.0 2014
 9:   C 3.0 2019
10:   C 3.0 2020
11:   C 4.0 2021", drop = 1L)
...