Как рассчитать скользящую среднюю по заданной группировке и справиться с NA - PullRequest
0 голосов
/ 18 февраля 2019

У меня есть data.table, для которого необходимо рассчитать скользящее среднее по предыдущим n дням данных (для простоты будем использовать n = 2, не включая текущий день) для указанной группировки (ID1, ID2).Скользящая средняя должна пытаться включить последние 2 дня значений для каждой пары ID1-ID2.Я хотел бы рассчитать скользящую среднюю для обработки NA двумя отдельными способами: 1. Рассчитывать только тогда, когда есть 2 наблюдения не-NA, в противном случае avg должно быть NA (например, первые 2 дня в ID1-ID2 всегда будут иметь NA).2. Рассчитайте скользящее среднее на основе любых наблюдений, не относящихся к NA, за последние 2 дня (na.rm = TRUE?).

Я пытался использовать пакет zoo и различные функции внутри него.Я остановился на следующем (использовал shift (), чтобы исключить неделю, рассмотренную в avg, расположить даты в обратном порядке, чтобы выделить даты, которые не всегда упорядочены изначально):

library(zoo)
library(data.table)
DATE = rev(rep(seq(as.Date("2018-01-01"),as.Date("2018-01-04"),"day"),4))
VALUE =seq(1,16,1)
VALUE[16] <- NA
ID1 = rep(c("A","B"),each=8)
ID2 = rep(1:2,2,each=4)
testdata = data.frame (DATE, ID1, ID2, VALUE)
setDT(testdata)[order(DATE), VALUE_AVG := shift(rollapplyr(VALUE, 2, mean, 
na.rm=TRUE,fill = NA)), by = c("ID1", "ID2")]

Кажется, у меня проблемыгруппировка по нескольким столбцам.Группы, где VALUE начинается / заканчивается значениями NA, также, кажется, вызывают проблемы.Я открыт для любых решений, которые имеют смысл в рамках data.table, особенно frollmean (необходимо обновить мои версии R + data.table).Я не знаю, нужно ли мне упорядочивать даты по-разному в сочетании с указанным выравниванием (например, «вправо»).

Я надеюсь, что мои выходные данные будут выглядеть примерно так, за исключением упорядоченных по самой старой дате сначала для группы ID1-ID2:

           DATE ID1 ID2 VALUE VALUE_AVG
 1: 2018-01-04   A   1     1       2.5
 2: 2018-01-03   A   1     2       3.5
 3: 2018-01-02   A   1     3        NA
 4: 2018-01-01   A   1     4        NA
 5: 2018-01-04   A   2     5       6.5
 6: 2018-01-03   A   2     6       7.5
 7: 2018-01-02   A   2     7        NA
 8: 2018-01-01   A   2     8        NA
 9: 2018-01-04   B   1     9      10.5
10: 2018-01-03   B   1    10      11.5
11: 2018-01-02   B   1    11        NA
12: 2018-01-01   B   1    12        NA
13: 2018-01-04   B   2    13      14.5
14: 2018-01-03   B   2    14      15.0
15: 2018-01-02   B   2    15        NA
16: 2018-01-01   B   2    NA        NA

Кажется, мой код примерно достигает желаемых результатов для образцаданные.Тем не менее, при попытке запустить один и тот же код в большом наборе данных для среднего значения за 4 недели, где ID1 и ID2 являются целыми числами, я получаю следующую ошибку:

Error in seq.default(start.at, NROW(data), by = by) : 
  wrong sign in 'by' argument

Мои результаты кажутся подходящими для большинства ID1-ID2комбинации, но есть особые случаи ID1, где VALUE имеет начальные и конечные NA.Я предполагаю, что это вызывает проблему, хотя это не относится к примеру выше.

Ответы [ 2 ]

0 голосов
/ 18 февраля 2019

Использование shift усложняет это без необходимости.rollapply уже может справиться с этим сам.В rollapplyr укажите:

  • ширину list(-seq(2)), чтобы указать, что он должен действовать на смещения -1 и -2.

  • partial = TRUE, чтобы указать, что если имеется менее 2 предыдущих строк, он будет использовать все, что там есть.

  • fill = NA для заполненияпустые ячейки с NA

  • na.rm = TRUE, чтобы удалить любые NA и выполнить среднее значение только для оставшихся ячеек.Если все предыдущие ячейки являются NA, то среднее дает NaN.

Чтобы рассматривать только ситуации, когда есть 2 предыдущих не-NA, дающих NA, в противном случае удалите аргументы partial = TRUE и na.rm = TRUE.

Первый случай

Возьмите среднее значение не-NA в предыдущих 2 рядах или меньше строк, если меньше предыдущих строк.

testdata <- data.table(DATE, ID1, ID2, VALUE, key = c("ID1", "ID2", "DATE"))
testdata[, VALUE_AVG := 
  rollapplyr(VALUE, list(-seq(2)), mean, fill = NA, partial = TRUE, na.rm = TRUE),
  by = c("ID1", "ID2")]
testdata

, давая:

          DATE ID1 ID2 VALUE VALUE_AVG
 1: 2018-01-01   A   1     4        NA
 2: 2018-01-02   A   1     3       4.0
 3: 2018-01-03   A   1     2       3.5
 4: 2018-01-04   A   1     1       2.5
 5: 2018-01-01   A   2     8        NA
 6: 2018-01-02   A   2     7       8.0
 7: 2018-01-03   A   2     6       7.5
 8: 2018-01-04   A   2     5       6.5
 9: 2018-01-01   B   1    12        NA
10: 2018-01-02   B   1    11      12.0
11: 2018-01-03   B   1    10      11.5
12: 2018-01-04   B   1     9      10.5
13: 2018-01-01   B   2    NA        NA
14: 2018-01-02   B   2    15       NaN
15: 2018-01-03   B   2    14      15.0
16: 2018-01-04   B   2    13      14.5

Второй случай

NA, если любой из предыдущих 2 строк равен NA или если имеется менее 2 предыдущих строк.

testdata <- data.table(DATE, ID1, ID2, VALUE, key = c("ID1", "ID2", "DATE"))
testdata[, VALUE_AVG := 
  rollapplyr(VALUE, list(-seq(2)), mean, fill = NA),
  by = c("ID1", "ID2")]
testdata

, что дает:

          DATE ID1 ID2 VALUE VALUE_AVG
 1: 2018-01-01   A   1     4        NA
 2: 2018-01-02   A   1     3        NA
 3: 2018-01-03   A   1     2       3.5
 4: 2018-01-04   A   1     1       2.5
 5: 2018-01-01   A   2     8        NA
 6: 2018-01-02   A   2     7        NA
 7: 2018-01-03   A   2     6       7.5
 8: 2018-01-04   A   2     5       6.5
 9: 2018-01-01   B   1    12        NA
10: 2018-01-02   B   1    11        NA
11: 2018-01-03   B   1    10      11.5
12: 2018-01-04   B   1     9      10.5
13: 2018-01-01   B   2    NA        NA
14: 2018-01-02   B   2    15        NA
15: 2018-01-03   B   2    14        NA
16: 2018-01-04   B   2    13      14.5
0 голосов
/ 18 февраля 2019

Может быть что-то вроде:

setorder(setDT(testdata), ID1, ID2, DATE)
testdata[order(DATE), VALUE_AVG := shift(
        rollapplyr(VALUE, 2L, function(x) if(sum(!is.na(x)) > 0L) mean(x, na.rm=TRUE), fill = NA_real_)
    ), by = c("ID1", "ID2")]
...