一、介绍
乍看之下,日期和时间很简单,因为生活中很常用。但是细看下来,它可以很复杂。
本文介绍lubridate包来操作时间,需要额外加载这个包。
本文操作的数据表是nycflights13,也需要提起加载。
之前我们用的tidyverse包,也会用到它的函数,所以一起加载。
二、日期和时间操作
1. 三种形式
- date:tidyverse会以<date>标注。
- time:tidyverse中以<time>标注。
- date-time:顾名思义是一个date加上一个time,以<dttm>标注。在其他地方,这种类型在R中也被称为POSIXct,了解一下即可。
我们在这一篇文章里,主要分享关于date和date-time的内容,因为r中直接对时间操作的场合比较少。
2. 获得时间
today()获得一个date。
now()获得一个date-time。
3. 创建时间
a.从字符串创建
方法一:parse_datetime()、parse_date()、parse_time()
parse_datetime函数需要一个具有标准格式的参数传入。这个格式是从大时间到小时间。比如"2024-05-30T0922"就指的是2024年5月30日9点22分。如果你不写时间的话,会直接设置成半夜。
parse_date需要传入4位数的年份加上两位数月份、两位数日,中间用"-“或者”/"连接。
也就是下面这种:
parse_date("2024-05-03")
方法二:ymd_hms
这个方法很有创造力。
也就是在函数名中明确字符串的顺序。
如果你用的函数是ymd,也就是说告诉R你的字符串是按照year\month\day来安排的。
如果你用的mdy,那就是说字符串是按照month\day\year来安排的。
我们试验一下:
> ymd("20240530")
[1] "2024-05-30"
> mdy(05302024)
[1] "2024-05-30"
而且,你可以发现,日期在这里不用引号也可以。这就使语句变得极为简练。
还以把ymd_hms结合使用
> ymd_hms("20240530 082210")
[1] "2024-05-30 08:22:10 UTC"
> mdy_hm("05302024 0822")
[1] "2024-05-30 08:22:00 UTC"
b. 从独立的date-time组件中创建
大家还记得nycflights13这个数据集中flights表吧。
里面有几列,分别是year、month、day、hour、minute,这些就是我们说的date-time组件(component)。
我们可以通过make_date()或者是make_datetime()来创建一个date-time。
比如:
flights %>% select(year,month,day,hour,minute) %>% mutate(departure=make_datetime(year,month,day,hour,minute))
案例
我们再看一下flights表中有四个时间非常怪。
分别是:
dep_time, arr_time
实际出发和到达时间(格式为HHMM或HMM),当地时区。
sched_dep_time, sched_arr_time
预定出发和到达时间(格式为HHMM或HMM),当地时区。
以第一条数据为例,代表这次航班出发时间是5点17分,计划出发时间是5点15分,以此类推。
我们希望能够把这种时间变成常用的时间格式。
那思路很简单,以dep_time为例:
第一个flight的dep_time为517,那用517除以100,得到的商就是小时,余数就是分钟。
再结合make_time函数生成一个时间。
那我们可以这样写代码:
flights_dt <- flights %>% filter(!is.na(dep_time),!is.na(arr_time)) %>% mutate(dep_time=make_datetime(year,month,day,dep_time%/%100,dep_time%%100),arr_time=make_datetime(year,month,day,arr_time%/%100,arr_time%%100),sched_dep_time=make_datetime(year,month,day,sched_dep_time%/%100,sched_dep_time%%100),sched_arr_time=make_datetime(year,month,day,sched_arr_time%/%100,sched_arr_time%%100))
结果如下:
也就是说,时间都换成了datetime模式。
简单地回顾一下代码:
代码最开始使用filter筛选出空值,然后用mutate函数,构建四个变量dep_time,arr_time,sched_dep_time,sched_arr_time,在构建的时候,用make_datetime来构建出一个时间。
PS:因为构建方式类似,也可以自己创建一个函数,简化代码。
有了这些时间数据之后,我们可以视觉化一下一年内每天离开飞机数量的分布。
flights_dt %>% ggplot(aes(dep_time))+geom_freqpoly(binwidth=86400)
86400代表1天,即86400秒。
上面是统计了一年的每天飞机起飞数量,我们也可以具体到某一天。比如,我们想看看5月1号那一天每一分钟的起飞飞机数量:
flights_dt %>% filter(dep_time > ymd(20130501) & dep_time < ymd(20130502)) %>% ggplot(aes(dep_time))+geom_freqpoly(binwidth=600)
如果没有这样的一个时间,我们用之前的数据做这样的统计还是会遇到很多麻烦的。
反向操作:从datetime中提取日期时间组件(components)
这几个函数相对简单,大家看看代码就清楚了:
> datetime1 <- now()
> datetime1
[1] "2024-05-30 22:56:09 CST"
> year(datetime1)
[1] 2024
> month(datetime1)
[1] 5
> mday(datetime1)
[1] 30
> yday(datetime1)
[1] 151
> wday(datetime1)
[1] 5
> hour(datetime1)
[1] 22
> minute(datetime1)
[1] 56
> second(datetime1)
[1] 9.774509
> month(datetime1,label=TRUE)
[1] May
12 Levels: Jan < Feb < Mar < Apr < May < Jun < ... < Dec
> wday(datetime1,label=TRUE)
[1] Thu
Levels: Sun < Mon < Tue < Wed < Thu < Fri < Sat
简单说一下:
year:提取年
month:提取月(加上label=TRUE,会返回月份简短写法)
mday:提取当月第几天
yday:当年第几天
wday:当周第几天(加上label=TRUE,会返回周几简短写法)
hour:提取小时
minute:提取分钟
second:提取秒
例1:
我们可以用wday来看一年中周几的起飞飞机量最多。
flights_dt %>% mutate(wday=wday(dep_time,label=TRUE)) %>% ggplot(aes(wday))+geom_bar()
看起来周内起飞的飞机还比较平均,但周内比周末普遍要多。
例2
我们想看看一小时内每分钟延迟的飞机数量。
flights_dt %>% mutate(minute=minute(dep_time)) %>% group_by(minute) %>% summarize(avg_delay=mean(arr_delay,na.rm=TRUE),n=n()) %>% ggplot(aes(minute,avg_delay))+geom_line()
结果如下:
日期近似值
> now()
[1] "2024-05-31 09:26:16 CST"
> floor_date(now(),"hour")
[1] "2024-05-31 09:00:00 CST"
> floor_date(now(),"minute")
[1] "2024-05-31 09:26:00 CST"
> ceiling_date(now(),"hour")
[1] "2024-05-31 10:00:00 CST"
> ceiling_date(now(),"minute")
[1] "2024-05-31 09:27:00 CST"
> round_date(now(),"minute")
[1] "2024-05-31 09:27:00 CST"
> round_date(now(),"hour")
[1] "2024-05-31 09:00:00 CST"
如果了解其它语言就知道,这类似于其他语言对带小数点的数取近似值。只不过现在换成了对时间取近似值。
例
比如,我们想知道每一周的起飞数量的变化。那这需要把一周的时间转化为同一个日期。
这时候我们可以用floor_date(datetime,“week”)这个函数。这个函数会把日期转换为本周第一天。
flights_dt %>% count(week=floor_date(dep_time,"week")) %>% ggplot(aes(week,n))+geom_line()
c. 从已存的date/time对象中创建
4. 日期时间之间转换
如果要把date转化成datetime,用as_datetime();
如果要把datetime转化成date,用as_date();
5. 修改时间
修改时间有两种方式,第一种就是直接赋值
year(datetime)<-2022
第二种方式是用update函数
update(datetime,year=2022,month=6,mday=2,hour=2)