本章将展示如何在 R 中使用日期和时间。乍一看,日期和时间似乎很简单。在日常生活中一直在使用它们,它们似乎不会引起太多混乱。但是,对日期和时间了解得越多,它们似乎就越复杂。请尝试以下三个看似简单的问题:
- 每年有365天吗?
- 每天有24小时吗?
- 每分钟有 60 秒吗?
我确定不是每年都有 365 天,但是您知道确定一年是否为闰年的完整规则吗? (它分为三个部分。)您可能还记得世界上许多地方都使用夏令时 (DST),因此有些日子有 23 小时,有些日子有 25 小时。您可能不知道有些分钟有 61 秒,因为因为地球的自转正在逐渐减慢,所以不时添加闰秒。
日期和时间很困难,因为它们必须调和两个物理现象(地球自转及其绕太阳公转)和整个地缘政治现象,包括月份,时区和DST。本章不会教你关于日期和时间的每一个细节,但它会给你一个坚实的实践技能基础,帮助你解决常见的数据分析挑战。
本章将重点介绍 lubridate
包,它可以更轻松地在 R 中处理日期和时间。 lubridate
不是核心 tidyverse
的一部分,因为只有在处理日期/时间时才需要它。 我们还需要 nycflights13
来获取练习数据。
library(tidyverse)
library(lubridate)
library(nycflights13)
1. Creating date/times
有三种类型的日期/时间数据指的是时间:
data: 日期。Tibbles 将其打印为 date
。
time: 时间。Tibbles 将此打印为 time
。
data-time:日期时间。它唯一地标识了一个瞬间(通常到最近的秒)。 Tibbles 将其打印为 dttm
。
在本章中,我们将只关注日期和日期时间,要获取当前日期或日期时间,可以使用 today()
或 now()
:
today()
#> [1] "2020-10-09"
now()
#> [1] "2020-10-09 11:59:37 UTC"
否则,可能会通过三种方式创建日期/时间:
- 从一个字符串。
- 来自各个日期时间组件。
- 从现有的日期/时间对象。
它们的工作方式如下。
(1) From strings
日期/时间数据通常以字符串形式出现。 您已经看到了一种将字符串解析为日期时间中的日期时间的方法。 另一种方法是使用 lubridate 提供的助手。 一旦您指定了组件的顺序,它们就会自动计算出格式。 要使用它们,请确定日期中出现的年、月和日的顺序,然后按相同顺序排列“y”、“m”和“d”。 这为您提供了将解析您的日期的 lubridate 函数的名称。 例如:
ymd("2017-01-31")
#> [1] "2017-01-31"
mdy("January 31st, 2017")
#> [1] "2017-01-31"
dmy("31-Jan-2017")
#> [1] "2017-01-31"
这些功能也采用未加引号的数字。 这是创建单个日期/时间对象的最简洁的方法,因为您在过滤日期/时间数据时可能需要。 ymd() 简短而明确:
ymd(20170131)
#> [1] "2017-01-31"
ymd()创建日期。 要创建日期时间,请在解析函数的名称中添加下划线和“h”、“m”和“s”中的一个或多个:
ymd_hms("2017-01-31 20:11:59")
#> [1] "2017-01-31 20:11:59 UTC"
mdy_hm("01/31/2017 08:01")
#> [1] "2017-01-31 08:01:00 UTC"
您还可以通过提供时区来强制从日期创建日期时间:
ymd(20170131, tz = "UTC")
#> [1] "2017-01-31 UTC"
(2) From individual components
有时您会将日期时间的各个组件分布在多个列中,而不是单个字符串。 这是我们在航班数据中的内容:
flights %>%
select(year, month, day, hour, minute)
#> # A tibble: 336,776 x 5
#> year month day hour minute
#>
#> 1 2013 1 1 5 15
#> 2 2013 1 1 5 29
#> 3 2013 1 1 5 40
#> 4 2013 1 1 5 45
#> 5 2013 1 1 6 0
#> 6 2013 1 1 5 58
#> # … with 336,770 more rows
要从此类输入创建日期/时间,请使用 make_date() 表示日期,或使用 make_datetime() 表示日期时间:
flights %>%
select(year, month, day, hour, minute) %>%
mutate(departure = make_datetime(year, month, day, hour, minute))
#> # A tibble: 336,776 x 6
#> year month day hour minute departure
#>
#> 1 2013 1 1 5 15 2013-01-01 05:15:00
#> 2 2013 1 1 5 29 2013-01-01 05:29:00
#> 3 2013 1 1 5 40 2013-01-01 05:40:00
#> 4 2013 1 1 5 45 2013-01-01 05:45:00
#> 5 2013 1 1 6 0 2013-01-01 06:00:00
#> 6 2013 1 1 5 58 2013-01-01 05:58:00
#> # … with 336,770 more rows
让我们对航班的四个时间列中的每一个都做同样的事情。 时间以稍微奇怪的格式表示,因此我们使用模数算法来提取小时和分钟组件。 创建日期时间变量后,我将重点关注我们将在本章其余部分中探讨的变量。
make_datetime_100 %
filter(!is.na(dep_time), !is.na(arr_time)) %>%
mutate(
dep_time = make_datetime_100(year, month, day, dep_time),
arr_time = make_datetime_100(year, month, day, arr_time),
sched_dep_time = make_datetime_100(year, month, day, sched_dep_time),
sched_arr_time = make_datetime_100(year, month, day, sched_arr_time)
) %>%
select(origin, dest, ends_with("delay"), ends_with("time"))
flights_dt
#> # A tibble: 328,063 x 9
#> origin dest dep_delay arr_delay dep_time sched_dep_time
#>
#> 1 EWR IAH 2 11 2013-01-01 05:17:00 2013-01-01 05:15:00
#> 2 LGA IAH 4 20 2013-01-01 05:33:00 2013-01-01 05:29:00
#> 3 JFK MIA 2 33 2013-01-01 05:42:00 2013-01-01 05:40:00
#> 4 JFK BQN -1 -18 2013-01-01 05:44:00 2013-01-01 05:45:00
#> 5 LGA ATL -6 -25 2013-01-01 05:54:00 2013-01-01 06:00:00
#> 6 EWR ORD -4 12 2013-01-01 05:54:00 2013-01-01 05:58:00
#> # … with 328,057 more rows, and 3 more variables: arr_time ,
#> # sched_arr_time , air_time
有了这些数据,我可以可视化一年中出发时间的分布:
flights_dt %>%
ggplot(aes(dep_time)) +
geom_freqpoly(binwidth = 86400) # 86400 seconds = 1 day

或在一天内:
flights_dt %>%
filter(dep_time %
ggplot(aes(dep_time)) +
geom_freqpoly(binwidth = 600) # 600 s = 10 minutes

请注意,当您在数字上下文中(如在直方图中)使用日期时间时,1 表示 1 秒,因此 86400 的 binwidth 表示一天。 对于日期,1 表示 1 天。
(3) From other types
您可能需要在日期时间和日期之间切换。 这就是 as_datetime() 和 as_date() 的工作:
as_datetime(today())
#> [1] "2020-10-09 UTC"
as_date(now())
#> [1] "2020-10-09"
有时你会得到日期/时间作为来自“Unix Epoch”,1970-01-01 的数字偏移量。 如果偏移量以秒为单位,则使用 as_datetime(); 如果以天为单位,请使用 as_date()。
as_datetime(60 * 60 * 10)
#> [1] "1970-01-01 10:00:00 UTC"
as_date(365 * 10 + 2)
#> [1] "1980-01-01"
参考:https://r4ds.had.co.nz/dates-and-times.html