R语言——日期时间处理

R语言——日期时间处理

在我们日常所遇到的数据分析任务中,会遇到很多与日期时间挂钩的数据,比如本月每日的销售额和网页一天内每个时间节点的点击量。这类型的数据大多数为时间序列,而时间序列分析在日常中也是很常见的。现在我们先来聊一下R语言中关于日期时间的处理,之后有时间的话就学习一些有关时间序列分析的方法。

一、日期函数as.Date()函数

R中自带的函数as.Date首先和大家介绍一下它的日常用法,第一个就是我们使用as.Date来返回日期数据形式,且默认的格式为年-月-日,format参数用于识别输入的日期按照那种数据逻辑输入,比如下面数据是以"*年*月*日"的逻辑输入:

> as.Date("2019年9月28日", format = "%Y年%m月%d日")
[1] "2019-09-28"

其中我们看到上面%Y等等的字符,其实是日期格式的一种字符形式,常用的格式如下:

第二个用法就是我们给定起点日期,再输入延后天数,就可以输出对应的日期:

> as.Date(31,origin ='2019-01-01')
[1] "2019-02-01"

二、时间函数POSIXct与POSIXlt

(1).POSIXIt主要特点:作用是打散时间,把时间分成年、月、日、时、分、秒,并进行存储我们可以结合unclass()函数,从而提取日期时间信息。比如:

> unclass(as.POSIXlt('2018-9-7 8:12:23'))
[1] 23
[1] 12
$hour
[1] 8
$mday
[1] 7
[1] 8
$year
[1] 118
$wday
[1] 5
$yday
[1] 249
$isdst
[1] 0
$zone
[1] "CST"
$gmtoff
[1] NA

我们输入带时间的日期数据,利用unclass和as.POSIXlt函数就可以返回秒、分、时、日、该年已过月数、已过年数(从1900起)、星期几、该天对应该年的第几天,时区等等。

(2).POSIXct 是以1970年1月1号8点开始的以秒进行存储,如果是负数,则是之前的日期时间;正数则是之后,比如:

> unclass(as.POSIXct('1970-1-1 8:00:20'))
[1] 20
attr(,"tzone")
[1] ""

三、日期时间的运算

(1).日期相减,得到相差的天数

> as.Date("2019-10-01") - as.Date('2019-9-26')
Time difference of 5 days

(2).带时间的日期相减,得到相差数(可以指定units参数为"secs","mins","hours","days")

> difftime('2019-10-1 10:00:00',"2019-10-1 6:00:00",units="hours")
Time difference of 4 hours

四、提取日期数据的关键信息

format函数可以将时间格式,调节成指定时间样式,且format函数可以识别as.Data型以及POSIXct与POSIXlt型。比如:

> format(as.Date(Sys.Date()),format="%B")
[1] "九月"

五、其他的与日期时间函相关的函数

#取日期对象所处的周几
> weekdays(as.Date('2019-9-26'))
[1] "星期四"
#取日期对象所处的季度
> quarters(as.Date('2019-9-26'))
[1] "Q3"
#取日期对象所处的月份
> months(as.Date('2019-9-26'))
[1] "九月"
#显示系统的时间 日期
> Sys.time()
[1] "2019-09-26 16:04:27 CST"
> Sys.Date()
[1] "2019-09-26"

六、专业处理日期时间包——lubridate

(1).年月日时分秒类函数
这个拓展包帮助我们能更方便地处理日期和时间数据,首先我们要学习的函数是关于日期数据的排列位置,它们分别是ymd()、myd()和dmy()。三个函数是字母y(年)m(月)d(日)的组合,功能也十分形象:

> myd(91928)
[1] "2019-09-28"
> ymd(20190928)
[1] "2019-09-28"
> dmy(280919)
[1] "2019-09-28"

大家会有疑问,如果日期后面有时间怎么办,这个也是一样的道理ymd_hms(),所以自己体会吧。另外上面提到的这类函数,灵活度很大,如果我们输入的日期数据格式非常奇葩,它们也能准确识别的,比如:

> x=c("2019 10 01",20191001, "2019-10,1","2019-10-01", "test 2019 10 1", "201910 ???? 01")
> ymd(x)
[1] "2019-10-01" "2019-10-01" "2019-10-01" "2019-10-01" "2019-10-01" "2019-10-01"

其实我们看到上面虽然函数能够准确识别,但是都有一个条件,就是年月日都需要按使用的函数顺序排列好。如果我们面对顺序不一样的向量时,就需要使用parse_date_time(),比如:

> x=c("20190101",'01012019','021901')
> parse_date_time(x,orders = c("ymd","dmy","dym"))
[1] "2019-01-01 UTC" "2019-01-01 UTC" "2019-01-02 UTC"

(2).提取日期时间信息
在还没介绍lubridate之前,如果我们需要取日期时间数据的对应的年月日等等的数据,我们的做法一般是用unclass和as.Date或者POSIXct联动,再提起对应所需要的数据,但是lubridate却很方便地帮我们解决了提取的问题:
second(),minute(),hour(),day(),wday(),yday(),week(),month(),year()和tz()就分别可以提取秒,分,小时,天,周的第几天,年的第几天,星期,月,年和时区的信息。

#wday中的星期日会备注1,依次类推
> wday('2019-9-28')
[1] 7
> hour('2019-1-9 9:9:9')
[1] 9

除了能准确地提取日期时间的信息之外,lubridate还提供了能够模糊提取信息的函数,比如我们需要将未达到30分的时间舍去,分钟数据归零,大于30分钟的就向小时位进一位。此时就需要到round_date()、floor_date()和ceiling_date()函数的帮忙了。

#四舍五入
> round_date(as.POSIXct('2019-9-28 12:23:9'),'hour')
[1] "2019-09-28 12:00:00 CST"
#向下取整
> floor_date(as.POSIXct('2019-9-28 12:23:9'),'hour')
[1] "2019-09-28 12:00:00 CST"
#向上取整
> ceiling_date(as.POSIXct('2019-9-28 12:23:9'),'hour')
[1] "2019-09-28 13:00:00 CST"

(3).时区
在之间介绍POSIXct与POSIXlt大家会发现日期时间数据之后还要一串英文字符,如“CST”,其实这是中国时区的缩写。而关于时区,我们需要注意的只有两个事情,第一个给定一个时区下的日期时间,能写出另外一个时区对应的日期时间,第二个就是将时区和日期时间结合成为该时区下的日期时间。首先我们可以认识一些常见的时区:

而关于时区操作的我们只需要了解两个函数,with_tz()和force_tz(),前者用于将原日期时间更新变换新时区下的新日期时间,后者用于结合给定的日期时间和时区。

#创建一个美国时区下的日期时间
onedate <- ymd_hms("2019-09-28 10:00:00", tz = "America/Chicago")
#转换成(欧洲-伦敦)时区
> with_tz(onedate,'Europe/London')
[1] "2019-09-28 16:00:00 BST"
# 创建一个(欧洲-伦敦)时区下的日期时间
> force_tz(as.POSIXct('2019-09-28 10:00:00'),'Europe/London')
[1] "2019-09-28 02:00:00 BST"

(4).计算时间间隔

lubridate包为我们提供一系列的函数来实现对时间间隔信息的提取和计算。其中interval函数是我们创建时间间隔对象的关键函数:

# 创建时间间隔的变量
> date_sr = ymd_hms('2019-10-1 10:10:10')
> date_ed = ymd_hms('2019-10-2 21:56:34')
> date_all = interval(date_sr,date_ed)

创建好时间间隔对象之后,我们就可以对该对象使用对应的函数得到想要的信息。比如我们想获取对象的起始时间和终止时间,我们可以使用int_start()和int_end():

> int_start(date_all)
[1] "2019-10-01 10:10:10 UTC"
> int_end(date_all)
[1] "2019-10-02 21:56:34 UTC"

如果我们想计算该对象中的时间间隔,我们可以使用time_length(),默认以秒为单位,也可以自己设置间隔的单位:

> time_length(date_all)
[1] 128784
> time_length(date_all,'day')
[1] 1.490556

或者有时候我们在创建完时间间隔对象后想同时修改两端的时间值,这时候我们就可以使用int_shift():

将时间间隔对象往后一周
> int_shift(date_all,by=duration(day =7 ))
[1] 2019-10-08 10:10:10 UTC--2019-10-09 21:56:34 UTC

或者我们会想去对调起始和终止的时间,则可以使用int_flip()

> int_flip(date_all)
[1] 2019-10-02 21:56:34 UTC--2019-10-01 10:10:10 UTC

(5).duration和period对象

我们上面已经学习了时间间隔对象的创建,下面继续来介绍更一般的时间对象。首先是duration(),这个函数可以创建持续时间类型对象,且纯粹以秒为单位计算时段的长度,比如

> duration(90, "seconds")
[1] "90s (~1.5 minutes)"
> duration(90, "minute")
[1] "5400s (~1.5 hours)"
> duration(90, "year")
[1] "2838240000s (~89.94 years)"

我们也有一些快速创建持续时间的对象以便于进行日期时间操作,例如:dyears(x) 、dweeks(x) 、ddays(x) 、dhours(x) 、dminutes(x) 和dseconds(x),其中的x表示长度。且这样函数都有统一的特点就是'd+时间日期单词+(x)',也非常容易理解,比如我需要创建表示10分钟的对象,那么dminutes(10)就完成了,另外需要特别注意的系没有dmonths(x),因为其以秒为单位来创建,而月度常在30天和31天之间变化。

period()创建的对象就属于周期对象,不同于duration()是以秒为单位,period()是以较长的时钟周期来计算时段长度,比如:

> period(num = 2,"day")
[1] "2d 0H 0M 0S"
> period(1,'year')