通常传感器记录的数据,是为了避免闰年导致的种种稀奇古怪的错误,纯数字形式的日期格式很常见(例如19710101或儒略日)。这些纯数字形式日期的可读性通常都较差,所以需要经过解析变成更易理解的格式。还有另外一种比较普遍的情况是不同国家使用不同的日期制式和时区,比如英联邦国家偏向使用“日月年”或“月日年”的形式记录日期,以及12小时制来表达时间,而国内则倾向使用“年月日”的形式和24小时制。由于以这些情况的存在,在处理与时间有关的数值时,解析日期和时间变量往往无可避免。
对于日期时间的处理看似简单直接,但该问题却是数据分析当中与默认值处理难度相当的另一大挑战。在lubridate包问世之前,尽管已有其他功能强大的R包,诸如zoo、chron等,但都因为种种原因而无法达到与lubridate包一样简洁明了的效果。lubridate包的出现,极大地提高了用户解析日期数据的效率,从而使得开发人员能将更多时间用于分析数据而不是微调代码本身。lubridate包的最大优势可以总结为如下三点。
1)使用人类语言书写的编程语法,易于用户理解和记忆。
2)总结并融合了其他R包中的时间处理函数,并且优化了默认设置,更利于用户上手使用。
3)能够轻松完成时间日期数据的计算任务。
一般情况下,ymd及其子函数可以完整地解析以数字或字符串形式出现的日期形式,只有当日期中对不同的成分以类似双引号作为分隔符的情况,或者是对象为奇数的情况对(详见代码演示),ymd等函数可能会无法直接进行解析,而是需要进行额外处理。ymd函数即代表年月日,ymd_hms函数则代表年月日时分秒。两类函数的参数名称、结构和位置完全一致,具体函数名称见表2-23。默认时区为世界标准时间UTC。表2-23中列出了两组函数所有的子函数。读者在解析时间时应当注意时区,因为北京时间比UTC早8个小时,所以是UTC+8。所有对象经过解析后都会输出为年月日(时分秒)的标准日期格式,并且类别为“Date”。
表2-23 lubridate包中日期格式解析主要函数一览
lubridate函数可以仅使用默认设置轻松解析偶数位的字符型向量,必须要注意的是偶数位必须大于6位,否则会产生NA。在下列的代码中“2018 1 2”因为其中存在空格,所以被默认解析为6位。同样的逻辑也适用于解析日期时间对象。参数tz用于设置时区,示例代码如下:
> library(lubridate) > ymd(c(20180102, "2017-01-02","2018 1 2")) ## [1] "2018-01-02" "2017-01-02" "2018-01-02" > dmy_h(c(1802201810,"20-10-2018 24"),tz = "Asia/Shanghai") ## [1] "2018-02-18 10:00:00 CST" "2018-10-21 00:00:00 CST"
如果函数没有自动解析正确的时区,那么读者可以使用Sys.timezone()或Olson-Names()来寻找正确的时区,并传参设置时区。
气象领域通常会计算若干年的月、日平均降雨量或气温等指标,这时就会涉及月和日的提取要求。lubridate包中的函数,包含了提取从年到秒所有单位的功能。而为了方便记忆,这些函数的名称也都与相应的组件一一对应。需要读者注意的一点是,该组函数只能提取时间日期格式的对象,这些对象可以是常见的“Date”“POSIXct”“POSIXlt”“Period”等,或者是其他日期时间处理R包中的格式“chron”“yearmon”“yearqtr”“zoo”“zooreg”等。
下面的代码演示了最基本的使用方法,详细的参数微调可以提供一些额外的信息,读者可以自行参阅帮助文档:
> date <- ymd(c(20180102, "2017-02-07","20180711")) > year(date) ## [1] 2018 2017 2018 > month(date) ## [1] 1 2 7 > week(date) ## [1] 1 6 28 > day(date) ## [1] 2 7 11 > hour(date) ## [1] 0 0 0
当遇到使用英文月份简写的日期,比如24 Jan 2018/Jan 24,或者其他更糟糕的情况时,如果使用传统的baseR中的函数,诸如strptime或是format之类,那么用户可能会浪费很多时间去猜测和组装正确的日期时间格式,因为只有顺序和格式都正确的时候,baseR中提供的相应函数才可以正确解析日期时间,否则就会不停地返回NA值。幸运的是,guess_formats和parse_date_time两个函数的存在,完全颠覆了以往的解析模式,从而使得这一过程变得简单有趣。
使用这两个函数解析日期时间的大体思路具体如下。
1)执行guess_formats函数以用于猜测需要解析对象的可能日期时间顺序及格式,用户必须指定可能存在的格式顺序。
2)复制guess_formats函数的返回结果。
3)执行parse_date_time,并将复制的内容以字符串向量的格式传参给函数。
4)若遇到解析不成功或不彻底的情况,则需要手动组建日期时间格式(组件列表请参看表2-24),并加入到guess_formats中的order参数中。
下面的代码简要解释了guess_formats和parse_date_time两个函数配合使用解析日期时间的流程。首先生成一个名为example_messyDate的练习字符串向量,然后对该向量运行guess_formats,第二位参数orders中包含了可能存在的日期时间格式,并函数的返回结果中会报告匹配的顺序格式。将报告结果复制到parse_date_time的第二位参数中。至此解析成功。
> example_messyDate <- c("24 Jan 2018",1802201810) > guess_formats(example_messyDate,c("mdY", "BdY", "Bdy", "bdY", "bdy","dbY","dmYH")) ## dObY dOmYH dmYH ## "%d %Ob %Y" "%d%Om%Y%H" "%d%m%Y%H" > parse_date_time(example_messyDate,orders = c("dObY","dOmYH","dmYH")) ## [1] "2018-01-24 00:00:00 UTC" "2018-02-18 10:00:00 UTC"
表2-24 日期时间组组成部分解释