Использование скользящего среднего для расчета в пределах окна значений

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

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

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

MS <- c(36148, 36753,37364,38062,38737,39580,40029,40387,41208,42006,42796, 43533,44274,44988,45696,46398,47079,47742,48429,49135,49861,50591,51324,52059)
HR <- c(84,84,84,84,84,96,84,84,96,84,84,96,84,84,96,84,84,84,84,84,84,84,84,84)

df <- data.frame(MS, HR)

Я пробовал несколько пакетов (а именно набор скользящих функций Zoo), но не смог применить их к этой проблеме.

Спасибо!


person KNichs    schedule 09.01.2020    source источник
comment
Не могли бы вы добавить ожидаемый результат?   -  person sm925    schedule 09.01.2020
comment
Приносим извинения за непонятность и благодарим за помощь!   -  person KNichs    schedule 10.01.2020
comment
Я добавил правки, которые должны прояснить, что я ищу - если это все еще неясно, я ищу что-то очень близкое к приведенному ниже ответу! Данные выборки - это короткая выборка из гораздо более длинной серии. И да, последнее значение было досадной опечаткой.   -  person KNichs    schedule 10.01.2020


Ответы (4)


Вариант с использованием неэквивалентного соединения в data.table, который также обрабатывает идентификатор:

library(data.table)
setDT(df)[, avgHR := 
    df[.(ID=ID, start=MS-15000, end=MS), on=.(ID, MS>=start, MS<=end),
        by=.EACHI, mean(HR)]$V1
]

выход:

    ID    MS HR    avgHR
 1:  1 36148 84 84.00000
 2:  1 36753 84 84.00000
 3:  1 37364 84 84.00000
 4:  1 38062 84 84.00000
 5:  1 38737 84 84.00000
 6:  1 39580 96 86.00000
 7:  1 40029 84 85.71429
 8:  1 40387 84 85.50000
 9:  1 41208 96 86.66667
10:  1 42006 84 86.40000
11:  1 42796 84 86.18182
12:  1 43533 96 87.00000
13:  1 44274 84 86.76923
14:  1 44988 84 86.57143
15:  1 45696 96 87.20000
16:  1 46398 84 87.00000
17:  1 47079 84 86.82353
18:  1 47742 84 86.66667
19:  1 48429 84 86.52632
20:  1 49135 84 86.40000
21:  1 49861 84 86.28571
22:  1 50591 84 86.18182
23:  1 51324 84 86.18182
24:  1 52059 84 86.18182
    ID    MS HR    avgHR

данные:

MS <- c(36148, 36753,37364,38062,38737,39580,40029,40387,41208,42006,42796, 43533,44274,44988,45696,46398,47079,47742,48429,49135,49861,50591,51324,52059)
HR <- c(84,84,84,84,84,96,84,84,96,84,84,96,84,84,96,84,84,84,84,84,84,84,84,84)

df <- data.frame(ID=1, MS, HR)
person chinsoon12    schedule 10.01.2020
comment
Whaou, большой поклонник решения. Спасибо. Это должно быть суперэффективно - person denis; 10.01.2020

rollapplyr в зоопарке принимает вектор ширины, и findInterval можно использовать для вычисления индекса MS 15 секунд назад, поэтому, если мы вычтем это из 1: n, мы получим w, количество позиций для усреднения. Какие именно интервалы производить, не обсуждается в вопросе, поэтому мы будем предполагать, что правый край каждого интервала находится во входной точке.

library(zoo)

w <- with(df, seq_along(MS) - findInterval(MS - 15000, MS))
transform(df, roll = rollapplyr(HR, w, mean, fill = NA))
person G. Grothendieck    schedule 09.01.2020

Я не совсем уверен, как вы хотите применить скользящее среднее 15, но вот один из способов сделать то, что, я думаю, вы ищете. Сначала мы подмножество данных, которые находятся между 7,5 с до и 7,5 с после, а затем берем среднее значение. Однако это будет иметь краевой эффект, поскольку до первого значения нет 7,5 с.

library(tidyverse)

roll_vec <- c()
for(i in 1:nrow(df)){
  ref <- df$MS[[i]]
  val <- df %>%
    filter(MS <= ref + 7500 & MS >= ref- 7500) %>%
    pull(HR) %>%
    mean
  roll_vec[[i]] <- val
}


df %>%
  mutate(roll_15s = roll_vec) 
#>       MS HR roll_15s
#> 1  36148 84 87.00000
#> 2  36753 84 87.00000
#> 3  37364 84 86.76923
#> 4  38062 84 86.57143
#> 5  38737 84 86.57143
#> 6  39580 96 86.57143
#> 7  40029 84 86.57143
#> 8  40387 84 86.57143
#> 9  41208 96 86.57143
#> 10 42006 84 86.57143
#> 11 42796 84 86.57143
#> 12 43533 96 86.57143
#> 13 44274 84 87.00000
#> 14 44988 84 87.27273
#> 15  4569 96 96.00000


df %>%
  mutate(roll_15s = roll_vec) %>%
  ggplot(aes(MS, HR))+
  geom_line()+
  geom_line(aes(y = roll_15s), color = "blue")

Обратите внимание, что на графике черная линия - это необработанные данные, а синяя линия - скользящее среднее за 15 секунд.

person AndS.    schedule 09.01.2020
comment
обычно средства прокрутки имеют меньшую точку, чем исходные данные, а ваши - нет ... - person denis; 10.01.2020

Одно из возможных решений:

library(magrittr)
start_range <- df$MS[df$MS < max(df$MS)-15000]

lapply(start_range,function(t){
  data.frame(MS = mean(df$MS[df$MS %between% c(t,t+15000)]),
             HR = mean(df$HR[df$MS %between% c(t,t+15000)]))
}) %>% Reduce(rbind,.)

        MS       HR
1 43218.00 86.18182
2 43907.82 86.18182
3 44603.55 86.18182
4 44948.29 86.28571
5 45673.38 86.33333

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

MS <- c(36148, 36753,37364,38062,38737,39580,40029,40387,41208,42006,42796, 43533,44274,44988,45696,46398,47079,47742,48429,49135,49861,50591,51324,52059,53289,54424)
HR <- c(84,84,84,84,84,96,84,84,96,84,84,96,84,84,96,84,84,84,84,84,84,84,84,84,85,88)
df <- data.frame(MS, HR)

Идея здесь состоит в том, чтобы вычислить для каждого значения MS среднее значение HR и время MS всех точек, имеющих время между этим значением (t по ширине) и 15 секундами после него. Я ограничиваю это диапазоном, в котором у меня есть значения, охватывающие 15: вектор start_range.

person denis    schedule 09.01.2020
comment
Мне очень нравится это решение. В конечном итоге у меня возникли проблемы из-за размера данных, но это очень полезно! - person KNichs; 10.01.2020
comment
Очевидно, это связано с неполнотой моего первоначального вопроса, но каково ваше мнение о том, как лучше всего сохранить третий столбец идентификатора (например, столбец № участника) с помощью этого решения? - person KNichs; 10.01.2020
comment
@KNichs да, мое решение неэффективно. Если вам нужно быстрое решение, решение chinsoon12 должно быть лучшим (и может легко обрабатывать идентификаторы), а решение Г. Гротендика, безусловно, более эффективно, чем мое. Для идентификаторов зависит, хотите ли вы перебрать идентификаторы (т.е. таблица в длинном формате) или нет. - person denis; 10.01.2020