R

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

2021-01-28  本文已影响0人  名本无名

前言

我们接着上节未讲完的日期/时间处理

4. 时间跨度

接下来,我们将介绍日期算术运算。包括加法、减法和除法。

在这里,我们要先了解三个代表时间跨度的类:

4.1 periods

R 中,当两个日期相减时,将得到一个 difftime 对象

> h_age <- today() - ymd(19791014)
> h_age
Time difference of 15082 days

difftime 类对象记录了秒,分钟,小时,天或周的时间跨度

这种模糊性会使 difftime 有点难以处理,因此 lubridate 提供了一种始终用秒来表示的方法:duration

> as.duration(h_age)
[1] "1303084800s (~41.29 years)"

duration 有许多方便的构造函数

> dseconds(15)
[1] "15s"
> dminutes(10)
[1] "600s (~10 minutes)"
> dhours(c(12, 24))
[1] "43200s (~12 hours)" "86400s (~1 days)"  
> ddays(0:5)
[1] "0s"                "86400s (~1 days)"  "172800s (~2 days)" "259200s (~3 days)" "345600s (~4 days)"
[6] "432000s (~5 days)"
> dweeks(3)
[1] "1814400s (~3 weeks)"
> dyears(1)
[1] "31557600s (~1 years)"

duration 始终以秒为单位来记录时间,可以通过传入分钟、小时、天、周和年等单位来创建较大的单位。

你可以使用加法或乘法

> 2 * dyears(1)
[1] "63115200s (~2 years)"
> dyears(1) + dweeks(12) + dhours(15)
[1] "38869200s (~1.23 years)"

对天的加法减法

tomorrow <- today() + ddays(1)
last_year <- today() - dyears(1)

但是由于 duration 代表的是精确的时间,因此有时可能会得到意外的结果

> one_pm <- ymd_hms("2016-03-12 13:00:00", tz = "America/New_York")
> one_pm
[1] "2016-03-12 13:00:00 EST"
> one_pm + ddays(1)
[1] "2016-03-13 14:00:00 EDT"

为什么会变成下午两点呢?你可以注意到了,时区已经变了。

由于 DST 的原因,312 日只有 23 个小时,因此,如果加上一天的秒数,我们将得到不同的时间。

4.2 Periods

为了解决这个问题,lubridate 提供了 periods,它是一个时间跨度,但是没有固定的秒数。

而是人为定义的时间单位,如天或月。

> one_pm
[1] "2016-03-12 13:00:00 EST"
> one_pm + days(1)
[1] "2016-03-13 13:00:00 EDT"

类似 duration,也有许多函数用于创建 periods

> seconds(15)
[1] "15S"
> minutes(10)
[1] "10M 0S"
> hours(c(12, 24))
[1] "12H 0M 0S" "24H 0M 0S"
> days(7)
[1] "7d 0H 0M 0S"
> months(1:6)
[1] "1m 0d 0H 0M 0S" "2m 0d 0H 0M 0S" "3m 0d 0H 0M 0S" "4m 0d 0H 0M 0S" "5m 0d 0H 0M 0S"
[6] "6m 0d 0H 0M 0S"
> weeks(3)
[1] "21d 0H 0M 0S"
> years(1)
[1] "1y 0m 0d 0H 0M 0S"

periods 进行加法和乘法

> 10 * (months(6) + days(1))
[1] "60m 10d 0H 0M 0S"
> days(50) + hours(25) + minutes(2)
[1] "50d 25H 2M 0S"

durations 相比较,periods 更符合我们的预期

> ymd("2016-01-01") + dyears(1)
[1] "2016-12-31 06:00:00 UTC"
> ymd("2016-01-01") + years(1)
[1] "2017-01-01"
> one_pm + ddays(1)
[1] "2016-03-13 14:00:00 EDT"
> one_pm + days(1)
[1] "2016-03-13 13:00:00 EDT"

来让我用 periods 来解决与航班日期有关的问题。有些飞机在离开纽约市之前似乎已经到达目的地

> flights_dt %>% 
+     filter(arr_time < dep_time) 
# A tibble: 10,633 x 9
   origin dest  dep_delay arr_delay dep_time            sched_dep_time      arr_time           
   <chr>  <chr>     <dbl>     <dbl> <dttm>              <dttm>              <dttm>             
 1 EWR    BQN           9        -4 2013-01-01 19:29:00 2013-01-01 19:20:00 2013-01-01 00:03:00
 2 JFK    DFW          59        NA 2013-01-01 19:39:00 2013-01-01 18:40:00 2013-01-01 00:29:00
 3 EWR    TPA          -2         9 2013-01-01 20:58:00 2013-01-01 21:00:00 2013-01-01 00:08:00
 4 EWR    SJU          -6       -12 2013-01-01 21:02:00 2013-01-01 21:08:00 2013-01-01 01:46:00
 5 EWR    SFO          11       -14 2013-01-01 21:08:00 2013-01-01 20:57:00 2013-01-01 00:25:00
 6 LGA    FLL         -10        -2 2013-01-01 21:20:00 2013-01-01 21:30:00 2013-01-01 00:16:00
 7 EWR    MCO          41        43 2013-01-01 21:21:00 2013-01-01 20:40:00 2013-01-01 00:06:00
 8 JFK    LAX          -7       -24 2013-01-01 21:28:00 2013-01-01 21:35:00 2013-01-01 00:26:00
 9 EWR    FLL          49        28 2013-01-01 21:34:00 2013-01-01 20:45:00 2013-01-01 00:20:00
10 EWR    FLL          -9       -14 2013-01-01 21:36:00 2013-01-01 21:45:00 2013-01-01 00:25:00
# … with 10,623 more rows, and 2 more variables: sched_arr_time <dttm>, air_time <dbl>

这些是夜间航班。起飞和到达时间使用了相同的日期信息,但这些航班是在第二天到达的。

我们可以通过在每个夜间航班的到达时间上加上 days(1) 来解决这个问题

flights_dt <- flights_dt %>% 
  mutate(
    overnight = arr_time < dep_time,
    arr_time = arr_time + days(overnight * 1),
    sched_arr_time = sched_arr_time + days(overnight * 1)
  )

现在,我们所有的飞行都遵循物理定律

> flights_dt %>% 
+     filter(overnight, arr_time < dep_time) 
# A tibble: 0 x 10
# … with 10 variables: origin <chr>, dest <chr>, dep_delay <dbl>, arr_delay <dbl>, dep_time <dttm>,
#   sched_dep_time <dttm>, arr_time <dttm>, sched_arr_time <dttm>, air_time <dbl>, overnight <lgl>

4.3 intervals

dyears(1) / ddays(365) 应该返回的是 1,因此 durations 总是表示秒数,而一年表示为 365 天的秒数

years(1) / days(1) 返回的是什么?如果是 2015 年返回的是 365,而 2016 返回的是 366

对于 lubridate 来说,当没有足够的信息来给出一个明确的答案。它会给出一个估计值

> years(1) / days(1)
[1] 365.25

如果你想要更精确的值,可以使用 interval

interval 是一个有起点的 durations,因此您可以准确地确定它的持续时间

> next_year <- today() + years(1)
> (today() %--% next_year) / ddays(1)
[1] 365

要找出间隔中有多少个周期,您需要使用整数除法

> (today() %--% next_year) %/% days(1)
[1] 365

4.4 总结

如何在持续时间、周期和间隔之间进行选择?一如既往,选择最简单的数据结构来解决您的问题。

image.png

4.5 思考练习

  1. 创建一个向量,存储了 2015 年每月的第一天。创建一个包含本年度每月的第一天的日期向量。

  2. 编写一个函数,给定您的生日(以日期为单位),返回您的年龄(岁)。

5. 时区

获取当前时区

> Sys.timezone()
[1] "Asia/Shanghai"

使用 OlsonNames() 查看所有时区名称的完整列表:

> length(OlsonNames())
[1] 593
> head(OlsonNames())
[1] "Africa/Abidjan"     "Africa/Accra"       "Africa/Addis_Ababa" "Africa/Algiers"    
[5] "Africa/Asmara"      "Africa/Asmera"

R 中,时区仅仅是控制打印日期时间的属性。

例如,下面这三个对象表示同一时间点

> (x1 <- ymd_hms("2015-06-01 12:00:00", tz = "America/New_York"))
[1] "2015-06-01 12:00:00 EDT"
> (x2 <- ymd_hms("2015-06-01 18:00:00", tz = "Europe/Copenhagen"))
[1] "2015-06-01 18:00:00 CEST"
> (x3 <- ymd_hms("2015-06-02 04:00:00", tz = "Pacific/Auckland"))
[1] "2015-06-02 04:00:00 NZST"

您可以使用减法验证它们是否是同一时间

> x1 - x2
Time difference of 0 secs
> x1 - x3
Time difference of 0 secs

除非另有说明,否则 lubridate 始终使用 UTC

UTC(协调世界时)是科学界使用的标准时区,大致相当于其前身 GMT(格林威治标准时间)

> x4 <- c(x1, x2, x3)
> x4
[1] "2015-06-01 12:00:00 EDT" "2015-06-01 12:00:00 EDT" "2015-06-01 12:00:00 EDT"

你可以通过两种方式来更改时区

> x4a <- with_tz(x4, tzone = "Australia/Lord_Howe")
> x4a
[1] "2015-06-02 02:30:00 +1030" "2015-06-02 02:30:00 +1030" "2015-06-02 02:30:00 +1030"
> x4a - x4
Time differences in secs
[1] 0 0 0
> x4b <- force_tz(x4, tzone = "Australia/Lord_Howe")
> x4b
[1] "2015-06-01 12:00:00 +1030" "2015-06-01 12:00:00 +1030" "2015-06-01 12:00:00 +1030"
> x4b - x4
Time differences in hours
[1] -14.5 -14.5 -14.5
上一篇下一篇

猜你喜欢

热点阅读