r вычисление скользящего среднего с окном на основе значения (не числа строк или переменной даты / времени)
Я довольно новичок во всех пакетах, предназначенных для вычисления скользящих средних в R, и я надеюсь, что вы можете показать мне правильное направление.
У меня есть следующие данные в качестве примера:
ms <- c(300, 300, 300, 301, 303, 305, 305, 306, 308, 310, 310, 311, 312,
314, 315, 315, 316, 316, 316, 317, 318, 320, 320, 321, 322, 324,
328, 329, 330, 330, 330, 332, 332, 334, 334, 335, 335, 336, 336,
337, 338, 338, 338, 340, 340, 341, 342, 342, 342, 342)
correct <- c(1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0,
1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1,
1, 0, 0, 1, 0, 0, 1, 1, 0, 0)
library(dplyr)
df <- as.data.frame(data_frame(ms, correct))
"мс" - это моменты времени в миллисекундах, а "правильно" - правильно ли выполнено определенное действие (1= правильно, 0= не правильно).
Теперь моя цель состоит в том, чтобы я хотел рассчитать процент правильного (или среднего) по окнам заданного количества миллисекунд. Как видите, определенные моменты времени отсутствуют, а определенные моменты времени встречаются несколько раз. Поэтому я не хочу делать фильтр по номеру строки. Я рассмотрел некоторые пакеты, такие как "tidyquant", но мне кажется, что для пакетов такого типа нужна переменная времени / даты вместо числовой переменной, чтобы определить окно, по которому усредняются значения. Есть ли способ указать в окне числовое значение df$ms?
Большое спасибо!
3 ответа
Проверять:
library(dplyr)
# count the number of values per ms
df <- df %>%
group_by(ms) %>%
mutate(Nb.values = n())
# consider a window of 1 ms and compute the percentage for each window
df2 <- setNames(aggregate(correct ~ factor(df$ms, levels = as.character(seq(min(df$ms), max(df$ms), 1))),
df, sum),
c("ms", "Count.correct"))
# complete data frame (including unused levels)
df2 <- tidyr::complete(df2, ms)
df2$ms <- as.numeric(levels(df2$ms))[df2$ms]
df2 <- df2 %>% left_join(distinct(df[, c(1, 3)]), "ms")
# compute a rolling mean of the percentage of correct, with a width of 5
df2 %>%
mutate(Window = paste(ms, ms+4, sep = "-"), # add windows
Rolling.correct = zoo::rollapply(Count.correct, 5, sum, na.rm = T,
partial = TRUE, fill = NA, align = "left") /
zoo::rollapply(Nb.values, 5, sum, na.rm = T, partial = TRUE,
fill = NA, align = "left")) # add rolling mean
# A tibble: 43 x 5
ms Count.correct Nb.values Window Rolling.correct
<dbl> <dbl> <int> <chr> <dbl>
1 300 2 3 300-304 0.40
2 301 0 1 301-305 0.00
3 302 NA NA 302-306 0.25
4 303 0 1 303-307 0.25
5 304 NA NA 304-308 0.25
6 305 0 2 305-309 0.25
7 306 1 1 306-310 0.25
8 307 NA NA 307-311 0.00
9 308 0 1 308-312 0.20
10 309 NA NA 309-313 0.25
# ... with 33 more rows
Для полноты, вот ответ, который использует data.table для агрегирования в неравном объединении.
ОП пояснил в комментариях, что он ищет скользящее окно продолжительностью 5 мс, то есть окна, которые идут 300-304, 301-305, 302-306 и т. Д.
Поскольку в наборе данных OP отсутствует точка данных с 302 мс, недостающие значения необходимо заполнить.
library(data.table)
ws <- 5 # define window size
setDT(df)[SJ(start = seq(min(ms), max(ms), 1))[, end := start + ws - 1],
on = .(ms >= start, ms <= end),
.(share_correct = mean(correct)), by = .EACHI]
ms ms share_correct 1: 300 304 0.4000000 2: 301 305 0.0000000 3: 302 306 0.2500000 4: 303 307 0.2500000 5: 304 308 0.2500000 6: 305 309 0.2500000 7: 306 310 0.2500000 8: 307 311 0.0000000 9: 308 312 0.2000000 10: 309 313 0.2500000 11: 310 314 0.2000000 12: 311 315 0.4000000 13: 312 316 0.4285714 14: 313 317 0.2857143 15: 314 318 0.3750000 16: 315 319 0.4285714 17: 316 320 0.4285714 18: 317 321 0.4000000 19: 318 322 0.4000000 20: 319 323 0.2500000 21: 320 324 0.4000000 22: 321 325 0.3333333 23: 322 326 0.5000000 24: 323 327 1.0000000 25: 324 328 1.0000000 26: 325 329 0.5000000 27: 326 330 0.2000000 28: 327 331 0.2000000 29: 328 332 0.4285714 30: 329 333 0.3333333 31: 330 334 0.2857143 32: 331 335 0.5000000 33: 332 336 0.3750000 34: 333 337 0.2857143 35: 334 338 0.3000000 36: 335 339 0.3750000 37: 336 340 0.3750000 38: 337 341 0.4285714 39: 338 342 0.4000000 40: 339 343 0.4285714 41: 340 344 0.4285714 42: 341 345 0.4000000 43: 342 346 0.5000000 ms ms share_correct
Если OP заинтересован только в окнах, где в наборе данных существует начальная точка, код можно упростить:
setDT(df)[SJ(start = unique(ms))[, end := start + ws - 1],
on = .(ms >= start, ms <= end),
.(share_correct = mean(correct)), by = .EACHI]
ms ms share_correct 1: 300 304 0.4000000 2: 301 305 0.0000000 3: 303 307 0.2500000 4: 305 309 0.2500000 5: 306 310 0.2500000 6: 308 312 0.2000000 7: 310 314 0.2000000 8: 311 315 0.4000000 9: 312 316 0.4285714 10: 314 318 0.3750000 11: 315 319 0.4285714 12: 316 320 0.4285714 13: 317 321 0.4000000 14: 318 322 0.4000000 15: 320 324 0.4000000 16: 321 325 0.3333333 17: 322 326 0.5000000 18: 324 328 1.0000000 19: 328 332 0.4285714 20: 329 333 0.3333333 21: 330 334 0.2857143 22: 332 336 0.3750000 23: 334 338 0.3000000 24: 335 339 0.3750000 25: 336 340 0.3750000 26: 337 341 0.4285714 27: 338 342 0.4000000 28: 340 344 0.4285714 29: 341 345 0.4000000 30: 342 346 0.5000000 ms ms share_correct
В обоих случаях таблица данных, содержащая интервалы [start, end]
создается на лету и присоединяется к df
, Во время неравномерного соединения промежуточный результат сразу группируется по параметрам соединения (by = .EACHI
) и агрегировано. Обратите внимание, что закрытые интервалы используются, чтобы соответствовать ожиданиям ОП.
Вы можете попробовать "вырезать". Например, если вы хотите разделить мс так, чтобы у вас было всего 5 групп, то вы можете сделать:
df$ms_factor <- cut(df$ms, 5)
df_new <- df %>% group_by(ms_factor) %>% summarise(mean = mean(correct))
Это может быть сделано с base R
:
calculate_irregular_ratio <- function(df, time_var = "ms", window_var = 5, calc_var = "correct") {
sapply(df[[time_var]], function(x) round(mean(df[[calc_var]][df[[time_var]] >= (x - window_var) & df[[time_var]] <= x]), 2))
}
Вы можете применить его следующим образом (по умолчанию установлено значение 5 мс, вы можете изменить его, изменив window_var
параметр):
df$window_5_ratio <- calculate_irregular_ratio(df, window_var = 5)
В вашем случае вы получите (показаны только первые 10 строк):
ms correct window_5_ratio
1 300 1 0.67
2 300 1 0.67
3 300 0 0.67
4 301 0 0.50
5 303 0 0.40
6 305 0 0.29
7 305 0 0.29
8 306 1 0.20
9 308 0 0.20
10 310 0 0.17
Он ведет себя как скользящее среднее, однако не зависит от рядов. Вместо этого он берет окно на основе значений в столбце.
Например, в строках 6 и 7 он принимает значение текущей строки (305 мс) и вычисляет отношение для всех значений в кадре данных, которые составляют 305 и - 5, то есть между 305 и 300, получая 0,29.
Конечно, вы всегда можете изменить функцию самостоятельно, например, если вы хотите, чтобы окно 5 фактически означало 301 - 305, а не 300 - 305, вы можете установить + 1 после x - window_var
, так далее.