fecha change r data.table

change - ¿Por qué es as.Date lento en un vector de caracteres?



change format date r (5)

Como otros mencionaron, strptime (conversión de carácter a POSIXlt) es el cuello de botella aquí. Otra solución simple utiliza el paquete lubridate y su método fast_strptime su lugar.

Esto es lo que parece en mis datos:

> tables() NAME NROW MB COLS [1,] pp 3,718,339 126 session_id,date,user_id,path,num_sessions KEY [1,] user_id,date Total: 126MB > pp[, 2, with = F] date 1: 2013-09-25 2: 2013-09-25 3: 2013-09-25 4: 2013-09-25 5: 2013-09-25 --- 3718335: 2013-09-25 3718336: 2013-09-25 3718337: 2013-09-25 3718338: 2013-10-11 3718339: 2013-10-11 > system.time(pp[, date := as.Date(fast_strptime(date, "%Y-%m-%d"))]) user system elapsed 0.315 0.026 0.344

Para comparacion:

> system.time(pp[, date := as.Date(date, "%Y-%m-%d")]) user system elapsed 108.193 0.399 108.844

¡Eso es ~ 316 veces más rápido!

Empecé a usar el paquete data.table en R para mejorar el rendimiento de mi código. Estoy usando el siguiente código:

sp500 <- read.csv(''../rawdata/GMTSP.csv'') days <- c("Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday") # Using data.table to get the things much much faster sp500 <- data.table(sp500, key="Date") sp500 <- sp500[,Date:=as.Date(Date, "%m/%d/%Y")] sp500 <- sp500[,Weekday:=factor(weekdays(sp500[,Date]), levels=days, ordered=T)] sp500 <- sp500[,Year:=(as.POSIXlt(Date)$year+1900)] sp500 <- sp500[,Month:=(as.POSIXlt(Date)$mon+1)]

Noté que la conversión realizada por la función as.Date es muy lenta, en comparación con otras funciones que crean días de la semana, etc. ¿Por qué? ¿Hay una solución mejor / más rápida, cómo convertir a formato de fecha? (Si me preguntan si realmente necesito el formato de fecha, probablemente sí, porque luego uso ggplot2 para hacer gráficos, que funcionan como un encanto con este tipo de datos).

Ser más preciso

> system.time(sp500 <- sp500[,Date:=as.Date(Date, "%m/%d/%Y")]) user system elapsed 92.603 0.289 93.014 > system.time(sp500 <- sp500[,Weekday:=factor(weekdays(sp500[,Date]), levels=days, ordered=T)]) user system elapsed 1.938 0.062 2.001 > system.time(sp500 <- sp500[,Year:=(as.POSIXlt(Date)$year+1900)]) user system elapsed 0.304 0.001 0.305

En MacAir i5 con un poco menos de 3000000 observaciones.

Gracias


Creo que es solo eso. as.Date convierte el character en Date mediante POSIXlt , usando strptime . Y strptime es muy lento, creo.

Para rastrearlo a través de usted, escriba as.Date , luego los methods(as.Date) , luego observe el método del character .

> as.Date function (x, ...) UseMethod("as.Date") <bytecode: 0x2cf4b20> <environment: namespace:base> > methods(as.Date) [1] as.Date.character as.Date.date as.Date.dates as.Date.default [5] as.Date.factor as.Date.IDate* as.Date.numeric as.Date.POSIXct [9] as.Date.POSIXlt Non-visible functions are asterisked > as.Date.character function (x, format = "", ...) { charToDate <- function(x) { xx <- x[1L] if (is.na(xx)) { j <- 1L while (is.na(xx) && (j <- j + 1L) <= length(x)) xx <- x[j] if (is.na(xx)) f <- "%Y-%m-%d" } if (is.na(xx) || !is.na(strptime(xx, f <- "%Y-%m-%d", tz = "GMT")) || !is.na(strptime(xx, f <- "%Y/%m/%d", tz = "GMT"))) return(strptime(x, f)) stop("character string is not in a standard unambiguous format") } res <- if (missing(format)) charToDate(x) else strptime(x, format, tz = "GMT") #### slow part, I think #### as.Date(res) } <bytecode: 0x2cf6da0> <environment: namespace:base> >

¿Por qué as.POSIXlt(Date)$year+1900 relativamente rápido? Nuevamente, trace a través de:

> as.POSIXct function (x, tz = "", ...) UseMethod("as.POSIXct") <bytecode: 0x2936de8> <environment: namespace:base> > methods(as.POSIXct) [1] as.POSIXct.date as.POSIXct.Date as.POSIXct.dates as.POSIXct.default [5] as.POSIXct.IDate* as.POSIXct.ITime* as.POSIXct.numeric as.POSIXct.POSIXlt Non-visible functions are asterisked > as.POSIXlt.Date function (x, ...) { y <- .Internal(Date2POSIXlt(x)) names(y$year) <- names(x) y } <bytecode: 0x395e328> <environment: namespace:base> >

Intrigado, profundicemos en Date2POSIXlt. Para este bit, necesitamos grep main / src para saber qué archivo .c debemos ver.

~/R/Rtrunk/src/main$ grep Date2POSIXlt * names.c:{"Date2POSIXlt",do_D2POSIXlt, 0, 11, 1, {PP_FUNCALL, PREC_FN, 0}}, $

Ahora sabemos que debemos buscar D2POSIXlt:

~/R/Rtrunk/src/main$ grep D2POSIXlt * datetime.c:SEXP attribute_hidden do_D2POSIXlt(SEXP call, SEXP op, SEXP args, SEXP env) names.c:{"Date2POSIXlt",do_D2POSIXlt, 0, 11, 1, {PP_FUNCALL, PREC_FN, 0}}, $

Oh, podríamos haber adivinado datetime.c. De todos modos, para ver la última copia en vivo:

datetime.c

Busque allí D2POSIXlt y verá lo simple que es pasar de Fecha (numérico) a POSIXlt. También verá cómo POSIXlt es un vector real (8 bytes) más siete vectores enteros (4 bytes cada uno). ¡Eso es 40 bytes, por fecha!

Entonces, el quid de la cuestión (creo) es por qué el tiempo de strptime es tan lento, y tal vez eso se puede mejorar en R. O simplemente evitar POSIXlt , ya sea directa o indirectamente.

Aquí hay un ejemplo reproducible que usa la cantidad de elementos indicados en la pregunta (3,000,000):

> Range = seq(as.Date("2000-01-01"),as.Date("2012-01-01"),by="days") > Date = format(sample(Range,3000000,replace=TRUE),"%m/%d/%Y") > system.time(as.Date(Date, "%m/%d/%Y")) user system elapsed 21.681 0.060 21.760 > system.time(strptime(Date, "%m/%d/%Y")) user system elapsed 29.594 8.633 38.270 > system.time(strptime(Date, "%m/%d/%Y", tz="GMT")) user system elapsed 19.785 0.000 19.802

Pasar tz parece acelerar el strptime , lo cual ocurre con as.Date.character . Entonces tal vez depende de tu localidad. Pero strptime parece ser el culpable, no data.table . Quizás vuelva a ejecutar este ejemplo y vea si le lleva 90 segundos en su equipo.


Esta es una vieja pregunta, pero creo que este pequeño truco podría ser útil. Si tiene varias filas con la misma fecha, puede hacer

data[, date := as.Date(date[1]), by = date]

Es mucho más rápido ya que solo procesa cada fecha una vez (en mi conjunto de datos de 40 millones de filas va de 25 segundos a 0.5 segundos).


Gracias por las sugerencias. Lo resolví escribiendo el algoritmo gaussiano para las fechas y obtuve mejores resultados, ver abajo.

getWeekDay <- function(year, month, day) { # Implementation of the Gaussian algorithm to get weekday 0 - Sunday, ... , 7 - Saturday Y <- year Y[month<3] <- (Y[month<3] - 1) d <- day m <- ((month + 9)%%12) + 1 c <- floor(Y/100) y <- Y-c*100 dayofweek <- (d + floor(2.6*m - 0.2) + y + floor(y/4) + floor(c/4) - 2*c) %% 7 return(dayofweek) } sp500 <- read.csv(''../rawdata/GMTSP.csv'') days <- c("Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday") # Using data.table to get the things much much faster sp500 <- data.table(sp500, key="Date") sp500 <- sp500[,Month:=as.integer(substr(Date,1,2))] sp500 <- sp500[,Day:=as.integer(substr(Date,4,5))] sp500 <- sp500[,Year:=as.integer(substr(Date,7,10))] #sp500 <- sp500[,Date:=as.Date(Date, "%m/%d/%Y")] #sp500 <- sp500[,Weekday:=factor(weekdays(sp500[,Date]), levels=days, ordered=T)] sp500 <- sp500[,Weekday:=factor(getWeekDay(Year, Month, Day))] levels(sp500$Weekday) <- days

Ejecutar todo el bloque anterior (incluida la lectura de la fecha de csv) ... Data.table es realmente impresionante.

user system elapsed 19.074 0.803 20.284

El tiempo de la conversión en sí es 3.49 transcurrido.


Originalmente pensé: "El argumento a como fecha anterior no tiene el formato especificado".

Ahora pienso: asumí que el valor de Fecha que codificabas estaba en un formato estándar. Supongo que no. Entonces estás haciendo dos procesos. Está formateando de nuevo el formato de fecha y está reorganizando en función de los nuevos valores que tienen una secuencia de clasificación completamente diferente.