vectores style para operador operaciones numérico div con binario argumento r date datetime lubridate

style - Mejores prácticas para evitar redondeos en la manipulación de fechas.



tags$div shiny (2)

Parece que sería mejor evitar la conversión del tiempo decimal si es posible.

Cuando se convierte de fecha a fecha decimal, también se necesita tener en cuenta el tiempo. Dado que Date no tiene un tiempo específico asociado, decimal_date asume que es 00:00:00 .

Sin embargo, si solo nos preocupa la fecha (y no la hora), podemos asumir que es el momento. Podría decirse que la mitad del día ( 12:00:00 ) es tan buena como el comienzo del día ( 00:00:00 ). Esto haría que la conversión a la Date más confiable ya que no estamos en la marca de la medianoche y unos segundos de apagado no afectan la salida. Una de las formas de hacer esto sería agregar 12*60*60/(365*24*60*60) a dd$time

dd$time2 = dd$time + 12*60*60/(365*24*60*60) data.frame(dd[1:3], "00:00:00" = as.Date(date_decimal(dd$time)), "12:00:00" = as.Date(date_decimal(dd$time2)), check.names = FALSE) # year month day 00:00:00 12:00:00 #1 1918 9 1 1918-09-01 1918-09-01 #2 1918 9 2 1918-09-02 1918-09-02 #3 1918 9 3 1918-09-03 1918-09-03 #4 1918 9 4 1918-09-03 1918-09-04 #5 1918 9 5 1918-09-04 1918-09-05 #6 1918 9 6 1918-09-05 1918-09-06 #7 1918 9 7 1918-09-07 1918-09-07 #8 1918 9 8 1918-09-08 1918-09-08 #9 1918 9 9 1918-09-09 1918-09-09 #10 1918 9 10 1918-09-09 1918-09-10 #11 1918 9 11 1918-09-10 1918-09-11 #12 1918 9 12 1918-09-12 1918-09-12

Cabe señalar, sin embargo, que el valor del tiempo decimal obtenido de esta manera será diferente.

Estoy haciendo algo de manipulación de fecha / hora y experimentando problemas de disparo redondeables, pero desagradables, al convertir fecha -> hora -> fecha. Superé este problema temporalmente al redondearlo en los puntos apropiados, pero me pregunto si existen mejores prácticas para el manejo de fechas que serían más limpias. Estoy usando una mezcla de funciones base-R y lubridate .

tl; dr hay una forma buena y simple de convertir la fecha decimal (YYYY.fff) a la clase de Date (y viceversa) sin pasar por POSIXt e incurrir en complicaciones de redondeo (y potencialmente de zona horaria)?

Comience con algunos días a partir de 1918, como columnas separadas de año / mes / día (no es una parte crítica de mi problema, pero es donde empieza a comenzar mi proyecto):

library(lubridate) dd <- data.frame(year=1918,month=9,day=1:12)

Convertir año / mes / día -> fecha -> hora:

dd <- transform(dd, time=decimal_date(make_date(year, month, day)))

Las diferencias sucesivas en el vector de tiempo resultante no son exactamente 1 debido al redondeo : esto es comprensible pero conlleva problemas en el futuro.

table(diff(dd$time)*365) ## 0.999999999985448 1.00000000006844 ## 9 2

Ahora supongamos que vuelvo a una fecha: las fechas son ligeramente antes o después de la medianoche (apagadas por <1 segundo en cualquier dirección):

d2 <- lubridate::date_decimal(dd$time) # [1] "1918-09-01 00:00:00 UTC" "1918-09-02 00:00:00 UTC" # [3] "1918-09-03 00:00:00 UTC" "1918-09-03 23:59:59 UTC" # [5] "1918-09-04 23:59:59 UTC" "1918-09-05 23:59:59 UTC" # [7] "1918-09-07 00:00:00 UTC" "1918-09-08 00:00:00 UTC" # [9] "1918-09-09 00:00:00 UTC" "1918-09-09 23:59:59 UTC" # [11] "1918-09-10 23:59:59 UTC" "1918-09-12 00:00:00 UTC"

Si ahora quiero fechas (en lugar de objetos POSIXct) puedo usar as.Date() , pero para mi consternación como.Date () se trunca en lugar de redondear ...

tt <- as.Date(d2) ## [1] "1918-09-01" "1918-09-02" "1918-09-03" "1918-09-03" "1918-09-04" ## [6] "1918-09-05" "1918-09-07" "1918-09-08" "1918-09-09" "1918-09-09" ##[11] "1918-09-10" "1918-09-12"

Así que las diferencias son ahora 0/1/2 días:

table(diff(tt)) # 0 1 2 # 2 7 2

Puedo solucionar esto redondeando primero:

table(diff(as.Date(round(d2)))) ## 1 ## 11

pero me pregunto si hay una mejor manera (por ejemplo, mantener POSIXct fuera de mi canalización y quedarme con fechas ...

Como lo sugiere este artículo de R-help desk de 2004 por Grothendieck y Petzoldt:

Al considerar qué clase usar, elija siempre la clase menos compleja que sea compatible con la aplicación. Es decir, use la Date si es posible, de lo contrario use chron y de otra manera use las clases POSIX . Dicha estrategia reducirá en gran medida el potencial de error y aumentará la confiabilidad de su aplicación.

La tabla extensa en este artículo muestra cómo traducir entre Date , chron y POSIXct , pero no incluye el tiempo decimal como uno de los candidatos ...


lubridate::decimal_date() está devolviendo un valor numeric . Si lo comprendo correctamente, la pregunta es cómo convertir ese número en Date y redondearlo adecuadamente sin rebotar a través de POSIXct .

as.Date(1L, origin = ''1970-01-01'') nos muestra que podemos proporcionar as.Date con días desde algunos orígenes especificados y convertir inmediatamente al tipo de fecha. Sabiendo esto, podemos omitir la parte del año por completo y establecerla como origen. Luego podemos convertir nuestras fechas decimales a días:

as.Date((dd$time-trunc(dd$time)) * 365, origin = "1918-01-01") .

Entonces, una función como esta podría hacer el truco (al menos durante años sin días bisiestos):

date_decimal2 <- function(decimal_date) { years <- trunc(decimal_date) origins <- paste0(years, "-01-01") # c.f. https://.com/questions/14449166/dates-with-lapply-and-sapply do.call(c, mapply(as.Date.numeric, x = (decimal_date-years) * 365, origin = origins, SIMPLIFY = FALSE)) }

Nota al margen: Admito que fui un poco a un agujero de conejo tratando de mover el origen alrededor del trato con la fecha anterior a 1970. Descubrí que cuanto más se alejaba el origen de la fecha objetivo, más extraños eran los resultados (y no de una forma que parecía explicarse fácilmente en los días bisiestos). Como el origen es flexible, decidí apuntarlo justo encima de los valores objetivo. Para los días de descanso, los segundos, y cualquier otra cosa extraña que nos tenga reservada, en su propia cabeza, así sea. =)