今天看啥  ›  专栏  ›  名本无名

R 数据处理(十六)—— lubridate

名本无名  · 简书  ·  · 2021-01-27 21:28

1. 前言

本节我们将介绍 R 中的时间和日期的处理。乍一看,你可能觉得这很简单。

让我们来看看下面三个问题:

  • 每年都是 365 天?
  • 每天都有 24 小时?
  • 每分钟是 60 秒?

当然,你肯定知道一年不一定都是 365 天,但是你知道哪一年是闰年吗?

世界上很多地方都使用夏令时,所以有些日子是 23 小时,有些日子是 25 小时。

你可能不知道有些分钟有 61 秒,因为地球自转正逐渐减慢,时不时地增加闰秒。

有时候,时间和日期是很困难的。因为需要协调地球的自转和公转与地区的月份、时区、夏令时之间的关系。

下面我们将详细介绍如何处理这些数据

1.1 导入

本节的重点是使用 lubridate 包来处理 R 中的日期和时间。

lubridate 不是核心的 tidyverse 包,所以需要手动导入。并利用 nycflights13 作为例子数据来练习

library(tidyverse)

library(lubridate)
library(nycflights13)

2. 创建时间/日期

总共有 3 种形式的日期/时间数据

  • date : tibble 中标题的 <date>
  • time : 一天内的时间, tibble 中标题的 <time>
  • date-time : 日期+时间,唯一标识时间点, tibble 中标题的 <dttm>

本节我们只关注日期和日期+时间,并尽可能使其简单化,如果能用日期就不用日期+时间

要获取当前的日期和日期+时间,可以

> today()
[1] "2021-01-27"
> now()
[1] "2021-01-27 19:48:46 CST"

此外,有三种方法创建日期/时间

  • 根据字符串创建
  • 单个日期-时间组件
  • 现存的日期-时间

它们的工作方式如下

2.1 字符串

日期/时间通常以字符串的形式出现,一种方式我们已经在前面的 readr 中介绍过了,另一种方法是使用 lubridate 包提供的工具

通过制定字符串中年( y )、月( m )、日( d )的出现顺序,会自动转换为日期格式。

> ymd("2017-01-31")
[1] "2017-01-31"
> mdy("January 31st, 2017")
[1] "2017-03-01"
> dmy("31-Jan-2017")
[1] "2017-01-31"

这些函数也可以接受不带引号的数字

> ymd(20170131)
[1] "2017-01-31"

想要创建日期时间的话,可以在 ymd ( mdy dmy 等) 后面添加一个下划线以及一个或多个 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.2 单个组件

有时候我们的数据会像 flights 表一样,日期时间是分开的

> flights %>% 
+     select(year, month, day, hour, minute)
# A tibble: 336,776 x 5
    year month   day  hour minute
   <int> <int> <int> <dbl>  <dbl>
 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
 7  2013     1     1     6      0
 8  2013     1     1     6      0
 9  2013     1     1     6      0
10  2013     1     1     6      0
# … with 336,766 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          
   <int> <int> <int> <dbl>  <dbl> <dttm>             
 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
 7  2013     1     1     6      0 2013-01-01 06:00:00
 8  2013     1     1     6      0 2013-01-01 06:00:00
 9  2013     1     1     6      0 2013-01-01 06:00:00
10  2013     1     1     6      0 2013-01-01 06:00:00
# … with 336,766 more rows

让我们对 flights 剩下的其他四列时间进行格式化

> flights_dt <- flights %>% 
+     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      arr_time           
   <chr>  <chr>     <dbl>     <dbl> <dttm>              <dttm>              <dttm>             
 1 EWR    IAH           2        11 2013-01-01 05:17:00 2013-01-01 05:15:00 2013-01-01 08:30:00
 2 LGA    IAH           4        20 2013-01-01 05:33:00 2013-01-01 05:29:00 2013-01-01 08:50:00
 3 JFK    MIA           2        33 2013-01-01 05:42:00 2013-01-01 05:40:00 2013-01-01 09:23:00
 4 JFK    BQN          -1       -18 2013-01-01 05:44:00 2013-01-01 05:45:00 2013-01-01 10:04:00
 5 LGA    ATL          -6       -25 2013-01-01 05:54:00 2013-01-01 06:00:00 2013-01-01 08:12:00
 6 EWR    ORD          -4        12 2013-01-01 05:54:00 2013-01-01 05:58:00 2013-01-01 07:40:00
 7 EWR    FLL          -5        19 2013-01-01 05:55:00 2013-01-01 06:00:00 2013-01-01 09:13:00
 8 LGA    IAD          -3       -14 2013-01-01 05:57:00 2013-01-01 06:00:00 2013-01-01 07:09:00
 9 JFK    MCO          -3        -8 2013-01-01 05:57:00 2013-01-01 06:00:00 2013-01-01 08:38:00
10 LGA    ORD          -2         8 2013-01-01 05:58:00 2013-01-01 06:00:00 2013-01-01 07:53:00
# … with 328,053 more rows, and 2 more variables: sched_arr_time <dttm>, air_time <dbl>

然后,我们可以看看全年的出发时间的分布

> flights_dt %>% 
+     ggplot(aes(dep_time)) + 
+     geom_freqpoly(binwidth = 86400) # 86400 seconds = 1 day
image.png

也可以查看一天内的出发情况

> flights_dt %>% 
+     filter(dep_time < ymd(20130102)) %>% 
+     ggplot(aes(dep_time)) + 
+     geom_freqpoly(binwidth = 600) # 600 s = 10 minutes
image.png

注意 :在日期中 1 表示一天,在时间中 1 表示一秒, 86400 秒为一天

2.3 其他类型

有时你可能需要自日期-时间和日期之间相互切换,可以使用 as_datetime() as_date()

> as_datetime(today())
[1] "2021-01-27 UTC"
> as_date(now())
[1] "2021-01-27"

如果想获取相对于 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"

2.4 思考练习

  1. 如果解析包含无效日期的字符串会怎样?
ymd(c("2010-10-10", "bananas"))
  1. tzone 参数对 today() 有什么作用?

  2. 使用相应的 lubridate 函数来解析以下每个日期

d1 <- "January 1, 2010"
d2 <- "2015-Mar-07"
d3 <- "06-Jun-2017"
d4 <- c("August 19 (2015)", "July 1 (2015)")
d5 <- "12/30/14" # Dec 30, 2014

3. 日期-时间组件

这一部分的重点是介绍访问器函数,用于获取或设置日期-时间组件。

3.1 获取组件

你可以使用下面的访问器函数来获取日期时间组件:

  • year()
  • month()
  • mday() 月份的天,
  • yday() 一年中的天,
  • wday() 星期,
  • hour()
  • minute()
  • second()
> datetime <- ymd_hms("2020-07-08 12:34:56")
> year(datetime)
[1] 2020
> month(datetime)
[1] 7
> mday(datetime)
[1] 8
> yday(datetime)
[1] 190
> wday(datetime)
[1] 4

对于 month() wday() ,您可以设置 label = TRUE 来返回一个月或星期的缩写名称。

设置 abbr = FALSE 返回全名

> month(datetime, label = TRUE)
[1]  7
Levels:  1 <  2 <  3 <  4 <  5 <  6 <  7 <  8 <  9 < 10 < 11 < 12
> wday(datetime, label = TRUE, abbr = FALSE)
[1] 星期三
Levels: 星期日 < 星期一 < 星期二 < 星期三 < 星期四 < 星期五 < 星期六

我们可以使用 wday() 来查看一周的航班

> flights_dt %>% 
+     mutate(wday = wday(dep_time, label = TRUE)) %>% 
+     ggplot(aes(x = wday)) +
+     geom_bar() +
+     theme(text = element_text(family='Kai')) # 显示中文
image.png

如果我们看一小时内每分钟的平均延迟,会看到一个有趣的现象。

> flights_dt %>% 
+   mutate(minute = minute(dep_time)) %>% 
+   group_by(minute) %>% 
+   summarise(
+     avg_delay = mean(arr_delay, na.rm = TRUE),
+     n = n()) %>% 
+   ggplot(aes(minute, avg_delay)) +
+   geom_line()
`summarise()` ungrouping output (override with `.groups` argument)
image.png

我们可以看到,在 20-30 分钟和 50-60 分钟起飞的航班延误要比其他时间少得多

有趣的是,如果我们看预计出发时间,则没有这样的模式

> sched_dep <- flights_dt %>% 
+     mutate(minute = minute(sched_dep_time)) %>% 
+     group_by(minute) %>% 
+     summarise(
+         avg_delay = mean(arr_delay, na.rm = TRUE),
+         n = n())
`summarise()` ungrouping output (override with `.groups` argument)
> #> `summarise()` ungrouping output (override with `.groups` argument)
> 
> ggplot(sched_dep, aes(minute, avg_delay)) +
+     geom_line()
image.png

3.2 四舍五入

类似于浮点数,我们可以使用 floor_date() round_date() ceiling_date() 将日期四舍五入到附近的时间单位

> flights_dt %>% 
+     count(week = floor_date(dep_time, "week")) %>% 
+     ggplot(aes(week, n)) +
+     geom_line()

3.3 设置组件

您还可以使用每个访问器函数来设置日期/时间的组成部分

> (datetime <- ymd_hms("2010-10-08 12:34:56"))
[1] "2010-10-08 12:34:56 UTC"
> year(datetime) <- 2020
> datetime
[1] "2020-10-08 12:34:56 UTC"
> month(datetime) <- 01
> datetime
[1] "2020-01-08 12:34:56 UTC"
> hour(datetime) <- hour(datetime) + 1
> datetime
[1] "2020-01-08 13:34:56 UTC"

此外,您可以使用 update() 创建新的日期时间,而不是在原地进行修改。这也允许您一次设置多个值。

> update(datetime, year = 2020, month = 2, mday = 2, hour = 2)
[1] "2020-02-02 02:34:56 UTC"

如果设置的值太大,它们将自动顺延

> ymd("2015-02-01") %>% 
+     update(mday = 30)
[1] "2015-03-02"
> ymd("2015-02-01") %>% 
+     update(hour = 400)
[1] "2015-02-17 16:00:00 UTC"

您可以使用 update() 来显示一天的航班分布

> flights_dt %>% 
+     mutate(dep_hour = update(dep_time, yday = 1)) %>% 
+     ggplot(aes(dep_hour)) +
+     geom_freqpoly(binwidth = 300)
image.png

3.4 思考练习

  1. 一天中的飞行时间分布在一年中如何变化

  2. air_time 与出发和到达之间的持续时间进行比较,解释您的发现(提示:考虑机场的位置)。

  3. 平均延迟时间在一天中如何变化?您应该使用 dep_time 还是 sched_dep_time ?为什么?

  4. 如果想最大程度地减少飞机晚点的情况,应该在一周中的哪一天离开?




原文地址:访问原文地址
快照地址: 访问文章快照