2015-06-24 11 views
7

Ho un frame di dati come:Selezionare righe all'interno di un determinato intervallo di tempo

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 

Quello che voglio fare è quello di selezionare i time frame + -10 minuti quando Category è "1".

Per questo caso, poiché category = 1 è 2013-11-02 08:07:25 AM, desidero selezionare tutte le righe entro 07:57:25 AM to 08:17:25 AM.

Qual è il modo migliore per gestire questa attività?

Inoltre, c'è forse più "1" per ogni periodo di tempo. (Il telaio vero e proprio dei dati è più complicato, contiene TimeStamp multipla con diversi utenti, vale a dire non v'è un'altra colonna denominata "UserID")

+5

Ora tutto quello che resta da fare è un po 'di benchamrk epico su tutte le risposte che immagino. –

+3

@DavidArenburg - So dove cadrà la mia risposta ;-) Sto facendo affidamento sulla potenza di calcolo in aumento esponenziale, o sulle persone che hanno bisogno di prendere un caffè ogni paio d'ore mentre il loro codice funziona. – thelatemail

risposta

10

In base di R, senza lubridate-zione o qualsiasi altra cosa (supponendo che si sta andando per convertire TimeStamp a un oggetto POSIXct), come:

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 

Se hai l' più 1, dovreste anello su di esso come:

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

Questo sembra funzionare:

dati:

Come per @DavidArenburg 's commento (e come detto nella sua risposta) il modo giusto per convertire la colonna timestamp in un oggetto POSIXct è (se non già):

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

Soluzione:

012.351.
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) 

uscita:

> 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 

Tenete a mente che l'uscita in caso di molteplici Category==1 righe, l'output di mia funzione sarà una lista (in questa ocasione solo è un elemento) in modo sarà necessaria una do.call(rbind, pickrows(df)) combinare tutto in un data.frame.

+0

Ciao @DavidArenburg. Sì, nella mia sessione R ho ma dal momento che la sua colonna timestamp ha il formato esatto di default 'POSIXct' presumo che sia così sul suo data.frame. Nel nostro caso lo leggiamo come testo. Questo è il motivo per cui 'dput' è migliore. – LyzandeR

+0

@DavidArenburg Sì, questo era sul mio script quando ho costruito la mia risposta e funziona: 'df $ TimeStamp <- as.POSIXct (df $ TimeStamp)' – LyzandeR

+1

@DavidArenburg Farò il presupposto che il suo timestamp sia corretto e che ci sarà nessun PM più tardi (per le ore del mattino). Stai aggiungendo un processo di pulizia dei dati nella tua risposta che va bene ma non è necessario. Non ci sono prove del fatto che il suo formato orario vada storto in seguito .. – LyzandeR

7

Ecco come vorrei affrontare questo utilizzando data.table::foverlaps

In primo luogo, la conversione TimeStamp ad una vera e propria POSIXct

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

Poi creeremo un insieme di dati temporanea in cui Category == 1 ad unirsi contro. Ci sarà anche possibile creare una colonna "fine" e key da entrambi colonne "end" "start" e

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

Poi, faremo lo stesso per df ma imposteremo 10 minuti intervalli

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

poi, tutto resta da fare è quello di eseguire foverlaps e sottoinsieme da incidenze corrispondenti

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

Ecco la mia soluzione con dplyr e lubridate. Ecco i passaggi:

Trova dove category ==1, aggiungere a questo, + e - 10 minuti con s' minutes con un semplice c(-1, 1) * minutes(10)poi utilizzando filter il lubridate al sottoinsieme basa sul due dell'intervallo memorizzato nel rang vettoriale.

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

Utilizzando 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

Mi piace molto la tua soluzione! Grazie per aver postato non sapevo nemmeno di% -%. – SabDeM

+0

thx. abbreviazione molto utile per la creazione di intervalli. –

3

Personalmente mi piace la semplicità nella risposta di base R di @thelatemail. Ma solo per divertimento, fornirò un'altra risposta utilizzando join mobili in data.table, al contrario di join join soluzione fornita da @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 

Questo dovrebbe funzionare bene, anche se hai più di una cella in cui è Category 1, AFAICT. Sarebbe bello racchiuderlo come una funzionalità per questo tipo di operazioni per data.table ...

PS: fare riferimento agli altri post per la conversione di TimeStamp in formato POSIXct.

Problemi correlati