2015-06-24 4 views
7

У меня есть кадр данных, как:Выберите строки в пределах определенного интервала времени

TimeStamp     Category 

2013-11-02 07:57:18 AM   0 
2013-11-02 08:07:19 AM   0 
2013-11-02 08:07:21 AM   0 
2013-11-02 08:07:25 AM   1 
2013-11-02 08:07:29 AM   0 
2013-11-02 08:08:18 AM   0 
2013-11-02 08:09:20 AM   0 
2013-11-02 09:04:18 AM   0 
2013-11-02 09:05:22 AM   0 
2013-11-02 09:07:18 AM   0 

То, что я хочу сделать, это выбрать время + -10 минутные кадры, когда Category является «1».

Для этого случая, поскольку category = 1 находится в 2013-11-02 08:07:25 AM, я хочу выбрать все строки в пределах 07:57:25 AM to 08:17:25 AM.

Каков наилучший способ справиться с этой задачей?

дополнение, возможно, несколько «1» для каждого временного интервала. (Кадр реальных данных более усложнять, она содержит несколько TimeStamp с разными пользователями, то есть еще один столбец с именем «UserID»)

+5

Теперь все осталось сделать, это какой-то эпический скамейка на все ответы, которые я предполагаю. –

+3

@DavidArenburg - Я знаю, где мой ответ будет падать на это ;-) Я полагаюсь на вычислительную мощность, увеличивающуюся экспоненциально, или людям, которые должны хватать кофе каждые пару часов, пока их код работает. – thelatemail

ответ

10

В базовой R без lubridate-ков или что-либо другое (при условии, что вы собираетесь конвертировать TimeStamp в POSIXct объекта), как:

df$TimeStamp <- as.POSIXct(TimeStamp, format = "%Y-%m-%d %I:%M:%S %p") 
df[with(df, abs(difftime(TimeStamp[Category==1],TimeStamp,units="mins")) <= 10),] 

#   TimeStamp Category 
#2 2013-11-02 08:07:19  0 
#3 2013-11-02 08:07:21  0 
#4 2013-11-02 08:07:25  1 
#5 2013-11-02 08:07:29  0 
#6 2013-11-02 08:08:18  0 
#7 2013-11-02 08:09:20  0 

Если у вас есть несколько 1 «ы, вы должны были бы петлю над ним, как:

check <- with(df, 
    lapply(TimeStamp[Category==1], function(x) abs(difftime(x,TimeStamp,units="mins")) <= 10) 
) 
df[do.call(pmax, check)==1,] 
4

Это похоже на работу:

данные:

Согласно @DavidArenburg «s комментарий (и, как отметил в своем ответе) правильный способ преобразовать столбец временной метки в POSIXct объекта (если это не уже):

df$TimeStamp <- as.POSIXct(df$TimeStamp, format = "%Y-%m-%d %I:%M:%S %p") 

Решение:

library(lubridate) #for minutes 
library(dplyr)  #for between 
pickrows <- function(df) { 
    #pick category == 1 rows 
    df2 <- df[df$Category==1,] 
    #for each timestamp create two variables start and end 
    #for +10 and -10 minutes 
    #then pick rows between them 
    lapply(df2$TimeStamp, function(time) { 
     start <- time - minutes(10) 
     end <- time + minutes(10) 
     df[between(df$TimeStamp, start, end),] 
    }) 
} 

#run function 
pickrows(df) 

Выход:

> pickrows(df) 
[[1]] 
      TimeStamp Category 
2 2013-11-02 08:07:19  0 
3 2013-11-02 08:07:21  0 
4 2013-11-02 08:07:25  1 
5 2013-11-02 08:07:29  0 
6 2013-11-02 08:08:18  0 
7 2013-11-02 08:09:20  0 

Имейте в виду, что выход в случае кратных Category==1 строк, выход моей функции будет список (в этом ocassion он только один элемент), поэтому будет необходимо do.call(rbind, pickrows(df)) объединить все в один data.frame.

+0

Привет @DavidArenburg. Да, в моей сессии R у меня есть, но поскольку его столбец timestamp имеет точный формат 'POSIXct' по умолчанию, я предполагаю, что это похоже на его data.frame. В нашем случае мы читаем его как текст. Вот почему 'dput' лучше. – LyzandeR

+0

@DavidArenburg Да, это было на моем скрипте, когда я построил свой ответ, и он работает: 'df $ TimeStamp <- as.POSIXct (df $ TimeStamp)' – LyzandeR

+1

@DavidArenburg Я сделаю предположение, что его временная метка верна и будет no PM later on (for am times). Вы добавляете процесс очистки данных в свой ответ, который хорош, но это необязательно. Нет никаких доказательств того, что его формат времени будет ошибочным позже .. – LyzandeR

7

Вот как я бы подойти к этому с помощью data.table::foverlaps

Во-первых, конвертировать TimeStamp в надлежащее POSIXct

library(data.table) 
setDT(df)[, TimeStamp := as.POSIXct(TimeStamp, format = "%Y-%m-%d %I:%M:%S %p")] 

Затем мы создадим временный набор данных, где Category == 1 присоединиться к. Мы также создадим столбец «конца» и key оба «старт» и «конец» колонн

df2 <- setkey(df[Category == 1L][, TimeStamp2 := TimeStamp], TimeStamp, TimeStamp2) 

Тогда мы будем делать то же самое для df, но установим 10 минуты интервалов

setkey(df[, `:=`(start = TimeStamp - 600, end = TimeStamp + 600)], start, end) 

Тогда все, что осталось сделать, это запустить foverlaps и подмножество с помощью соответствующих инцидентов

indx <- foverlaps(df, df2, which = TRUE, nomatch = 0L)$xid 
df[indx, .(TimeStamp, Category)] 
#    TimeStamp Category 
# 1: 2013-11-02 08:07:19  0 
# 2: 2013-11-02 08:07:21  0 
# 3: 2013-11-02 08:07:25  1 
# 4: 2013-11-02 08:07:29  0 
# 5: 2013-11-02 08:08:18  0 
# 6: 2013-11-02 08:09:20  0 
1

Вот мое решение с dplyr и lubridate. Вот шаги:

Найти где category ==1, добавить к этому, + и - 10 минут с lubridate «s minutes с простым c(-1, 1) * minutes(10)затем с использованием filter на подмножества на основе два интервала хранящихся в rang векторе.

library(lubridate) 
library(dplyr) 
wi1 <- which(dat$Category == 1) 
rang <- dat$TimeStamp[wi1] + c(-1,1) * minutes(10) 
dat %>% filter(TimeStamp >= rang[1] & TimeStamp <= rang[2]) 
      TimeStamp Category 
1 2013-11-02 08:07:19  0 
2 2013-11-02 08:07:21  0 
3 2013-11-02 08:07:25  1 
4 2013-11-02 08:07:29  0 
5 2013-11-02 08:08:18  0 
6 2013-11-02 08:09:20  0 
4

Использование lubridate:

df$TimeStamp <- ymd_hms(df$TimeStamp) 
span10 <- (df$TimeStamp[df$Category == 1] - minutes(10)) %--% (df$TimeStamp[df$Category == 1] + minutes(10)) 
df[df$TimeStamp %within% span10,] 
      TimeStamp Category 
2 2013-11-02 08:07:19  0 
3 2013-11-02 08:07:21  0 
4 2013-11-02 08:07:25  1 
5 2013-11-02 08:07:29  0 
6 2013-11-02 08:08:18  0 
7 2013-11-02 08:09:20  0 
+0

Мне очень нравится ваше решение! Спасибо за сообщение, что я даже не знал о% -%. – SabDeM

+0

thx. очень полезная стенограмма для создания интервалов. –

3

Мне лично нравится простота в базе R ответа от @thelatemail. Но просто для удовольствия, я дам еще один ответ, используя скользящие соединения в data.table, в отличие от перекрывающиеся диапазоны объединяет решение предоставлено @DavidArenburg.

require(data.table) 
dt_1 = dt[Category == 1L] 
setkey(dt, TimeStamp) 

ix1 = dt[.(dt_1$TimeStamp - 600L), roll=-Inf, which=TRUE] # NOCB 
ix2 = dt[.(dt_1$TimeStamp + 600L), roll= Inf, which=TRUE] # LOCF 

indices = data.table:::vecseq(ix1, ix2-ix1+1L, NULL) # not exported function 
dt[indices] 
#    TimeStamp Category 
# 1: 2013-11-02 08:07:19  0 
# 2: 2013-11-02 08:07:21  0 
# 3: 2013-11-02 08:07:25  1 
# 4: 2013-11-02 08:07:29  0 
# 5: 2013-11-02 08:08:18  0 
# 6: 2013-11-02 08:09:20  0 

Это должно работать нормально, даже если у вас есть более одной ячейки, где Category является 1, AFAICT. Было бы здорово обернуть это как функцию для этого типа операций для data.table ...

PS: обратитесь к другим сообщениям для преобразования TimeStamp в формат POSIXct.

Смежные вопросы