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"