Быстрое скользящее среднее + суммирование

В R я пытаюсь сделать очень быстрое скользящее среднее большого вектора (до 400 тыс. Элементов), используя разные ширины окна, затем для каждой ширины окна суммируем данные по максимуму каждого года. Надеюсь, приведенный ниже пример будет понятен. Я пробовал несколько подходов, и самый быстрый до сих пор, кажется, использует roll_mean из пакета RcppRoll для бегущей средней и aggregate для выбора максимума. Обратите внимание, что проблема с памятью вызывает беспокойство: приведенная ниже версия требует очень мало памяти, так как она выполняет одно скользящее среднее и агрегацию одновременно; это предпочтительнее.

#Example data frame of 10k measurements from 2001 to 2014
n <- 100000
df <- data.frame(rawdata=rnorm(n),
                 year=sort(sample(2001:2014, size=n, replace=TRUE))
                 ) 

ww <- 1:120 #Vector of window widths

dfsumm <- as.data.frame(matrix(nrow=14, ncol=121))
dfsumm[,1] <- 2001:2014
colnames(dfsumm) <- c("year", paste0("D=", ww))

system.time(for (i in 1:length(ww)) {
  #Do the rolling mean for this ww
  df$tmp <- roll_mean(df$rawdata, ww[i], na.rm=TRUE, fill=NA)
  #Aggregate maxima for each year
  dfsumm[,i+1] <- aggregate(data=df, tmp ~ year, max)[,2]
}) #28s on my machine
dfsumm

Это дает желаемый результат: data.frame с 15 строками (годы с 2001 по 2015) и 120 столбцами (ширина окна), содержащими максимум для каждого ww и для каждого года.

Тем не менее, это все еще занимает слишком много времени для вычисления (так как я должен вычислить тысячи из них). Я пытался поиграть с другими вариантами, а именно dplyr а также data.table, но я не смог найти что-то быстрее из-за недостатка знаний об этих пакетах.

Какой самый быстрый способ сделать это, используя одно ядро (код уже распараллелен в другом месте)?

2 ответа

Решение

Управление памятью, то есть выделение и копирование, убивает вас с вашим подходом.

Вот подход data.table, который назначается по ссылке:

library(data.table)
setDT(df)
alloc.col(df, 200) #allocate sufficient columns

#assign rolling means in a loop
for (i in seq_along(ww)) 
  set(df, j = paste0("D", i),  value = roll_mean(df[["rawdata"]], 
                                        ww[i], na.rm=TRUE, fill=NA))

dfsumm <- df[, lapply(.SD, max, na.rm = TRUE), by = year] #aggregate

Используя новый frollmean Функция (добавлена ​​в data.table v1.12.0) позволяет сделать следующее

th = setDTthreads(1L)
df[, paste0("D",ww) := frollmean(rawdata, ww, na.rm=TRUE)]
dfsumm <- df[, lapply(.SD, max, na.rm=TRUE), by=year]
setDTthreads(th)

Вам следует подумать о смещении параллелизма вниз, так как этот вариант использования хорошо распараллелен в frollmean, Также операция группировки использует параллельную обработку.

Одна проблема с производительностью, которую вы создаете, - это использование динамически растущего вектора с помощью cbind, Вы можете попытаться выделить ожидаемый размер заранее, а затем заполнить его, используя dfsumm[x] <- y,

Другие вопросы по тегам