Быстрый способ повторной фильтрации фрейма данных в R

У меня есть фрейм данных, состоящий из серии из более чем 100 000 наблюдений, каждое из которых имеет отметку времени POSIXt. Наблюдения расположены в порядке дата/время. Временные интервалы между наблюдениями различны. Мне нужно создать новый вектор во фрейме данных со значением для каждого наблюдения, которое представляет собой количество наблюдений в течение 5 минут после этого наблюдения, соответствующих некоторым критериям (в приведенном ниже примере целевой вектор должен иметь значение ИСТИНА. В реальной задаче критерии более сложны и зависят от значений количества векторов во фрейме данных).

На данный момент производительность ужасна для примера ниже:

library(plyr)
set.seed(1)
observations = 1000
startTime = Sys.time()

data <- as.data.frame(matrix(0, ncol = 3, nrow = observations))
colnames(data) <- c("timeStamp", "goal", "derived")
data$goal <- (runif(observations,0,1.1) > 1.0)
data$timeStamp <- runif(observations,1,90*60) + startTime
data <- arrange(data, timeStamp)
for(i in 1:nrow(data))
{
  data[i,"derived"] <- filter(data, goal == TRUE, timeStamp > data[[i,"timeStamp"]], timeStamp < (data[[i,"timeStamp"]] + (5 * 60))) %>% nrow
}
summary(data)

Можно ли выполнить операцию фильтрации данных без создания нового фрейма данных в цикле?

Есть ли лучший способ оптимизировать процесс?

В идеале, есть ли способ устранить зацикливание строк фрейма данных?


person Geoff S    schedule 24.11.2017    source источник
comment
Вы можете добавить set.seed(123) перед созданием данных, чтобы он всегда давал один и тот же набор данных (это должно облегчить проверку ответов)   -  person Tensibai    schedule 24.11.2017
comment
Я думаю, что скользящее соединение в пакете data.table может помочь   -  person Tensibai    schedule 24.11.2017


Ответы (1)


Один из подходов заключается в использовании неэквивалентного соединения:

library(data.table)
setorder(setDT(dat), timeStamp)
dat[, derived := dat[.(goal = TRUE, ts1 = timeStamp, ts2 = timeStamp + 5 * 60), 
    on = .(goal, timeStamp >= ts1, timeStamp <= ts2), .N, by = .EACHI]$N][]
dat
                timeStamp  goal derived
   1: 2017-11-24 07:19:47  TRUE       3
   2: 2017-11-24 07:19:48 FALSE       2
   3: 2017-11-24 07:19:51 FALSE       2
   4: 2017-11-24 07:20:04 FALSE       2
   5: 2017-11-24 07:20:06 FALSE       2
  ---                                  
 996: 2017-11-24 08:49:06  TRUE       2
 997: 2017-11-24 08:49:13 FALSE       1
 998: 2017-11-24 08:49:16  TRUE       1
 999: 2017-11-24 08:49:25 FALSE       0
1000: 2017-11-24 08:49:40 FALSE       0

Воспроизводимые данные

observations <- 1000
startTime <- as.POSIXct("2017-11-24 07:19:44")
set.seed(123L)
dat <- data.frame(
  timeStamp = runif(observations, 1, 90*60) + startTime,
  goal = runif(observations, 0, 1.1) > 1.0
)
person Uwe    schedule 24.11.2017
comment
Документация по таблицам данных полезна для понимания этого решения. . - person Geoff S; 26.11.2017