Нужна функция скользящего среднего, чтобы учитывать только предыдущие наблюдения - PullRequest
0 голосов
/ 29 декабря 2018

Мне нужно создать скользящее среднее переменной, которая учитывает только предыдущие наблюдения этой переменной, для каждой отдельной группы данных.

Я использовал функцию, а затем немного взломал мои переменные, чтобысделай так, чтоб это работало.Позвольте мне объяснить ниже.

Я получил эту функцию из stackoverflow:

mav <- function(x,n) if(length(x) >= n)stats::filter(x,rep(1/n,n), sides=1) else NA_real_ 

Давайте рассмотрим пример скользящего среднего по 2 наблюдениям:

test = data.table("values" = c(1,2,3,4,5,6,7,8, 9,10,11,12), "category" = c(1,1,1,1,1,1,2,2,2,2,2,2))
test[, ma2 := as.numeric(mav(values, n = 2)), by = category]

Это дает:

   values category  ma2
      1        1   NA
      2        1  1.5
      3        1  2.5
      4        1  3.5
      5        1  4.5
      6        1  5.5
      7        2   NA
      8        2  7.5
      9        2  8.5
     10        2  9.5
     11        2 10.5
     12        2 11.5

Я хочу, чтобы третье наблюдение ma2 было средним из последних двух наблюдений ma2.Но здесь 3-е наблюдение ma2 является средним значением 2-го и 3-го наблюдения.

Итак, я создаю Vprev, другую переменную, которая совпадает с «Values», но принимает предыдущее значение «Values» для каждого наблюдения:

test[, vprev:= as.numeric(shift(values, 1L, type = "lag" )), by = category]

И затем,Вместо этого я запускаю скользящее среднее («TRUEma2») для переменной vprev:

test[, TRUEma2 := as.numeric(mav(vprev, n = 2)), by = category] 

values category  ma2 vprev TRUEma2
  1        1   NA    NA      NA
  2        1  1.5     1      NA
  3        1  2.5     2     1.5
  4        1  3.5     3     2.5
  5        1  4.5     4     3.5
  6        1  5.5     5     4.5
  7        2   NA    NA      NA
  8        2  7.5     7      NA
  9        2  8.5     8     7.5
 10        2  9.5     9     8.5
 11        2 10.5    10     9.5
 12        2 11.5    11    10.5

Раньше это работало нормально, потому что мои наборы данных были довольно маленькими.Но теперь я должен сделать это на нескольких наборах данных, которые имеют от 2 до 3 миллионов наблюдений.И мне нужно создать скользящие средние для примерно 30 переменных в каждом наборе данных.Процесс, который я описал, занимает до 1 минуты 40 секунд для каждой переменной, поэтому я рассчитал, что мне потребуется 25 часов для предварительной обработки всех моих наборов данных ...

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

test[, vprev:= as.numeric(shift(values, 1L, type = "lag" )), by = category]

Сама скользящая средняя не требует много времени для вычисления.

Я попытался пропуститьэто с помощью shift () в строке кода скользящего среднего:

test[, TRUEma2 := as.numeric(mav(shift(values,1L,type = "lag), n = 2)), by = category]   

Но это не быстрее.

Я также пытался изменить функцию скользящего среднего следующим образом:

mav2 <- function(x,n) if(length(x) >= n+1)stats::filter(x-1,rep(1/n,n), sides=1) else NA_real_ 

Но тогда первое значение x может принять значение наблюдения перед ним, которое не совпадает сгруппа данных / категория.

     values category mav2
      1        1   NA
      2        1  0.5
      3        1  1.5
      4        1  2.5
      5        1  3.5
      6        1  4.5
      7        2   NA
      8        2  6.5
      9        2  7.5
     10        2  8.5
     11        2  9.5
     12        2 10.5

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

Большое спасибо за вашу помощь:)

РЕДАКТИРОВАТЬ: я пробовал решения, предложенные lbusett и Icecreamtoucan, и хотя он работал на тестовых данных, Igotследующее сообщение об ошибке на реальных данных :

Error in [. data.table (toptrain2, ,: = (paste0("m3_", c("killsM")), : Type of RHS ('double') must match LHS ('logical'). To check and coerce would impact performance too much for the fastest cases. Either change the type of the target column, or coerce the RHS of := yourself (e.g. by using 1L instead of 1)

Меня попросили дать образец фактическогоданные.Вот результат (просто небольшая выдержка из моих данных):

structure(list(killsM = c(4L, 2L, 0L, 3L, 6L, 0L, 1L, 2L, 3L, 5L, 6L, 1L, 4L, 4L, 2L, 6L, 6L, 3L, 1L, 2L), soloKillsM = c(4L, 2L, 0L, 0L, 3L, 0L, 0L, 1L, 1L, 3L, 0L, 0L, 1L, 2L, 0L, 3L, 0L, 1L, 0L, 0L), deathsM = c(3L, 5L, 5L, 1L, 4L, 4L, 3L, 2L, 0L, 4L, 1L, 7L, 1L, 2L, 2L, 2L, 3L, 3L, 3L, 1L), assistsM = c(1L, 1L, 2L, 2L, 7L, 0L, 2L, 2L, 3L, 0L, 4L, 1L, 0L, 1L, 1L, 1L, 4L, 1L, 3L, 3L), killParticipationM = c(0.151515151515152, 0.0909090909090909, 0.125, 0.3125, 0.464285714285714, 0, 0.157894736842105, 0.210526315789474, 0.222222222222222, 0.185185185185185, 0.434782608695652, 0.0869565217391304, 0.2, 0.25, 0.130434782608696, 0.304347826086957, 0.4, 0.16, 0.181818181818182, 0.227272727272727), firstTowerKillM = c(0L, 0L, 0L, 0L, 1L, 0L, 0L, 0L, 0L, 0L, 1L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L)), row.names = c(NA, 20L), class = "data.frame")

Мне кажется, что единственная разница с данными теста - это имя переменных иценность наблюдений

Ответы [ 3 ]

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

Как насчет смещения результатов вместо входных значений?Примерно так (используя rollmean из пакета zoo):

library(data.table)
library(zoo)
test = data.table("values" = c(1,2,3,4,5,6,7,8, 9,10,11,12), 
                  "category" = c(1,1,1,1,1,1,2,2,2,2,2,2))
test[, paste0("ravg_", c("values")) := shift(lapply(
  .SD, rollmean, k = 2, na.pad = TRUE, align = "right"), 1), 
  .SDcols = c("values"), by = category]

    values category ravg_values
 1:      1        1          NA
 2:      2        1          NA
 3:      3        1         1.5
 4:      4        1         2.5
 5:      5        1         3.5
 6:      6        1         4.5
 7:      7        2          NA
 8:      8        2          NA
 9:      9        2         7.5
10:     10        2         8.5
11:     11        2         9.5
12:     12        2        10.5

Вы также можете легко адаптировать его к нескольким столбцам (см. https://stackoverflow.com/a/31482551/6871135)

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

Вы можете объединить функции shift и rollmeanr в пакетах data.table и zoo соответственно следующим образом.

library(data.table)
library(zoo)
test = data.table(values = 1:12, category = rep(1:2, each = 6))
test[, mg2 := shift(rollmeanr(values, 2, fill = NA)), category]

   values category      mg2
1:      1        1       NA
2:      2        1       NA
3:      3        1      1.5
4:      4        1      2.5
5:      5        1      3.5
6:      6        1      4.5
7:      7        2       NA
8:      8        2       NA
9:      9        2      7.5
10:     10       2      8.5
11:     11       2      9.5
12:     12       2     10.5
0 голосов
/ 29 декабря 2018

Я думаю, вы могли бы ускорить это, поместив сдвиг в функцию, которую вы используете для вычисления среднего значения, например

mav_shift <- function(x,n) if(length(x) >= n)stats::filter(shift(x),rep(1/n,n), sides=1) else NA_real_

По моему быстрому тесту это очень немного увеличиваетвремя, чтобы запустить функцию, и удаляет шаг создания новой переменной.Пожалуйста, проверьте, чтобы убедиться, что он работает, как ожидалось, но результаты ваших данных образца выглядят одинаково.

РЕДАКТИРОВАТЬ и более быстрое решение:

mav_shift <- function(x,n) {
  if(length(x) >= n) { 
    stats::filter(shift(x),rep(1/n,n), sides=1) 
  } else NA_real_

result <- by(test$values, test$category, mav_shift, n=2, simplify=T)
test$new <- as.vector(unlist(result))
...