Capítulo25 Fechas, horas y minutas
## [1] "2026-09-09"
El tema proviene de los siguientes sitios.
English: https://r4ds.had.co.nz/dates-and-times.html
Español: https://es.r4ds.hadley.nz/16-datetimes.html
25.1 Temas:
Por qué las fechas son más difíciles de lo que parecen
Los tres tipos:
<date>,<time>y<dttm>Convertir texto a fecha con
ymd()y compañíaExtraer partes:
year(),month(),wday(),hour()Redondear:
floor_date(),ceiling_date(),round_date()Duraciones, períodos e intervalos: los tres no son lo mismo
Zonas horarias
library(lubridate)
library(datos)
library(tidyverse)
25.2 Por qué las fechas son difíciles
Contar tiempo parece trivial y no lo es, porque el calendario que usamos está lleno de excepciones que el código tiene que respetar:
- los meses no duran lo mismo, así que “un mes después del 31 de enero” no tiene una respuesta obvia;
- hay años bisiestos, así que un año no siempre son 365 días;
- hay cambios de horario de verano, así que un día no siempre son 24 horas;
- hay zonas horarias, así que dos personas pueden estar en fechas distintas en el mismo instante;
- y hasta hay segundos intercalares, que se añaden de vez en cuando para cuadrar el reloj con la rotación de la Tierra.
De ahí sale la idea central del capítulo, que conviene tener clara desde el principio: hay dos maneras distintas de medir tiempo, y hacen falta las dos. Una cuenta segundos exactos, sin excepciones, y la otra cuenta en unidades humanas respetando el calendario. Casi todos los errores con fechas vienen de usar la que no era.
Puerto Rico no cambia de horario de verano y usa siempre la zona
"America/Puerto_Rico" (AST, cuatro horas menos que UTC). Eso nos ahorra un
problema que sufren los que trabajan con datos de Estados Unidos continental, pero
aparece en cuanto se comparan datos de aquí con datos de allá.
25.3 Creando fechas/horas
En R y en los sistemas tipo Unix el tiempo se cuenta como el número de segundos (o días) transcurridos desde la medianoche del 1 de enero de 1970, conocida como la época Unix. Por eso as.numeric() de una fecha devuelve los días desde ese punto.
R distingue tres cosas, y conviene usar la más sencilla que sirva:
- date,
- time,
- date-time,
<date>: solo el día, sin hora. Por dentro es un número: los días transcurridos desde el 1 de enero de 1970.<time>: solo la hora del día, sin fecha. Por dentro, los segundos desde la medianoche.<dttm>: fecha y hora juntas. Por dentro, los segundos desde la época Unix, y además arrastra una zona horaria.
Usa <dttm> solo cuando de verdad necesites la hora: las fechas-hora arrastran
zona horaria y complican todo lo demás.
https://github.com/edgararuiz/guias-rapidas/blob/master/fechas.pdf
library(tidyverse)
library(lubridate) # paquete para convertir fechas
tidyverse_packages() # lista completa de los paquetes activado con **tidyverse**## [1] "broom" "conflicted" "cli" "dbplyr"
## [5] "dplyr" "dtplyr" "forcats" "ggplot2"
## [9] "googledrive" "googlesheets4" "haven" "hms"
## [13] "httr" "jsonlite" "lubridate" "magrittr"
## [17] "modelr" "pillar" "purrr" "ragg"
## [21] "readr" "readxl" "reprex" "rlang"
## [25] "rstudioapi" "rvest" "stringr" "tibble"
## [29] "tidyr" "xml2" "tidyverse"
25.4 El paquete lubridate
ymd(), mdy(), dmy() (lubridate): convertir texto a fechas.
- Qué hacen: interpretan una cadena según el orden de sus partes:
ymd("2024-03-01"),mdy("3/1/2024"),dmy("1/3/2024"). - Con hora: añade
_hms,_hmo_h(p. ej.dmy_hms()). - Extraer partes:
year(),month(),day(),wday()(día de la semana).
El truco de lubridate es que el nombre de la función dice el orden de las
partes, no el formato exacto. dmy() significa “día, mes, año”, y con eso le
basta: acepta "1/3/2024", "01-03-2024", "1 marzo 2024" y "1.3.24". No hay
que escribir formatos como %d/%m/%Y, que es lo que hacía falta en base R y lo
que casi nadie recuerda.
"01/03/2024" es el 1 de marzo o el 3 de enero según de dónde venga el archivo, y
ninguna función puede adivinarlo. dmy() y mdy() las dos van a darte una
fecha válida, y una de las dos estará mal. Antes de convertir, averigua de dónde
salieron los datos. Si hay valores con día mayor que 12 en la columna, salen NA
con una de las dos funciones, y esa es tu pista.
La función today() regresa la fecha de hoy, similar a Sys.date()
## [1] "2026-09-09"
Use la función de tribble(), del paquete tibble, para crear una tabla manualmente
fechas <- tribble(
~ codigo, ~ fecha,
"001", "01/01/2019 00:00:00",
"002", "31/03/2019 01:05:00",
"003", "14/06/2019 20:00:00",
"004", "01/12/1859 11:32:13",
"005", "01/01/0501 05:04:33"
)
fechas## # A tibble: 5 × 2
## codigo fecha
## <chr> <chr>
## 1 001 01/01/2019 00:00:00
## 2 002 31/03/2019 01:05:00
## 3 003 14/06/2019 20:00:00
## 4 004 01/12/1859 11:32:13
## 5 005 01/01/0501 05:04:33
Hay varias opciones para convertir texto a fechas, el más usado es mdy_hms(), pero en este caso el formato utilizado no funciona bien.
Un error muy común es usar mdy() cuando las fechas están en formato día/mes/año. Como no existe un mes 31, obtendrás NA o fechas equivocadas; elige la función (dmy(), mdy(), ymd()) según el orden real de la fecha.
25.4.1 Un error tipico
## # A tibble: 5 × 2
## codigo fecha
## <chr> <dttm>
## 1 001 2019-01-01 00:00:00
## 2 002 NA
## 3 003 NA
## 4 004 1859-01-12 11:32:13
## 5 005 0501-01-01 05:04:33
Ya que el día es el primero, y no el mes, usamos dmy_hms()
## # A tibble: 5 × 2
## codigo fecha
## <chr> <dttm>
## 1 001 2019-01-01 00:00:00
## 2 002 2019-03-31 01:05:00
## 3 003 2019-06-14 20:00:00
## 4 004 1859-12-01 11:32:13
## 5 005 0501-01-01 05:04:33
lubridate tiene varias funciones para extraer partes de la fecha, por ejemplo: año, mes, día, hora, minuto, y cuatrimestre.
nueva_fechas %>%
mutate(a = year(fecha),
m = month(fecha),
d = day(fecha),
h = hour(fecha),
mn = minute(fecha),
q = quarter(fecha)
)## # A tibble: 5 × 8
## codigo fecha a m d h mn q
## <chr> <dttm> <int> <int> <int> <int> <int> <int>
## 1 001 2019-01-01 00:00:00 2019 1 1 0 0 1
## 2 002 2019-03-31 01:05:00 2019 3 31 1 5 1
## 3 003 2019-06-14 20:00:00 2019 6 14 20 0 2
## 4 004 1859-12-01 11:32:13 1859 12 1 11 32 4
## 5 005 0501-01-01 05:04:33 501 1 1 5 4 1
Las funciones round_date(), ceiling_date() y floor_date() permiten redondear la fecha al número más cercano de la unidad especificada
nueva_fechas %>%
mutate(
redondear = round_date(fecha, unit = " month"), # round_date es redondea al mes más cercano
techo = ceiling_date(fecha, unit = "day"), # ceiling_date redondea al día más cercano
suelo = floor_date(fecha, unit = "month"), # floor_date redondea al mes más cercano
suelo_hour= floor_date(fecha, unit = "hour") # redondea a la hora más cercana
)## # A tibble: 5 × 6
## codigo fecha redondear techo
## <chr> <dttm> <dttm> <dttm>
## 1 001 2019-01-01 00:00:00 2019-01-01 00:00:00 2019-01-01 00:00:00
## 2 002 2019-03-31 01:05:00 2019-04-01 00:00:00 2019-04-01 00:00:00
## 3 003 2019-06-14 20:00:00 2019-06-01 00:00:00 2019-06-15 00:00:00
## 4 004 1859-12-01 11:32:13 1859-12-01 00:00:00 1859-12-02 00:00:00
## 5 005 0501-01-01 05:04:33 0501-01-01 00:00:00 0501-01-02 00:00:00
## # ℹ 2 more variables: suelo <dttm>, suelo_hour <dttm>
25.5 Intervalos y duraciones
La función interval() crea un objeto R de intervalo de tiempo. En este caso, el intervalo entre la fecha en la tabla, y el día de hoy. Nota que esto puede ser muy útil si quiere calcular la cantidad de tiempo entre dos fechas.
## # A tibble: 5 × 3
## codigo fecha intervalo
## <chr> <dttm> <Interval>
## 1 001 2019-01-01 00:00:00 2019-01-01 00:00:00 UTC--2026-09-09 UTC
## 2 002 2019-03-31 01:05:00 2019-03-31 01:05:00 UTC--2026-09-09 UTC
## 3 003 2019-06-14 20:00:00 2019-06-14 20:00:00 UTC--2026-09-09 UTC
## 4 004 1859-12-01 11:32:13 1859-12-01 11:32:13 UTC--2026-09-09 UTC
## 5 005 0501-01-01 05:04:33 0501-01-01 05:04:33 UTC--2026-09-09 UTC
int_length() regresa el número de segundos dentro del intervalo
nueva_fechas %>%
mutate(
intervalo = interval(fecha, today()), #
segundos = int_length(intervalo) # número de segundos en el intervalo
)## # A tibble: 5 × 4
## codigo fecha intervalo segundos
## <chr> <dttm> <Interval> <dbl>
## 1 001 2019-01-01 00:00:00 2019-01-01 00:00:00 UTC--2026-09-09 UTC 242611200
## 2 002 2019-03-31 01:05:00 2019-03-31 01:05:00 UTC--2026-09-09 UTC 234917700
## 3 003 2019-06-14 20:00:00 2019-06-14 20:00:00 UTC--2026-09-09 UTC 228369600
## 4 004 1859-12-01 11:32:13 1859-12-01 11:32:13 UTC--2026-09-09 UTC 5262841667
## 5 005 0501-01-01 05:04:33 0501-01-01 05:04:33 UTC--2026-09-09 UTC 48146036127
25.6 El operador %--% simplifica el cálculo del intervalo
Compara con el script anterior
## # A tibble: 5 × 3
## codigo fecha intervalo
## <chr> <dttm> <Interval>
## 1 001 2019-01-01 00:00:00 2019-01-01 00:00:00 UTC--2026-09-09 UTC
## 2 002 2019-03-31 01:05:00 2019-03-31 01:05:00 UTC--2026-09-09 UTC
## 3 003 2019-06-14 20:00:00 2019-06-14 20:00:00 UTC--2026-09-09 UTC
## 4 004 1859-12-01 11:32:13 1859-12-01 11:32:13 UTC--2026-09-09 UTC
## 5 005 0501-01-01 05:04:33 0501-01-01 05:04:33 UTC--2026-09-09 UTC
Para saber el número de días en el intervalo, divida el intervalo por la función que corresponde a días, days()
nueva_fechas %>%
mutate(dias = fecha %--% today() / days(),
anio= fecha %--% today() / years()) # número de días en el intervalo## # A tibble: 5 × 4
## codigo fecha dias anio
## <chr> <dttm> <dbl> <dbl>
## 1 001 2019-01-01 00:00:00 2808 7.69
## 2 002 2019-03-31 01:05:00 2719. 7.44
## 3 003 2019-06-14 20:00:00 2643. 7.24
## 4 004 1859-12-01 11:32:13 60913. 167.
## 5 005 0501-01-01 05:04:33 557246. 1526.
Intervalo entre una lista de fecha y otra fecha y hora específica
## # A tibble: 5 × 3
## codigo fecha dias
## <chr> <dttm> <dbl>
## 1 001 2019-01-01 00:00:00 365
## 2 002 2019-03-31 01:05:00 276.
## 3 003 2019-06-14 20:00:00 200.
## 4 004 1859-12-01 11:32:13 58470.
## 5 005 0501-01-01 05:04:33 554803.
25.7 Ejercicio
Usa esta función y calcula el número de día que ha transcurrido entre el día de su nacimiento y el día de hoy
## [1] 254707200
## [1] 2948
## [1] 96.83871
## [1] 8.071233
## [1] 254707200000
Los resultados se pueden visualizar usando ggplot2
nueva_fechas %>%
mutate(dias = fecha %--% today() / years()) %>%
ggplot() +
geom_col(aes(codigo, dias)) +
coord_flip()
25.8 Serie de funciones para calcular la duración de un intervalo de tiempo.
- dyears() # años
- dmonths() # meses
- dweeks() # semanas
- ddays() # días
- dhours() # horas
- dminutes() # minutos
- dseconds() # segundos
- dmilliseconds() # milisegundos
- dmicroseconds() # microsegundos
- dnanoseconds() # nanosegundos
- dpicoseconds() # picosegundos
25.9 as.duration
as.duration() crea un objecto en R que contiene la duración del intervalo de tiempo.
nueva_fechas %>%
mutate(desde_hoy = as.duration(fecha %--% today())) # duración desde la fecha hasta hoy## # A tibble: 5 × 3
## codigo fecha desde_hoy
## <chr> <dttm> <Duration>
## 1 001 2019-01-01 00:00:00 242611200s (~7.69 years)
## 2 002 2019-03-31 01:05:00 234917700s (~7.44 years)
## 3 003 2019-06-14 20:00:00 228369600s (~7.24 years)
## 4 004 1859-12-01 11:32:13 5262841667s (~166.77 years)
## 5 005 0501-01-01 05:04:33 48146036127s (~1525.66 years)
El objeto de duración de tiempo se puede filtrar fácilmente basado en una variedad de tipo de tiempos. En este caso, semanas dweeks() crea un objeto de duración de la largura especificada
nueva_fechas %>%
mutate(desde_hoy = as.duration(fecha %--% today())) %>%
filter(desde_hoy > dyears(7))## # A tibble: 5 × 3
## codigo fecha desde_hoy
## <chr> <dttm> <Duration>
## 1 001 2019-01-01 00:00:00 242611200s (~7.69 years)
## 2 002 2019-03-31 01:05:00 234917700s (~7.44 years)
## 3 003 2019-06-14 20:00:00 228369600s (~7.24 years)
## 4 004 1859-12-01 11:32:13 5262841667s (~166.77 years)
## 5 005 0501-01-01 05:04:33 48146036127s (~1525.66 years)
Otra opción es ddays().
nueva_fechas %>%
mutate(desde_hoy = as.duration(fecha %--% today())) %>%
filter(desde_hoy < ddays(2500)) # Nota que se puede filtrar por el número de días que han acumulado## # A tibble: 0 × 3
## # ℹ 3 variables: codigo <chr>, fecha <dttm>, desde_hoy <Duration>
25.10 Crear una fecha desde columnas individuales
- make_date()
- make_datetime()
Unir el año, mes, día, hora y minutos que estén en diferentes columnas en uno
- sumamente practico cuando se tiene una base de datos con las fechas en diferentes columnas
## # A tibble: 6 × 19
## anio mes dia horario_salida salida_programada atraso_salida
## <int> <int> <int> <int> <int> <dbl>
## 1 2013 1 1 517 515 2
## 2 2013 1 1 533 529 4
## 3 2013 1 1 542 540 2
## 4 2013 1 1 544 545 -1
## 5 2013 1 1 554 600 -6
## 6 2013 1 1 554 558 -4
## # ℹ 13 more variables: horario_llegada <int>, llegada_programada <int>,
## # atraso_llegada <dbl>, aerolinea <chr>, vuelo <int>, codigo_cola <chr>,
## # origen <chr>, destino <chr>, tiempo_vuelo <dbl>, distancia <dbl>,
## # hora <dbl>, minuto <dbl>, fecha_hora <dttm>
vuelos %>%
dplyr::select(anio, mes, dia, hora, minuto) %>%
mutate(salida_dia=make_date(anio, mes, dia),
salida_dia_minutos = make_datetime(anio, mes, dia, hora, minuto))## # A tibble: 336,776 × 7
## anio mes dia hora minuto salida_dia salida_dia_minutos
## <int> <int> <int> <dbl> <dbl> <date> <dttm>
## 1 2013 1 1 5 15 2013-01-01 2013-01-01 05:15:00
## 2 2013 1 1 5 29 2013-01-01 2013-01-01 05:29:00
## 3 2013 1 1 5 40 2013-01-01 2013-01-01 05:40:00
## 4 2013 1 1 5 45 2013-01-01 2013-01-01 05:45:00
## 5 2013 1 1 6 0 2013-01-01 2013-01-01 06:00:00
## 6 2013 1 1 5 58 2013-01-01 2013-01-01 05:58:00
## 7 2013 1 1 6 0 2013-01-01 2013-01-01 06:00:00
## 8 2013 1 1 6 0 2013-01-01 2013-01-01 06:00:00
## 9 2013 1 1 6 0 2013-01-01 2013-01-01 06:00:00
## 10 2013 1 1 6 0 2013-01-01 2013-01-01 06:00:00
## # ℹ 336,766 more rows
Unir el año, mes, día, hora que estén en diferentes columnas en uno
vuelos %>%
dplyr::select(anio, mes, dia, hora, minuto) %>%
mutate(salida2 = make_date(anio, mes, dia))## # A tibble: 336,776 × 6
## anio mes dia hora minuto salida2
## <int> <int> <int> <dbl> <dbl> <date>
## 1 2013 1 1 5 15 2013-01-01
## 2 2013 1 1 5 29 2013-01-01
## 3 2013 1 1 5 40 2013-01-01
## 4 2013 1 1 5 45 2013-01-01
## 5 2013 1 1 6 0 2013-01-01
## 6 2013 1 1 5 58 2013-01-01
## 7 2013 1 1 6 0 2013-01-01
## 8 2013 1 1 6 0 2013-01-01
## 9 2013 1 1 6 0 2013-01-01
## 10 2013 1 1 6 0 2013-01-01
## # ℹ 336,766 more rows
## Conversión de fechas que no son estándar
Datos_raros=tribble(~Ind, ~Persona, ~fecha_de_nacimiento,
1, "Abuela", "1/1/30",
2, "Abuelo","1/30/01",
3, "Tio", "1/30/09")
Datos_raros ## # A tibble: 3 × 3
## Ind Persona fecha_de_nacimiento
## <dbl> <chr> <chr>
## 1 1 Abuela 1/1/30
## 2 2 Abuelo 1/30/01
## 3 3 Tio 1/30/09
## # A tibble: 3 × 4
## Ind Persona fecha_de_nacimiento nueva_fecha
## <dbl> <chr> <chr> <date>
## 1 1 Abuela 1/1/30 2030-01-01
## 2 2 Abuelo 1/30/01 2001-01-30
## 3 3 Tio 1/30/09 2009-01-30
## [1] "1970-01-01"
df <- tibble(
Persona = c("Abuela", "Abuelo", "Tio"),
fecha_de_nacimiento = c("1/1/1930", "1/30/1901", "1/30/1909")
)
df <- df %>%
mutate(
nueva_fecha = mdy(fecha_de_nacimiento),
nueva_fecha = if_else(year(nueva_fecha) > year(today()),
nueva_fecha - years(100),
nueva_fecha)
)
df## # A tibble: 3 × 3
## Persona fecha_de_nacimiento nueva_fecha
## <chr> <chr> <date>
## 1 Abuela 1/1/1930 1930-01-01
## 2 Abuelo 1/30/1901 1901-01-30
## 3 Tio 1/30/1909 1909-01-30
#Datos_raros$fecha2=as.Date(as.Date(format(as.Date(Datos_raros$fecha_de_nacimiento, format="%m/%d/%Y"), "19%y%m%d"), "%Y%m%d"))
#Datos_raros25.11 %/% y %%: división entera y residuo
Estos dos operadores no son de fechas, pero aparecen constantemente al trabajar
con ellas, porque muchos archivos guardan la hora como un número: las 5:30 de
la tarde vienen como 1730.
%/%es la división entera: se queda con la parte de arriba y tira el resto.1730 %/% 100da17, la hora.%%es el residuo: se queda con lo que sobra.1730 %% 100da30, los minutos.
Con los dos juntos se parte un 1730 en hora y minutos sin tocar texto.
vuelos |> dplyr::select(tiempo_vuelo) |>
mutate(t=tiempo_vuelo %/% 100) |> # Division
mutate(m= tiempo_vuelo %% 100) # modulus... Cambia el 100 para 1, 10, 1000... y mira cual es el resultado## # A tibble: 336,776 × 3
## tiempo_vuelo t m
## <dbl> <dbl> <dbl>
## 1 227 2 27
## 2 227 2 27
## 3 160 1 60
## 4 183 1 83
## 5 116 1 16
## 6 150 1 50
## 7 158 1 58
## 8 53 0 53
## 9 140 1 40
## 10 138 1 38
## # ℹ 336,766 more rows
25.11.1 El operador %% devuelve el residuo de una división
- Por ejemplo,
5 %% 2devuelve1, porque el residuo de dividir 5 entre 2 es 1. - Y
5 %/% 2devuelve2, la parte entera de la división.
%% sirve también para preguntar “¿es múltiplo de?”: x %% 2 == 0 es la manera
de saber si un número es par, y anio %% 4 == 0 es el primer paso de la regla de
los años bisiestos.
## [1] "anio" "mes" "dia"
## [4] "horario_salida" "salida_programada" "atraso_salida"
## [7] "horario_llegada" "llegada_programada" "atraso_llegada"
## [10] "aerolinea" "vuelo" "codigo_cola"
## [13] "origen" "destino" "tiempo_vuelo"
## [16] "distancia" "hora" "minuto"
## [19] "fecha_hora"
hacer_fechahora_100 <- function(anio, mes, dia, tiempo) {
make_datetime(anio, mes, dia, tiempo %/% 100, tiempo %% 100)
}
vuelos_dt <- vuelos %>%
filter(!is.na(horario_salida), !is.na(horario_llegada)) %>%
mutate(
horario_salida = hacer_fechahora_100(anio, mes, dia, horario_salida),
horario_llegada = hacer_fechahora_100(anio, mes, dia, horario_llegada),
salida_programada = hacer_fechahora_100(anio, mes, dia, salida_programada),
llegada_programada = hacer_fechahora_100(anio, mes, dia, llegada_programada)
) %>%
dplyr::select(origen, destino, starts_with("atraso"), starts_with("horario"), ends_with("programada"), tiempo_vuelo)
head(vuelos_dt)## # A tibble: 6 × 9
## origen destino atraso_salida atraso_llegada horario_salida
## <chr> <chr> <dbl> <dbl> <dttm>
## 1 EWR IAH 2 11 2013-01-01 05:17:00
## 2 LGA IAH 4 20 2013-01-01 05:33:00
## 3 JFK MIA 2 33 2013-01-01 05:42:00
## 4 JFK BQN -1 -18 2013-01-01 05:44:00
## 5 LGA ATL -6 -25 2013-01-01 05:54:00
## 6 EWR ORD -4 12 2013-01-01 05:54:00
## # ℹ 4 more variables: horario_llegada <dttm>, salida_programada <dttm>,
## # llegada_programada <dttm>, tiempo_vuelo <dbl>
25.11.1.1 Cuantos segundos hay en un dia?
vuelos_dt %>%
ggplot(aes(horario_salida)) +
geom_freqpoly(binwidth = 86400) # 86400 segundos = 1 día
vuelos_dt %>%
filter(horario_salida < ymd(20130102)) %>%
ggplot(aes(horario_salida)) +
geom_freqpoly(binwidth = 600) # 600 segundos = 10 minutos
Nota variables que no funciona
## [1] "2010-10-10" NA
## [1] "Jan 1, 2010"
## [1] "2010-01-01"
## [1] "2015-Mar-07"
## [1] "2015-03-07"
## [1] "06-Jun-2017"
## [1] "2017-06-06"
## [1] "Aug 19 (2015)" "Jul 1 (2015)"
## [1] "2015-08-19" "2015-07-01"
## [1] "12/30/14"
## [1] "2014-12-30"
## [1] "ene 1, 2010"
## [1] NA
## [1] "Agosto 19 (2015)" "Julio 1 (2015)"
## [1] NA NA
## [1] "2010-01-01"
## [1] "2015-03-07"
## [1] "2017-06-06"
## [1] "2015-08-19" "2015-07-01"
## [1] "2014-12-30"
## [1] NA
## [1] NA NA
- Ejercicios:
Hacer los ejercicios en la sección 16.2.4 del libro en español
25.12 Extrayendo parte de las fecha-hora
- year()
- month()
- mday()
- yday()
- wday()
Extraer y redondear (lubridate): sacar una parte de una fecha, o llevarla a un borde.
year(),month(),mday(),yday(),wday(): el año, el mes, el día del mes, el día del año (1 a 366) y el día de la semana.month(x, label = TRUE)ywday(x, label = TRUE): devuelven el nombre en vez del número, como factor ordenado. Es lo que hace falta para graficar.hour(),minute(),second(): las partes de la hora.ceiling_date()
floor_date()
round_date()
floor_date(x, "month"): lleva la fecha hacia atrás al principio del mes. Es la manera de agrupar por mes sin perder el año.ceiling_date(): hacia adelante;round_date(): al borde más cercano.
Para contar por mes, floor_date(fecha, "month") es mejor que month(fecha).
month() devuelve un número del 1 al 12 y mezcla los años: el enero de 2016 y
el de 2017 caen en el mismo grupo. floor_date() conserva el año, así que la
serie sale en orden y sin mezclas.
- Ejercicios:
Hacer estos ejercicios en la sección 16.3.4 del libro en español. Entregar por MSTeams.
¿Cómo cambia la demora promedio durante el curso de un día? ¿Deberías usar horario_salida o salida_programada? ¿Por qué?
¿En qué día de la semana deberías salir si quieres minimizar las posibilidades de una demora?
Confirma nuestra hipótesis de que las salidas programadas en los minutos 20-30 y 50-60 están causadas por los vuelos programados que salen más temprano. Pista: crea una variable binaria que te diga si un vuelo tuvo o no demora.
25.13 Lapso de tiempo
Aquí está la idea más importante del capítulo. lubridate ofrece tres maneras
de hablar de un lapso de tiempo, y no son intercambiables:
Duraciones, períodos e intervalos: tres maneras de medir un lapso.
- Duración (
dseconds(),ddays(),dyears()): un número exacto de segundos. Undyears(1)son siempre 31 536 000 segundos, haya bisiesto o no. Es tiempo de físico. - Período (
days(),months(),years()): unidades humanas, que se estiran según el calendario.years(1)sumado al 1 de marzo da el 1 de marzo siguiente, tenga ese año 365 o 366 días. - Intervalo (
inicio %--% fin): un lapso con puntos de inicio y fin concretos. Solo con un intervalo se puede saber cuántos días tuvo de verdad, porque se sabe entre qué fechas está.
La regla práctica: períodos para hablar con humanos, duraciones para hacer física, intervalos cuando importa exactamente cuándo empezó.
El ejemplo que lo deja claro: si sumas dyears(1) (una duración) a una fecha justo
antes de un 29 de febrero, caes en un día distinto del que esperabas, porque un
año bisiesto tiene un día más y la duración no lo sabe. Con years(1) (un
período) caes donde cualquier persona diría. Los dos son correctos; contestan
preguntas distintas.
25.14 duraciones
# ¿Qué edad tiene Charles Darwin? fecha de naciemiento February 12, 1809
edad_h <- today() - ymd("1809-02-12")
edad_h <- today() - ymd(18090212)
edad_h## Time difference of 79467 days
## [1] "6865948800s (~217.57 years)"
Otras funciones de duración
## [1] "15s"
## [1] "600s (~10 minutes)"
## [1] "43200s (~12 hours)" "86400s (~1 days)"
## [1] "0s" "86400s (~1 days)" "172800s (~2 days)"
## [4] "259200s (~3 days)" "345600s (~4 days)" "432000s (~5 days)"
## [1] "1814400s (~3 weeks)"
## [1] "31557600s (~1 years)"
25.15 Puede agregar periodos de tiempo
## [1] "63115200s (~2 years)"
## [1] "38869200s (~1.23 years)"
## [1] "2026-09-08"
## [1] "2025-09-08 18:00:00 UTC"
25.16 períodos
## [1] "2016-03-12 13:00:00 EST"
## [1] "2016-03-13 14:00:00 EDT"
## [1] "15S"
## [1] "10M 0S"
## [1] "12H 0M 0S" "24H 0M 0S"
## [1] "7d 0H 0M 0S"
## [1] "1m 0d 0H 0M 0S" "2m 0d 0H 0M 0S" "3m 0d 0H 0M 0S" "4m 0d 0H 0M 0S"
## [5] "5m 0d 0H 0M 0S" "6m 0d 0H 0M 0S"
#> [1] "1m 0d 0H 0M 0S" "2m 0d 0H 0M 0S" "3m 0d 0H 0M 0S" "4m 0d 0H 0M 0S"
#> [5] "5m 0d 0H 0M 0S" "6m 0d 0H 0M 0S"
weeks(3)## [1] "21d 0H 0M 0S"
## [1] "1y 0m 0d 0H 0M 0S"
## [1] "60m 10d 0H 0M 0S"
## [1] "50d 25H 2M 0S"
## [1] "2016-12-31 06:00:00 UTC"
## [1] "2017-01-01"
## [1] "2016-03-13 14:00:00 EDT"
## [1] "2016-03-13 13:00:00 EDT"
## [1] "60m 10d 0H 0M 0S"
## [1] "50d 25H 2M 0S"
- intervalos
25.18 Intervalos
- %–% start end
Un intervalo es lo único que sabe entre qué dos fechas está, y por eso es lo
único que puede convertirse a cualquier unidad sin ambigüedad. La receta es
siempre la misma: se construye el intervalo con %--% y se divide entre la unidad
que se quiera.
25.19 Resumen
Para escoger entre los tres, la pregunta es qué quieres que pase con las excepciones del calendario:
| Quieres | Usa | Ejemplo |
|---|---|---|
| Un número exacto de segundos, sin excepciones | duración | tiempo de reacción, decaimiento |
| Lo que diría una persona con un calendario | período | “la cita es dentro de un mes” |
| Cuánto tiempo hay entre dos fechas concretas | intervalo | edad, duración de un experimento |
- Ejercicios:
Hacer los ejercicios en la sección 16.4.5 del libro en español
25.20 Zonas horarias
- Sys.timezone()
- head(OlsonNames())
- tz= “”
Una fecha-hora sin zona horaria está incompleta: las 3 de la tarde en San Juan y las 3 de la tarde en Madrid son dos instantes distintos. R guarda la zona como un atributo de la fecha-hora, y por defecto usa la de tu computadora.
Zonas horarias (base R y lubridate).
Sys.timezone(): en cuál zona está tu computadora ahora mismo.OlsonNames(): la lista completa de nombres válidos, unos 600. Los nombres van en la forma"Continente/Ciudad", como"America/Puerto_Rico", y no con siglas como"AST", que son ambiguas.with_tz(x, "zona"): el mismo instante, mostrado en otra zona. Cambia lo que se lee, no el momento.force_tz(x, "zona"): la misma hora del reloj, declarada en otra zona. Cambia el momento.
with_tz() y force_tz() se confunden y hacen cosas opuestas. with_tz() es
“esta reunión, ¿a qué hora es en Madrid?”. force_tz() es “esta hora que anoté
sin decir dónde, en realidad era hora de Madrid”. Si te equivocas de función, los
datos se mueven varias horas y nada avisa.
- Ejercicios:
Seleccionar 3 archivos de los vuelos que salen o llegan a PR, (el código del aeropuerto es “SJU”) de la base de datos de https://www.transtats.bts.gov/DL_SelectFields.asp?Table_ID=236 Pueden ser el mismo mes en 3 diferentes años o 3 diferentes meses en el mismo año.
Repite la mayoría de los análisis enseñado arriba (como práctica).
Evaluar el tiempo de retraso de los vuelos que salen de SJU en cada periodo seleccionado, y haz una gráfica para visualizar el patrón
Cuál es el día preferible para no tener retraso
Cuál es la mejor hora de salida para no tener retraso
Compara por lo menos 3 diferentes líneas saliendo de SJU y el periodo de retraso.
25.21 Ejercicios del capítulo
Sobre los ejercicios de este capítulo. Cada ejercicio se entrega con tres
cosas: el script, el resultado (que el bloque corra) y una explicación
en palabras. Los ejercicios usan Datos/crimen_pr.csv, que está en el
repositorio del curso y trae fecha y hora de verdad, no vuelos ni la fecha de
nacimiento de Darwin, que son los del capítulo. La hoja completa para entregar
está en Ejercicios/Ejercicios_Capitulo_25_fechas.Rmd.
25.21.1 Ejercicio 1. La misma fecha, escrita de cinco maneras
Convierte a fecha estos cinco textos, que son todos el mismo día, cada uno con
la función de lubridate que le corresponda: "2016-08-01", "01/08/2016",
"August 1, 2016", "20160801" y "01.08.2016".
Verificación: los cinco tienen que dar exactamente la misma fecha. Compruébalo
con n_distinct(), que debe dar 1.
En palabras: prueba ahora mdy("01/08/2016") y mira qué sale. ¿Dio error?
Explica por qué este es el error más peligroso de todo el capítulo y qué
comprobación harías en una columna de 5000 fechas para detectarlo.
25.21.2 Ejercicio 2. Extraer las partes
Lee Datos/crimen_pr.csv. Saca el día de la semana y la hora de cada incidente, y
cuenta cuántos hay de cada uno. Usa label = TRUE para que los días salgan con su
nombre.
Verificación: tienen que salir 7 días de la semana y 24 horas del día, todas con algún caso.
En palabras: ¿cuál día y cuál hora concentran más incidentes? Haz una gráfica
de barras de cada uno. ¿Qué habría pasado si hubieras usado wday() sin
label = TRUE para graficar?
25.21.3 Ejercicio 3. %/% y %%
Muchos archivos guardan la hora como un número de la forma HHMM. Toma este
vector: horas <- c(1730, 905, 2359, 15, 1200).
Sepáralo en hora y minutos con %/% y %%, y calcula los minutos transcurridos
desde la medianoche de cada uno. Después convierte los mismos valores a hora de
verdad con hm() de lubridate y comprueba que coinciden.
Verificación: los minutos desde la medianoche tienen que ser
1050, 545, 1439, 15, 720. La suma de los cinco es 3769.
En palabras: el valor 15 significa las 00:15, no las 15:00. Explica por qué
%/% y %% lo resuelven bien sin ningún caso especial. ¿Qué habría pasado si
hubieras intentado partir el número con str_sub(), cortando por posición?
25.21.4 Ejercicio 4. Duración contra período
Toma el 1 de marzo de 2024 (un año bisiesto) y réstale un año de las dos maneras:
con years(1) y con dyears(1). Haz lo mismo sumando un mes al 31 de enero, con
months(1).
Verificación: las dos restas dan días distintos, y la suma al 31 de enero
da NA.
En palabras: ninguno de los tres resultados es un error del programa.
Explica qué contesta cada uno, y por qué el NA es la respuesta más honesta que
puede dar R a “¿qué día es un mes después del 31 de enero?”.
25.21.5 Ejercicio 5. Intervalos
Con crimen_pr.csv, construye el intervalo que va de la primera a la última fecha
del archivo con %--%, y exprésalo en días, en semanas y en meses.
Verificación: el intervalo tiene que ser de unos dos meses, y en días tiene que dar un número entre 60 y 65.
En palabras: divide también entre dmonths(1) en vez de months(1) y compara.
¿Por qué no dan lo mismo? ¿Cuál de los dos números pondrías en la sección de
métodos de un informe?
25.21.6 Ejercicio 6. Redondear para agrupar
Con el mismo archivo, cuenta los incidentes por semana usando
floor_date(Fecha, "week"), y haz una gráfica de líneas de la serie.
Verificación: deben salir unas nueve o diez semanas, y la primera y la última tienen que tener bastantes menos casos que las del medio.
En palabras: ¿por qué los extremos son más bajos? Ya viste este mismo problema en el capítulo de importar datos. ¿Qué harías con esas dos semanas antes de enseñarle la gráfica a alguien?
25.21.7 Ejercicio 7 (BONO). Zonas horarias
Averigua en cuál zona horaria está tu computadora con Sys.timezone(). Después
toma el instante "2016-08-01 15:30:00" en "America/Puerto_Rico" y míralo en
"Europe/Madrid" y en "America/Los_Angeles" con with_tz(). Prueba también
force_tz() con el mismo instante.
Verificación: con with_tz() el instante es el mismo y solo cambia la hora que
se lee; con force_tz() cambia el instante.
En palabras: explica la diferencia con tus palabras. Puerto Rico no cambia de horario de verano y el este de Estados Unidos sí: ¿qué le pasa a la diferencia de horas entre San Juan y Nueva York a lo largo del año, y qué problema causaría eso al comparar datos de vuelos de los dos sitios?