Comparativa eficiente de POSIXct en data.table
time-series subset (4)
Hola, estoy buscando una manera eficiente de seleccionar POSIXct filas POSIXct de una data.table manera que la hora del día sea menor a las 12:00:00 (Nótese que NO se requiere milisegundos, por lo que podemos usar ITime por ejemplo)
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 solución (el problema aquí es que el reparto podría ser costoso si N es 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)
}
}
Mirada rápida sobre el 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
Aquí hay una manera que usa algunas de las funcionalidades de xts para lograr lo que quieres. Esta no es una gran solución porque los objetos xts deben ordenarse por tiempo, pero los objetos data.table no tienen que serlo. Además, puede que no sea terriblemente rápido, ya que xts y data.table realizan algún trabajo redundante. Sin embargo, pensé que podría ser interesante.
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
Lo anterior utiliza una cadena de xts estilo xts para obtener las filas en las que el tiempo es entre las 00:00:00 y las 12:00:00 de cada día. El uso de which=TRUE devuelve el número de fila en lugar de los datos de esa fila, de modo que podamos data.table el data.table por esas filas
Podría usar una cadena como "1970-01-01" para obtener todos los datos de ese día, o "1970-01" para obtener todos los datos de enero de 1970, o "1970-01-01 / 1970-01-02" para Obtener todas las filas de esos dos días.
La forma canónica de hacer esto es convertir a POSIXlt y extraer el componente de hora.
hour(as.POSIXlt(DT$dts, "GMT")) < 12
Esto parece ser comparable en rendimiento a las otras técnicas discutidas (y es más fácil de entender).
Una entrada tardía, pero creo que la solución as.POSIXlt creará una lista nombrada de vectores, de los cuales solo desea la hora
ITime por una columna de tiempo y luego usaría una búsqueda binaria para subcontratar esos tiempos antes de las 12 pm
Hay 60*60 *12 - 1 segundos antes de las 12 pm, así que seq_len(43199) devolverá todo hasta (pero no incluido) a las 12 pm
# 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))]
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
Ahora un poco de demostración de buen estilo xts subconjunto
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"