Подмножества наблюдений, которые отличаются как минимум на 30 минут

у меня естьdata.table (~ 30 миллионов строк), состоящий изdatetime колонка вPOSIXct формат,id столбец и несколько других столбцов (в примере я просто оставил один неактуальный столбецx продемонстрировать наличие других столбцов, которые необходимо сохранить).dput находится внизу поста.

head(DT)
#              datetime          x id
#1: 2016-04-28 16:20:18 0.02461368  1
#2: 2016-04-28 16:41:34 0.88953932  1
#3: 2016-04-28 16:46:07 0.31818101  1
#4: 2016-04-28 17:00:56 0.14711365  1
#5: 2016-04-28 17:09:11 0.54406602  1
#6: 2016-04-28 17:39:09 0.69280341  1

Q: для каждогоidМне нужно подгруппировать только те наблюдения, которые отличаются более чем на 30 минут времени. Что может быть эффективнымdata.table подход к этому (если возможно, без обширных циклов)?

Логика также может быть описана как (как в моем комментарии ниже):

По id всегда сохраняется первый ряд. Следующий ряд, который должен составлять не менее 30 минут после первого, также сохраняется. Предположим, что строка, которую нужно сохранить, - это строка 4. Затем вычислите разницу во времени между строкой 4 и строками 5: n и оставьте первую, которая отличается более чем на 30 минут, и т. Д.

В dput ниже я добавил столбецkeep чтобы указать, какие строки следует сохранить в этом примере, поскольку они отличаются более чем на 30 минут от предыдущего наблюдения, которое сохраняется для каждого идентификатора. Сложность состоит в том, что, по-видимому, необходимо итеративно рассчитывать разницу во времени (или, по крайней мере, в настоящее время я не могу придумать более эффективного подхода).

library(data.table)
DT <- structure(list(
  datetime = structure(c(1461853218.81561, 1461854494.81561, 
    1461854767.81561, 1461855656.81561, 1461856151.81561, 1461857949.81561, 
    1461858601.81561, 1461858706.81561, 1461859078.81561, 1461859103.81561, 
    1461852799.81561, 1461852824.81561, 1461854204.81561, 1461855331.81561, 
    1461855633.81561, 1461856311.81561, 1461856454.81561, 1461857177.81561, 
    1461858662.81561, 1461858996.81561), class = c("POSIXct", "POSIXt")), 
  x = c(0.0246136845089495, 0.889539316063747, 0.318181007634848, 
  0.147113647311926, 0.544066024711356, 0.6928034061566, 0.994269776623696, 
  0.477795971091837, 0.231625785352662, 0.963024232536554, 0.216407935833558, 
  0.708530468167737, 0.758459537522867, 0.640506813768297, 0.902299045119435, 
  0.28915973729454, 0.795467417687178, 0.690705278422683, 0.59414202044718, 
  0.655705799115822), 
  id = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L), 
  keep = c(TRUE, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, 
           FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, TRUE)), 
  .Names = c("datetime", "x", "id", "keep"), 
  row.names = c(NA, -20L), 
  class = c("data.table", "data.frame"))

setkey(DT, id, datetime)
DT[, difftime := difftime(datetime, shift(datetime, 1L, NA,type="lag"), units = "mins"),
   by = id]
DT[is.na(difftime), difftime := 0]
DT[, difftime := cumsum(as.numeric(difftime)), by = id]

Объяснениеkeep колонка:

Строки 2: 3 отличаются менее чем на 30 минут от строки 1 -> удалитьРяд 4 отличается более чем на 30 минут от ряда 1 -> сохранитьРяд 5 dufferes менее чем за 30 минут от ряда 4 -> удалитьРяд 6 отличается более чем на 30 минут от ряда 4 -> сохранить...

Желаемый вывод:

desiredDT <- DT[(keep)]

Спасибо за три экспертных ответа, которые я получил. Я проверил их на 1 и 10 миллионах строк данных. Вот выдержка из тестов.

а) 1 миллион строк

microbenchmark(frank(DT_Frank), roland(DT_Roland), eddi1(DT_Eddi1), eddi2(DT_Eddi2), 
               times = 3L, unit = "relative")
#Unit: relative
#              expr       min        lq      mean    median        uq      max neval
#   frank(DT_Frank)  1.286647  1.277104  1.185216  1.267769  1.140614 1.036749     3
# roland(DT_Roland)  1.000000  1.000000  1.000000  1.000000  1.000000 1.000000     3
#   eddi1(DT_Eddi1) 11.748622 11.697409 10.941792 11.647320 10.587002 9.720901     3
#   eddi2(DT_Eddi2)  9.966078  9.915651  9.210168  9.866330  8.877769 8.070281     3

б) 10 миллионов строк

microbenchmark(frank(DT_Frank), roland(DT_Roland), eddi1(DT_Eddi1), eddi2(DT_Eddi2), 
                times = 3L, unit = "relative")
#Unit: relative
#              expr       min        lq      mean    median        uq       max neval
#   frank(DT_Frank)  1.019561  1.025427  1.026681  1.031061  1.030028  1.029037     3
# roland(DT_Roland)  1.000000  1.000000  1.000000  1.000000  1.000000  1.000000     3
#   eddi1(DT_Eddi1) 11.567302 11.443146 11.301487 11.323914 11.176515 11.035143     3
#   eddi2(DT_Eddi2)  9.796800  9.693823  9.526193  9.594931  9.398969  9.211019     3

По-видимому, подход @ Frank к data.table и решение @ Roland на основе Rcpp схожи по производительности с Rcpp, имеющим небольшое преимущество, в то время как подходы @ eddi были все еще быстрыми, но не такими эффективными, как другие.

Однако, когда я проверил на равенство решений, я обнаружил, что подход @ Roland имеет немного другой результат, чем другие:

а) 1 миллион строк

all.equal(frank(DT_Frank), roland(DT_Roland))
#[1] "Component “datetime”: Numeric: lengths (982228, 982224) differ"
#[2] "Component “id”: Numeric: lengths (982228, 982224) differ"      
#[3] "Component “x”: Numeric: lengths (982228, 982224) differ"
all.equal(frank(DT_Frank), eddi1(DT_Eddi1))
#[1] TRUE
all.equal(frank(DT_Frank), eddi2(DT_Eddi2))
#[1] TRUE

б) 10 миллионов строк

all.equal(frank(DT_Frank), roland(DT_Roland))
#[1] "Component “datetime”: Numeric: lengths (9981898, 9981891) differ"
#[2] "Component “id”: Numeric: lengths (9981898, 9981891) differ"      
#[3] "Component “x”: Numeric: lengths (9981898, 9981891) differ"       
all.equal(frank(DT_Frank), eddi1(DT_Eddi1))
#[1] TRUE
all.equal(frank(DT_Frank), eddi2(DT_Eddi2))
#[1] TRUE

Мое текущее предположение состоит в том, что эта разница может быть связана с тем, составляет ли разница> 30 минут или> = 30 минут, хотя я еще не уверен в этом.

Заключительная мысль: я решил использовать решение @ Frank по двум причинам: 1. оно работает очень хорошо, почти совпадает с решением Rcpp, и 2. оно не требует другого пакета, с которым я еще не очень хорошо знаком ( все равно использую data.table)

Ответы на вопрос(3)

Ваш ответ на вопрос