Скользящее среднее по уникальной дате с несколькими наблюдениями за дату

#r #dplyr #moving-average

#r #dplyr #скользящее среднее

Вопрос:

У меня есть набор данных, который может содержать НЕСКОЛЬКО наблюдений за дату. Таким образом, может быть 5 наблюдений на date1, 2 наблюдения на date2 и 1 наблюдение в group3.

Я хочу рассчитать скользящее среднее — по дате — и, что важно, не суммируя / не уменьшая количество строк. То есть в приведенном выше примере у меня все равно было бы 8 строк данных, а в столбце рядом с ним у меня была бы скользящая средняя цена на эту дату. Я нахожу это сложным, потому что, когда я использую типичную скользящую функцию из ZOO package, она выполняется построчно, и я не знаю, как получитьэто пропустить по ДАТЕ

например, первым шагом обычно было бы:

 df %>% 
 groupy_by(DATE) %>% 
 summarise(mean_daily_price = mean(price)) %>% 
 ungroup() %>% 
 arrange(Date) %>% 
 mutate( ra = rollapply(price, 2, mean, partial=T)   
  

— но подведение итогов заставляет меня терять строки.

       library(dplyr)
      library(zoo)


            DF = structure(list(Date = c("Jan-13", "Jan-13", "Jan-13", "Jan-13",  "Jan-13", "Jul-14", "Jul-14", "Oct-16"), Price = c(100L, 200L,  300L, 1000L, 400L, 150L, 50L, 600L), Average.by.Date = c(400L,  400L, 400L, 400L, 400L, 100L, 100L, 600L), Moving_Average_Size_2 = c(NA,  NA, NA, NA, NA, 250L, 250L, 350L)), .Names = c("Date", "Price", "Average.by.Date", "Moving_Average_Size_2"), class = "data.frame", row.names = c(NA, 
-8L))
  

Комментарии:

1. Ваш вопрос немного неясен. Каков ваш ожидаемый результат? Что вы подразумеваете под «пропуском по дате»?

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

Ответ №1:

В приведенном ниже коде мы используем mutate вместо summarise добавить mean_daily_price , чтобы сохранить все строки фрейма данных. Затем в финале mutate мы работаем rollapply только с уникальными значениями mean_daily_price , но затем используем table и rep повторяем вывод rollapply по количеству строк для каждого Date .

 DF %>% 
  arrange(Date) %>% 
  group_by(Date) %>% 
  mutate(mean_daily_price = mean(Price)) %>% 
  ungroup() %>% 
  mutate(ra = rep(rollapply(unique(mean_daily_price), 2, mean, fill=NA, align="right"), 
                  table(Date)[order(unique(Date))]))
  
     Date Price Average.by.Date Moving_Average_Size_2 mean_daily_price    ra
1 Jan-13   100             400                    NA              400    NA
2 Jan-13   200             400                    NA              400    NA
3 Jan-13   300             400                    NA              400    NA
4 Jan-13  1000             400                    NA              400    NA
5 Jan-13   400             400                    NA              400    NA
6 Jul-14   150             100                   250              100   250
7 Jul-14    50             100                   250              100   250
8 Oct-16   600             600                   350              600   350
  

Комментарии:

1. Извините, но это неправильно. Здесь правильным ответом является MOVING_AVERAGE_SIZE_2 (или НУЛИ могут быть = 400) — здесь это менее важно, поскольку на данный момент только 1 дата наблюдения. Кроме того, возможно, я что-то упускаю, но уникальное (mean_daily_prrice) — что, если у меня одинаковая средняя цена на несколько дат?

Ответ №2:

Я думаю, что ваш самый безопасный подход должен быть двухэтапным процессом — вычислите скользящие средние Date , а затем объедините их обратно (все еще используя dplyr здесь)

 rolledAvg <-
  DF %>%
  group_by(Date) %>%
  summarise(mean_daily_price = mean(Price)) %>%
  ungroup() %>%
  arrange(Date) %>%
  mutate( ra = rollapply(mean_daily_price
                         , 2
                         , mean
                         , partial=T
                         , fill = NA))

left_join(DF, rolledAvg)
  

дает:

     Date Price Average.by.Date Moving_Average_Size_2 mean_daily_price  ra
1 Jan-13   100             400                    NA              400 250
2 Jan-13   200             400                    NA              400 250
3 Jan-13   300             400                    NA              400 250
4 Jan-13  1000             400                    NA              400 250
5 Jan-13   400             400                    NA              400 250
6 Jul-14   150             100                   250              100 350
7 Jul-14    50             100                   250              100 350
8 Oct-16   600             600                   350              600 600
  

Я вижу в комментарии к другому ответу, что вы не считаете, что первое ra должно быть 250 — если это так, измените расчет в своем вызове на rollapply . Прямо сейчас, похоже, он ведет себя так, как ожидалось / задокументировано. Итак, если вы хотите что-то другое, вам нужно будет объяснить желаемое изменение (возможно, отдельный вопрос).

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

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

Комментарии:

1. Вместо отдельного left_join , вы также можете добавить right_join(DF) в цепочку.

2. Попробуйте rollapplyr вместо rollapply . Также, если левое соединение записывается как left_join(x = DF) , тогда оно может быть добавлено в конце конвейера.

3. Оба @eipi10 и @G Grothendieck правы в том, что вызов (или left_join или right_join ) может быть добавлен в конце конвейера / цепочки. Я оставил их разделенными здесь, чтобы подчеркнуть, что *_join это была уникальная часть ответа (и что конструкция в вопросе может быть понята как правильная , только первый шаг).