B R日期时间
本节中我们将介绍在R中的日期与时间数据类型,以及如何通过lubridate包来处理日期与时间包来处理日期与时间。
B.1 日期-时间数据类型
R的基础包提供了三种日期-时间数据类型,采用POSIXt标准: - Date用于处理日期,它是一个整数,储存了距离1970年1月1日的天数,早于1970年1月1日的日期被储存为负数,Date不包括时间和时区信息; - POSIXct用于处理日期-时间,它也是一个整数,储存了以时间标准时间(UTC1)时区为准的,从1970年1月1日开始计时的秒数; - POSIXlt也用于处理日期-时间,但是它是将POSIXct储存在一个列表中。
B.2 lubridate包
lubridate
是tidyverse家族的成员,同样出自大神Hadley Wickham之手。如同tidyverse的其他成员一样,lubridate
包非常强大,它可以非常直观和灵活的处理日期与时间。lubridate
的所有函数都会返回POSIXct类型的日期-时间,缺省情况下,lubridate
使用UTC时区。lubridate
包括两类函数,一类用于处理时点数据(time instants),另一类则用于处理时段数据(time spans)
B.2.1 定义日期-时间变量
我们先加载lubridate包
函数today与now可以返回当前的日期和时间
## [1] "2023-11-11"
## [1] "2023-11-11 20:19:18 CST"
lubridate
包提供了将字符串转化成为日期-时间的函数族。在该函数族中,y表示年,m表示月份或者分钟,d表示日期,h表示小时,s表示秒,日期与时间的关键词之间通过_
连接,从而组成非常灵活多变的函数组合,将各种形式的字符串方便的转换为日期-时间,整个过程的用户体验非常友好。例如ymd_hms
函数可以转化各种形式的完整记录日期-时间的字符串:
## [1] "2017-11-28 14:02:00 UTC"
## [1] "2017-11-28 14:02:00 UTC"
## [1] "2017-11-28 14:02:00 UTC"
## [1] "2017-11-28 14:02:00 UTC"
感觉到lubridate
包的强大之处了吧!我们还可以随意组合关键词,从而转化不部分记录日期-时间信息的字符串,例如:
## [1] "2017-01-31"
## 04:04:03
## [1] "2001-07-01"
上面的例子中,hms::hms
可以将输入信息的三个元素对应为小时-分钟-秒,而yq
函数可以转化季度信息到日期。
## Date of length 0
## 04:04:03
## [1] "2001-07-01"
最后,我们也可以直接将整数转化为日期时间,lubridate
为我们提供了三个函数。as_datetime
函数可以将”1970-01-01 00:00:00 UTC”以来的秒数转化为日期-时间,as_date
将”1970-01-01”以来的天数转化为日期,hms::as.hms
将”00:00:00”以来的秒数转化为时间。
## [1] "2017-11-28 12:00:00 UTC"
## [1] "2017-11-28"
## 00:01:25
B.3 从日期-时间中取出元素
反过来,我们可以使用lubridate
包的date,year,month,quarter,day,wday,hour,minute,second
函数可以取出日期-时间的日期、年、月、季度、日、星期、小时、分钟以及秒钟。如下:
## [1] "2017-11-28"
## [1] 2017
## [1] 11
## [1] 28
## [1] 3
update
函数可以直接修改日期-时间,例如:
## [1] "2017-11-03 01:02:00 UTC"
lubridate
还提供了其他一些便利的函数
## [1] FALSE
## [1] TRUE
## [1] FALSE
B.4 日期-时间取整
lubridate
提供了四个日期和时间取整函数,这极大简化了我们的数据分析。在实践中,我们经常需要把数据聚合起来分析,一种常用的方式就是把同一月份的数据都取整到当月的第一天。
## [1] "2017-11-28 UTC"
## [1] "2017-12-01 UTC"
## [1] "2017-11-28 15:00:00 UTC"
## [1] "2017-10-31 14:02:00 UTC"
B.5 日期-时间的数学运算
lubridate
支持三种类型的时间段数据,时间长度(duration),按整秒计算;时间周期(period),以相应单位如年、日计算;时间区间(interval),包含一个开始时间和一个结束时间。在此基础上,lubridate
支持针对日期-时间的数学运算
### 时间长度
时间长度以整秒计数,以d
开头的关键词函数dseconds, dminutes, dhours, ddays, dweeks, dyears
生成,例如ddays(1)
返回的是一天的秒数,即24*3600秒:
## [1] "86400s (~1 days)"
对于时间尺度小于1秒的,会返回浮点数,例如:
## [1] "0.001s"
由于duration统一用整秒计数,所以他们很方便进行计算,例如
## [1] "3605s (~1 hours)"
## [1] TRUE
使用as.duration
函数可以加个两个日期-时间相减的结果转化为时间长度,如:
## [1] "95734320s (~3.03 years)"
我们也可以对日期-时间,加上或者减去时间长度,计算结果是按照整秒推移的,例如:
## [1] "2020-12-31 06:00:00 UTC"
细心的读者肯定已经发现了这里的问题,上面代码得到的结果是2020年12月31日而不是2021年1月1日,这是因为2020年是闰年,有366天,而dyears只会返回356天对应描述,因此相加的结果便是2020年12月31日。实践中,如果不小心,很容易在这里出问题。
B.5.1 时间周期
时间周期可以解决上面的问题,例如
## [1] "2021-01-01"
实际上,时间周期是一种有歧义的,翻译成日历时间长度可能更合适。在前面的例子中时间周期函数years
在运算时考虑到了日历时间的实际情况,当遇到闰年时,years
会自动转换为366天。因此years
对应的是一个日历年。类似的函数还有seconds, minutes, hours, days, weeks
。
时间周期也可以进行数学运算,如:
## [1] "2y 0m 10d 0H 0M 0S"
特别地,lubridate
没有提供月份周期的函数,需要使用lubridate::period(num, units="month")
生成月度周期,其中num是几个月的数值。
## [1] "2y 10m 0d 0H 0M 0S"
B.5.2 时间区间
lubridate
有三种方式构造时间区间,时间区间可以类比于一个实数区间,可以对其进行集合运算。第一种是使用%--%
运算符构造时间区间:
dt1 <- ymd_hms("2017-11-28T14:02:00")
dt2 <- ymd_hms("2020-12-10T14:54:00")
intv <- (dt1 %--% dt2)
intv
## [1] 2017-11-28 14:02:00 UTC--2020-12-10 14:54:00 UTC
也可以使用interval
函数来构造,如:
## [1] 2017-11-28 14:02:00 UTC--2020-12-10 14:54:00 UTC
还可以使用as.interval
函数,如:
## [1] 2017-11-28 14:02:00 UTC--2017-12-05 14:02:00 UTC
注意,时间区间是一个左闭右开的区间,也就是结束端点并没有被计算在内,所以上面的时间区间虽然涉及到了8个日期,但时长确实7天。
int_start
和int_end
可以分别取出时间区间的起始与结束端点:
## [1] "2017-11-28 14:02:00 UTC"
## [1] "2020-12-10 14:54:00 UTC"
%within%
算符可以用于判断一个日期-时间是否位于时间区间内:
## [1] FALSE
int_shift
可以平移一个时间区间:
## [1] 2017-12-01 14:02:00 UTC--2020-12-13 14:54:00 UTC
int_flip
可以反转一个时间区间:
## [1] 2020-12-10 14:54:00 UTC--2017-11-28 14:02:00 UTC
int_length
可以用于计算时间区间的长度,以秒计数:
## [1] 95734320
我们也可以使用除法计算时间长度,好处在于可以使用不同的单位:
## [1] 3.033638
int_overlaps
用于判断两个时间区间是否相交,int_aligns
用于判断两个时间区间是否有相同的端点:
## [1] TRUE
## [1] FALSE
以上便是R中日期-时间基本知识以及lubirdate
包的常规操作,这些知识对于时间序列数据的处理至关重要。
Coordinated Universal Time,是最主要的世界时间标准,其以原子时秒长为基础,在时刻上尽量接近于格林威治标准时间↩︎