2015-06-24 14 views
7

Ich habe einen Datenrahmen wie:Wählen Zeilen innerhalb eines bestimmten Zeitbereich

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 

Was ich tun möchte, ist die + -10 Minuten Zeitrahmen zu wählen, wenn Category „1“ ist.

In diesem Fall, weil category = 1 bei 2013-11-02 08:07:25 AM ist, möchte ich alle Zeilen innerhalb 07:57:25 AM to 08:17:25 AM auswählen.

Was ist der beste Weg, um diese Aufgabe zu bewältigen?

Darüber hinaus gibt es vielleicht mehrere "1" für jeden Zeitrahmen. (Der eigentliche Datenrahmen mehr komplizieren, es mehrere Timestamp mit unterschiedlichen Benutzern zuordnen, dh es gibt eine andere Spalte mit dem Namen „Benutzer-ID“)

+5

Jetzt ist nur noch etwas episch benchamrk auf alle Antworten, die ich denke, zu tun übrig. –

+3

@DavidArenburg - Ich weiß, wo meine Antwort darauf fallen wird ;-) Ich verlasse mich darauf, dass die Rechenleistung exponentiell steigt oder dass die Leute alle paar Stunden einen Kaffee trinken müssen, während ihr Code läuft. – thelatemail

Antwort

10

In Basis R, ohne lubridate-ing oder irgendetwas anderes (unter der Annahme, dass Sie Zeitstempel auf ein POSIXct Objekt zu konvertieren gehen), wie:

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 

Wenn Sie schon mehr 1 ‚s, Sie haben über sie Schleife gefallen haben würden:

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

Dies scheint zu funktionieren:

Daten:

Per @DavidArenburg ‚s Kommentar (und wie in seiner Antwort erwähnt) der richtige Weg, um die Timestamp-Spalte in ein POSIXct Objekt zu konvertieren ist (falls nicht bereits geschehen):

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

Lösung:

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) 

Ausgang:

> 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 

Beachten Sie, dass der Ausgang bei mehreren Category==1 Reihen, meine Funktion der Ausgabe eine Liste (in diesem ocassion nur es hat ein Element) so ein do.call(rbind, pickrows(df)) benötigt werden alles in einem data.frame zu kombinieren.

+0

Hallo @DavidArenburg. Ja, in meiner R-Session habe ich aber da seine Zeitstempelspalte genau das voreingestellte "POSIXct" -Format hat gehe ich davon aus, dass es so auf seinem data.frame ist. In unserem Fall lesen wir es als Text. Deshalb ist 'dput' besser. – LyzandeR

+0

@DavidArenburg Ja, das war auf meinem Skript, als ich meine Antwort konstruierte und es funktioniert: 'df $ TimeStamp <- as.POSIXct (df $ TimeStamp)' – LyzandeR

+1

@DavidArenburg Ich werde die Annahme machen, dass sein Timestamp korrekt ist und es wird keine PM später (für mal). Sie fügen in Ihrer Antwort einen Datenreinigungsprozess hinzu, der zwar in Ordnung ist, aber nicht erforderlich ist. Es gibt keine Beweise, dass sein Zeitformat später falsch geht. – LyzandeR

7

Hier ist, wie ich würde nähern dies mit data.table::foverlaps

Zuerst konvertieren TimeStamp zu einem richtigen POSIXct

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

Dann werden wir eine temporäre Datensatz erstellen, in dem Category == 1 gegen beizutreten. Wir werden auch ein „Ende“ -Spalte und key sowohl von „Start“ und „Ende“ Spalten

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

Dann erstellen wir für df das gleiche tun, sondern 10 Minuten Intervalle

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

gesetzt werden dann werden alle noch zu tun ist durch angepasste Inzidenzen foverlaps und Teilmenge laufen

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

Hier ist meine Lösung mit dplyr und lubridate. Hier sind die Schritte:

finden, wo category ==1, dieser hinzufügen, + und - 10 Minuten mit den lubridate ‚s minutes mit einem einfachen c(-1, 1) * minutes(10)dannfilter mit auf der Grundlage des zwei Intervall in dem rang Vektor gespeichert Teilmenge.

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

Mit 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

Ich mag deine Lösung wirklich! Danke für den Beitrag, den ich nicht einmal über% -% wusste. – SabDeM

+0

thx. Sehr nützliche Kurzschrift zum Erstellen von Intervallen. –

3

Ich persönlich mag die Einfachheit in der Basis R Antwort von @thelatemail. Aber nur zum Spaß, werde ich eine andere Antwort mit Rolling Joins in data.table, im Gegensatz zu überlappenden Bereich verbindet Lösung zur Verfügung gestellt von @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 

, wenn Sie mehr als eine Zelle Dies sollte gut funktionieren, auch haben, wo Category 1 ist, AFAICT. Es wäre toll, dies als Feature für diese Art von Operationen für data.table ...

einzupacken PS: beziehen Sie sich auf die anderen Beiträge für die Konvertierung TimeStamp in POSIXct-Format.

Verwandte Themen