2013-04-05 12 views
16

Ciao Cerco un modo efficiente di selezione POSIXct righe da una data.table tale che il tempo del giorno è inferiore a dire 12:00:00 (si noti che millisecondo non è richiesto, in modo che possiamo utilizzare ITime per esempio)efficiente confronto tra POSIXct in data.table

set.seed(1); N = 1e7; 
DT = data.table(dts = .POSIXct(1e5*rnorm(N), tz="GMT")) 
DT 
           dts 
#  1: 1969-12-31 06:35:54.618925 
#  2: 1970-01-01 05:06:04.332422 
#  ---       
# 9999999: 1970-01-03 00:37:00.035565 
#10000000: 1969-12-30 08:30:23.624506 

una soluzione (il problema qui è che il cast potrebbe essere costosa se N è grande)

f <- function(t, st, et) {time <- as.ITime(t); return(time>=as.ITime(st) & time<=as.ITime(et))} 
P <- function(t, s) { #geekTrader solution 
    ep <- .parseISO8601(s) 
    if(grepl('T[0-9]{2}:[0-9]{2}:[0-9]{2}/T[0-9]{2}:[0-9]{2}:[0-9]{2}', s)){ 
     first.time <- as.double(ep$first.time) 
     last.time <- as.double(ep$last.time)-31449600 
     SecOfDay <- as.double(t) %% 86400 
     return(SecOfDay >= first.time & SecOfDay <= last.time) 
    } else { 
     return(t >= ep$first.time & t <= ep$last.time)  
    } 
} 

Visualizzazione rapida sulla perf

system.time(resf <- DT[f(dts,'00:00:00','11:59:59')]) 
    user system elapsed 
    1.01 0.28 1.29 
system.time(resP <- DT[P(dts,'T00:00:00/T11:59:59')]) 
    user system elapsed 
    0.64 0.13 0.76 

identical(resf,resP) 
[1] TRUE 
+0

Sei felice abbastanza per creare una colonna 'itime' e chiave da questo? – mnel

+0

@mnel: sì, quindi facciamo una ricerca binaria ... – statquant

+2

Non dovresti davvero modificare le risposte delle persone nella tua domanda – GSee

risposta

7
P <- function(t, s) { 
    ep <- .parseISO8601(s) 

    if(grepl('T[0-9]{2}:[0-9]{2}:[0-9]{2}/T[0-9]{2}:[0-9]{2}:[0-9]{2}', s)){ 
    first.time <- as.double(ep$first.time) 
    last.time <- as.double(ep$last.time)-31449600 
    SecOfDay <- as.double(t) %% 86400 
    return(SecOfDay >= first.time & SecOfDay <= last.time) 

    } else { 
    return(t >= ep$first.time & t <= ep$last.time)  
    } 

} 

F <- function(t, st, et) { 
    time <- as.ITime(t) 
    return(time>=as.ITime(st) & time<=as.ITime(et)) 
} 


Sys.setenv(TZ='GMT') 
N = 1e7; 
set.seed(1); 

DT <- data.table(dts = .POSIXct(1e5*rnorm(N), tz="GMT")) 


system.time(resP <- DT[P(dts, 'T00:00:00/T12:00:00'), ]) 
## user system elapsed 
## 1.11 0.11 1.22 
system.time(resF <- DT[F(dts,'00:00:00','12:00:00')]) 
## user system elapsed 
## 2.22 0.29 2.51 

resP 
##       dts 
##  1: 1969-12-31 06:35:54 
##  2: 1970-01-01 05:06:04 
##  3: 1969-12-31 00:47:17 
##  4: 1970-01-01 09:09:10 
##  5: 1969-12-31 01:12:33 
##  ---      
##5000672: 1970-01-01 06:08:15 
##5000673: 1970-01-01 05:02:27 
##5000674: 1969-12-31 02:25:24 
##5000675: 1970-01-03 00:37:00 
##5000676: 1969-12-30 08:30:23 
resF 
##       dts 
##  1: 1969-12-31 06:35:54 
##  2: 1970-01-01 05:06:04 
##  3: 1969-12-31 00:47:17 
##  4: 1970-01-01 09:09:10 
##  5: 1969-12-31 01:12:33 
##  ---      
##5000672: 1970-01-01 06:08:15 
##5000673: 1970-01-01 05:02:27 
##5000674: 1969-12-31 02:25:24 
##5000675: 1970-01-03 00:37:00 
##5000676: 1969-12-30 08:30:23 

#Check the correctness 
resP[,list(mindts=max(dts)),by=list(as.Date(dts))] 
##  as.Date    mindts 
## 1: 1969-12-31 1969-12-31 12:00:00 
## 2: 1970-01-01 1970-01-01 12:00:00 
## 3: 1969-12-29 1969-12-29 12:00:00 
## 4: 1970-01-02 1970-01-02 12:00:00 
## 5: 1969-12-30 1969-12-30 12:00:00 
## 6: 1970-01-03 1970-01-03 12:00:00 
## 7: 1970-01-04 1970-01-04 11:59:59 
## 8: 1970-01-05 1970-01-05 11:59:45 
## 9: 1969-12-28 1969-12-28 12:00:00 
##10: 1969-12-27 1969-12-27 11:59:21 
##11: 1970-01-06 1970-01-06 10:53:21 
##12: 1969-12-26 1969-12-26 10:15:03 
##13: 1970-01-07 1970-01-07 08:21:55 
resF[,list(mindts=max(dts)),by=list(as.Date(dts))] 
##  as.Date    mindts 
## 1: 1969-12-31 1969-12-31 12:00:00 
## 2: 1970-01-01 1970-01-01 12:00:00 
## 3: 1969-12-29 1969-12-29 12:00:00 
## 4: 1970-01-02 1970-01-02 12:00:00 
## 5: 1969-12-30 1969-12-30 12:00:00 
## 6: 1970-01-03 1970-01-03 12:00:00 
## 7: 1970-01-04 1970-01-04 11:59:59 
## 8: 1970-01-05 1970-01-05 11:59:45 
## 9: 1969-12-28 1969-12-28 12:00:00 
##10: 1969-12-27 1969-12-27 11:59:21 
##11: 1970-01-06 1970-01-06 10:53:21 
##12: 1969-12-26 1969-12-26 10:15:03 
##13: 1970-01-07 1970-01-07 08:21:55 

Ora alcuni demo di bello sottoinsiemi xts stile

DT[P(dts, '1970')] 
##       dts 
##  1: 1970-01-01 05:06:04 
##  2: 1970-01-02 20:18:48 
##  3: 1970-01-01 09:09:10 
##  4: 1970-01-01 13:32:22 
##  5: 1970-01-01 20:30:32 
##  ---      
##5001741: 1970-01-02 15:51:12 
##5001742: 1970-01-03 01:41:31 
##5001743: 1970-01-01 06:08:15 
##5001744: 1970-01-01 05:02:27 
##5001745: 1970-01-03 00:37:00 
DT[P(dts, '197001')] 
##       dts 
##  1: 1970-01-01 05:06:04 
##  2: 1970-01-02 20:18:48 
##  3: 1970-01-01 09:09:10 
##  4: 1970-01-01 13:32:22 
##  5: 1970-01-01 20:30:32 
##  ---      
##5001741: 1970-01-02 15:51:12 
##5001742: 1970-01-03 01:41:31 
##5001743: 1970-01-01 06:08:15 
##5001744: 1970-01-01 05:02:27 
##5001745: 1970-01-03 00:37:00 
DT[P(dts, '19700102')] 
##       dts 
##  1: 1970-01-02 20:18:48 
##  2: 1970-01-02 17:59:38 
##  3: 1970-01-02 07:14:53 
##  4: 1970-01-02 02:13:03 
##  5: 1970-01-02 01:31:37 
##  ---      
##1519426: 1970-01-02 11:25:24 
##1519427: 1970-01-02 10:00:21 
##1519428: 1970-01-02 05:21:25 
##1519429: 1970-01-02 05:11:26 
##1519430: 1970-01-02 15:51:12 
DT[P(dts, '19700102 00:00:00/19700103 12:00:00')] 
##       dts 
##  1: 1970-01-02 20:18:48 
##  2: 1970-01-02 17:59:38 
##  3: 1970-01-02 07:14:53 
##  4: 1970-01-02 02:13:03 
##  5: 1970-01-02 01:31:37 
##  ---      
##1785762: 1970-01-02 05:21:25 
##1785763: 1970-01-02 05:11:26 
##1785764: 1970-01-02 15:51:12 
##1785765: 1970-01-03 01:41:31 
##1785766: 1970-01-03 00:37:00 

#Check the correctness again 
DT[P(dts, '19700102 00:00:00/19700103 12:00:00'), max(dts)] 
##[1] "1970-01-03 12:00:00 GMT" 
DT[P(dts, '19700102 00:00:00/19700103 12:00:00'), min(dts)] 
##[1] "1970-01-02 00:00:00 GMT" 
+0

Giusto o sbagliato, tendo a usare HHMMSS memorizzato come intero semplice , cioè nessuna epoca, nessuna classe –

+0

Bello, il tempo di memorizzazione come secondi e '%% 86400' sembra una soluzione semplice e diretta, e molto probabilmente molto veloce – Aaron

+0

@statquant Sembra funzionare bene dopo aver sostituito' as.integer' di 'as.double' –

3

Ecco un modo che utilizza alcune delle funzionalità da xts per compiere ciò che si desidera. Questa non è una soluzione eccezionale perché gli oggetti xts devono essere ordinati per ora, ma non è necessario che gli oggetti data.table. Inoltre, potrebbe non essere terribilmente veloce poiché è in corso un lavoro ridondante da xts e data.table. Ciò nonostante, ho pensato che potesse essere interessante.

library(data.table) 
library(xts) 
set.seed(1); N = 1e5; 
# I tweaked the following line to make this reproducible in other timezones. 
DT = data.table(dts = .POSIXct(1e5*rnorm(N), tz="GMT")) 
setkey(DT, dts) # must sort on time first so that the `xts` object we're about 
       # to create has the same order 
DT[, XTS:=xts(rep(NA, .N), dts)] # add a dummy xts object as a column 
DT[XTS["T00:00:00/T11:59:59.999999", which=TRUE]][, list(dts)] 
         dts 
    1: 1969-12-27 00:28:41 
    2: 1969-12-27 00:34:00 
    3: 1969-12-27 03:11:21 
    4: 1969-12-27 04:20:27 
    5: 1969-12-28 00:00:21 
    ---      
49825: 1970-01-05 08:05:22 
49826: 1970-01-05 09:35:32 
49827: 1970-01-05 09:49:49 
49828: 1970-01-05 09:50:27 
49829: 1970-01-05 11:07:32 

È possibile che questo utilizza una stringa di sottoinsiemi in stile xts per ottenere le righe in cui il tempo è 0:00:00-00:00:00 per tutti i giorni. L'utilizzo di which=TRUE restituisce il numero di riga invece dei dati da quella riga, in modo da poter suddividere lo data.table in base a tali righe.

È possibile utilizzare una stringa come "1970-01-01" per ottenere tutti i dati da quel giorno o "1970-01" per ottenere tutti i dati da gennaio 1970 o "1970-01-01/1970-01 -02 "per ottenere tutte le righe da quei due giorni.

+0

Sì, come si fa notare, l'ordine (operazione 'setkey') sembra essere costoso su tabelle più grandi (1e7) e per qualche motivo, il risultato del test con 1e7 righe (seed = 1) dà circa 60 righe in più nella soluzione (forse ha a che fare con i millisecondi?) – Arun

+0

@Arun "T00: 00: 00/T12: 00: 00" includerà anche le ore fino a 12:00 12:00 escluso. Ho modificato per utilizzare una stringa di sottoinsieme più precisa che fornisce lo stesso numero di righe dell'OP. – GSee

+0

Grazie a Gsee, anche se trovo questo x2 tempo meno efficiente del metodo basato (funzione 'f'), e questo senza l'ordinamento – statquant

4

Il modo canonico per fare ciò è convertire in POSIXlt ed estrarre il componente dell'ora.

hour(as.POSIXlt(DT$dts, "GMT")) < 12 

Questo sembra essere paragonabile in termini di prestazioni alle altre tecniche discusse (ed è più facile da capire).

+0

+1 Sicuramente questo è solo il modo più veloce? Se no, è sicuramente il più comprensibile per me. Ha ridotto 1e7 righe a ~ 5e6 righe (quello che mi aspetterei dato 'rnorm') in ~ 1 secondo. E non richiede altro che la base R. –

+0

'data.table :: hour' sta già usando' as.POSIXlt', quindi puoi scrivere qualcosa come 'DT [, morning: = (hour (DT $ dts) <12)] '. –

+0

Avete provato l'analisi comparativa? Il mio benchmark iniziale ha rivelato che 'as.POSIXlt' non ha dato il massimo delle prestazioni. –

0

Una voce in ritardo, ma penso che la soluzione as.POSIXlt creerà una lista denominata di vettori, di cui si vuole solo l'ora

avrei chiave da parte di una colonna ITime e quindi utilizzare una ricerca binaria per sottoinsieme quelli volte prima 12:00

ci sono 60*60 *12 - 1 secondi prima 12:00 così seq_len(43199) tornerà tutto fino a (ma non compresi) 12:00

# create IDate and ITime columns and key by time 
setkey(DT[, c('Date','Time') := IDateTime(dts)],Time) 

# subset times before 12pm 
DT[.(seq_len(43199))] 
+0

In realtà è molto bello, lasciami testare gli anni bisestili e le cose dell'estate, è sempre più difficile di quanto sembri. Grazie – statquant