ordenar numero manipulacion hora formato fechas fecha extraer convertir r posixct

numero - ordenar fechas en r



Cómo R formatea POSIXct con segundos fraccionarios (2)

Creo que R formatea incorrectamente los tipos de POSIXct con segundos fraccionarios. Envié esto a través de R-bugs como una solicitud de mejora y me terminé con "creemos que el comportamiento actual es correcto, se eliminó el error". Si bien estoy muy agradecido por el trabajo que han hecho y continúan haciendo, quise obtener la opinión de otras personas sobre este tema en particular, y tal vez consejos sobre cómo hacer que el punto sea más eficaz.

Aquí hay un ejemplo:

> tt <- as.POSIXct(''2011-10-11 07:49:36.3'') > strftime(tt,''%Y-%m-%d %H:%M:%OS1'') [1] "2011-10-11 07:49:36.2"

Es decir, tt se crea como un tiempo de POSIXct con una parte fraccionaria de .3 segundos. Cuando se imprime con un dígito decimal, el valor que se muestra es .2. Trabajo mucho con marcas de tiempo de milisegundos de precisión y me causa muchos dolores de cabeza que los tiempos se imprimen a menudo un escalón más bajo que el valor real.

Esto es lo que está sucediendo: POSIXct es un número en coma flotante de segundos desde la época. Todos los valores enteros se manejan con precisión, pero en el punto flotante base-2, el valor más cercano a .3 es ligeramente menor que .3. El comportamiento establecido de strftime() para el formato %OSn es redondear al número solicitado de dígitos decimales, por lo que el resultado mostrado es .2. Para otras partes fraccionarias, el valor del punto flotante está ligeramente por encima del valor ingresado y la pantalla da el resultado esperado:

> tt <- as.POSIXct(''2011-10-11 07:49:36.4'') > strftime(tt,''%Y-%m-%d %H:%M:%OS1'') [1] "2011-10-11 07:49:36.4"

El argumento de los desarrolladores es que para los tipos de tiempo siempre debemos redondear a la precisión solicitada. Por ejemplo, si el tiempo es 11: 59: 59.8 entonces imprimirlo con formato %H:%M debería dar "11:59" no "12:00", y %H:%M:%S debería dar "11: 59:59 "no" 12:00:00 ". Estoy de acuerdo con esto para números enteros de segundos y para el indicador de formato %S , pero creo que el comportamiento debería ser diferente para los indicadores de formato que están diseñados para fracciones de segundos. Me gustaría ver que %OSn use el %OSn ronda al más cercano incluso para n = 0 mientras que %S usa redondeo, de modo que la impresión 11: 59: 59.8 con formato %H:%M:%OS0 daría "12: 00:00 ". Esto no afectaría a nada en números enteros de segundos porque estos siempre se representan con precisión, pero manejaría más naturalmente los errores de redondeo durante segundos fraccionarios.

Así es como se maneja la impresión de partes fraccionarias, por ejemplo C, porque las vueltas de colada de entero se reducen:

double x = 9.97; printf("%d/n",(int) x); // 9 printf("%.0f/n",x); // 10 printf("%.1f/n",x); // 10.0 printf("%.2f/n",x); // 9.97

Hice una encuesta rápida sobre cómo se manejan los segundos fraccionarios en otros idiomas y entornos, y realmente no parece haber consenso. La mayoría de las construcciones están diseñadas para números enteros de segundos y las partes fraccionarias son una idea de último momento. Me parece que en este caso los desarrolladores R hicieron una elección que no es del todo irrazonable, pero de hecho no es la mejor, y no es consistente con las convenciones en otros lugares para mostrar números de coma flotante.

¿Cuáles son los pensamientos de las personas? ¿Es correcto el comportamiento R? ¿Es la forma en que tú mismo lo diseñarías?


Me encontré con este problema y comencé a buscar una solución. La respuesta de @ Aaron es buena, pero aún se rompe para las fechas grandes.

Aquí hay un código que redondea los segundos correctamente, de acuerdo con el format u option("digits.secs") :

form <- function(x, format = "", tz= "", ...) { # From format.POSIXct if (!inherits(x, "POSIXct")) stop("wrong class") if (missing(tz) && !is.null(tzone <- attr(x, "tzone"))) tz <- tzone # Find the number of digits required based on the format string if (length(format) > 1) stop("length(format) > 1 not supported") m <- gregexpr("%OS[[:digit:]]?", format)[[1]] l <- attr(m, "match.length") if (l == 4) { d <- as.integer(substring(format, l+m-1, l+m-1)) } else { d <- unlist(options("digits.secs")) if (is.null(d)) { d <- 0 } } secs.since.origin <- unclass(x) # Seconds since origin secs <- round(secs.since.origin %% 60, d) # Seconds within the minute mins <- floor(secs.since.origin / 60) # Minutes since origin # Fix up overflow on seconds if (secs >= 60) { secs <- secs - 60 mins <- mins + 1 } # Represents the prior minute lt <- as.POSIXlt(60 * mins, tz=tz, origin=ISOdatetime(1970,1,1,0,0,0,tz="GMT")); lt$sec <- secs + 10^(-d-1) # Add in the seconds, plus a fudge factor. format.POSIXlt(as.POSIXlt(lt), format, ...) }

El factor de fundido de 10 ^ (- d-1) es de aquí: conversión exacta de personaje-> POSIXct-> carácter con horas de milisegundos por Aaron.

Algunos ejemplos:

f <- "%Y-%m-%d %H:%M:%OS" f3 <- "%Y-%m-%d %H:%M:%OS3" f6 <- "%Y-%m-%d %H:%M:%OS6"

De una pregunta casi idéntica:

x <- as.POSIXct("2012-12-14 15:42:04.577895") > format(x, f6) [1] "2012-12-14 15:42:04.577894" > form(x, f6) [1] "2012-12-14 15:42:04.577895" > myformat.POSIXct(x, 6) [1] "2012-12-14 15:42:04.577895"

Desde arriba:

> format(t1) [1] "2011-10-11 07:49:36.2" > myformat.POSIXct(t1,1) [1] "2011-10-11 07:49:36.3" > form(t1) [1] "2011-10-11 07:49:36.3" > format(t2) [1] "2011-10-11 23:59:59.9" > myformat.POSIXct(t2,0) [1] "2011-10-12 00:00:00" > myformat.POSIXct(t2,1) [1] "2011-10-12 00:00:00.0" > form(t2) [1] "2011-10-12" > form(t2, f) [1] "2011-10-12 00:00:00.0"

La verdadera diversión llega en 2038 para algunas fechas. Supongo que es porque perdemos un poco más de precisión en la mantisa. Tenga en cuenta el valor del campo segundos.

> t3 <- as.POSIXct(''2038-12-14 15:42:04.577895'') > format(t3) [1] "2038-12-14 15:42:05.5" > myformat.POSIXct(t3, 1) [1] "2038-12-14 15:42:05.6" > form(t3) [1] "2038-12-14 15:42:04.6"

Este código parece funcionar para otros casos extremos que he intentado. Lo común entre format.POSIXct y myformat.POSIXct en la respuesta de Aaron es la conversión de POSIXct a POSIXlt con el campo de segundos intacto.

Esto apunta a un error en esa conversión. No estoy usando ningún dato que no esté disponible para as.POSIXlt() .

Actualizar

El error está en src/main/datetime.c:434 en la función estática localtime0 , pero todavía no estoy seguro de la corrección correcta:

Líneas 433-434:

day = (int) floor(d/86400.0); left = (int) (d - day * 86400.0 + 0.5);

El 0.5 extra para redondear el valor es el culpable. Tenga en cuenta que el valor de subsegundo de t3 anterior excede .5. localtime0 ocupa únicamente de los segundos, y los subseconds se agregan después de que vuelva localtime0 .

localtime0 devuelve resultados correctos si el doble que se presenta es un valor entero.


Un problema subyacente es que la representación POSIXct es menos precisa que la representación POSIXlt, y la representación POSIXct se convierte a la representación POSIXlt antes del formateo. A continuación vemos que si nuestra cadena se convierte directamente en representación POSIXlt, se genera correctamente.

> as.POSIXct(''2011-10-11 07:49:36.3'') [1] "2011-10-11 07:49:36.2 CDT" > as.POSIXlt(''2011-10-11 07:49:36.3'') [1] "2011-10-11 07:49:36.3"

También podemos ver eso al observar la diferencia entre la representación binaria de los dos formatos y la representación habitual de 0.3.

> t1 <- as.POSIXct(''2011-10-11 07:49:36.3'') > as.numeric(t1 - round(unclass(t1))) - 0.3 [1] -4.768372e-08 > t2 <- as.POSIXlt(''2011-10-11 07:49:36.3'') > as.numeric(t2$sec - round(unclass(t2$sec))) - 0.3 [1] -2.831069e-15

Curiosamente, parece que ambas representaciones son en realidad inferiores a la representación habitual de 0,3, pero que la segunda está lo suficientemente cerca o trunca de una manera diferente a la que estoy imaginando aquí. Dado eso, no voy a preocuparme por las dificultades de representación de puntos flotantes; aún pueden suceder, pero si tenemos cuidado con la representación que usamos, con suerte se minimizarán.

El deseo de Robert de obtener resultados redondeados es simplemente un problema de producción y podría abordarse de varias maneras. Mi sugerencia sería algo como esto:

myformat.POSIXct <- function(x, digits=0) { x2 <- round(unclass(x), digits) attributes(x2) <- attributes(x) x <- as.POSIXlt(x2) x$sec <- round(x$sec, digits) format.POSIXlt(x, paste("%Y-%m-%d %H:%M:%OS",digits,sep="")) }

Esto comienza con una entrada POSIXct y primero redondea a los dígitos deseados; luego se convierte en POSIXlt y vuelve a redondear. El primer redondeo asegura que todas las unidades aumenten apropiadamente cuando estamos en un límite de minuto / hora / día; las rondas del segundo redondeo después de convertir a la representación más precisa.

> options(digits.secs=1) > t1 <- as.POSIXct(''2011-10-11 07:49:36.3'') > format(t1) [1] "2011-10-11 07:49:36.2" > myformat.POSIXct(t1,1) [1] "2011-10-11 07:49:36.3" > t2 <- as.POSIXct(''2011-10-11 23:59:59.999'') > format(t2) [1] "2011-10-11 23:59:59.9" > myformat.POSIXct(t2,0) [1] "2011-10-12 00:00:00" > myformat.POSIXct(t2,1) [1] "2011-10-12 00:00:00.0"

Un último aparte: ¿Sabías que el estándar permite hasta dos segundos intercalares?

> as.POSIXlt(''2011-10-11 23:59:60.9'') [1] "2011-10-11 23:59:60.9"

OK, una cosa más. El comportamiento realmente cambió en mayo debido a un error presentado por el OP ( Error 14579 ); antes de eso, redondeó fracciones de segundo. Lamentablemente, eso significaba que a veces podía redondearse hasta un segundo que no era posible; en el informe de errores, subió a 60 cuando debería haber pasado al próximo minuto. Una razón por la que se tomó la decisión de truncar en lugar de redondas es que se está imprimiendo desde la representación POSIXlt, donde cada unidad se almacena por separado. Por lo tanto, pasar al próximo minuto / hora / etc. es más difícil que una simple operación de redondeo. Para redondear fácilmente, es necesario redondear en POSIXct representación y luego convertir de nuevo, como sugiero.