R 中的日期和时间(译文)

dapeng 按:本文由 dapeng 译自 Crawley 所著的 The R Book 。yangliufr 提醒这可能造成侵权,故2013年4月发布之后就删除了。到现在(2016年1月)已经过去了快三年,其间有很多朋友来信,提出的问题很多都能在本文中找到答案。所以,我再次贴出来,如有侵权,请告知我,我会迅速处理。

时间的度量非常特殊。一年的第一天是星期几,这在相继的年份中是不同的。各月份的天数不尽相同,而闰年在 2 月份会多出一天。美国人和英国人把月和日放在不同的位置: 3 / 4 / 2006 在前者看来是 3 月 4 日,而后者看来则是 4 月 3 日。偶尔有些年会多出一个“闰秒”,这是由于自从标准时间于 1900 年以回归年为基础建立以来,潮汐的摩擦减慢了地球的转速。原子钟设置得过慢,其累积效应导致需要不断地加入闰秒(自 1958 年以来有 32 次之多(译者注:截至本书英文版出版年份))。目前,尚有争议认为应当废除闰秒制,改为每个世纪增加一个“闰分”的方法(译者注:世界无线电会议将于 2015 年对此问题做出决议)。不同国家的时区制和光节约时制(译者注:俗称夏时制)增加了关于时间计算的复杂性。所有这些意味着,涉及日期和时间的工作是极其复杂的。幸好 R 具有一个强大的系统来处理这种复杂性。要了解 R 是如何处理日期和时间,我们先看看 Sys.time() 函数:

Sys.time()
## [1] "2005-10-23 10:17:42 GMT Daylight Time"

返回值按从左到右的严格顺序依次是:首先是最长的时间尺度(年),然后是用连字符(减号)分隔的月和日,接着是空格和时间,小时在前(24 小时制),后面是用冒号分隔的分钟和秒。最后有一个字符串来注明时区。你可以从Sys.time() 中获取日期,这要使用 substr,代码如下:

substr(as.character(Sys.time()), 1, 10)
## [1] "2005-10-23"

或者获取时间

substr(as.character(Sys.time()), 12, 19)
## [1] "10:17:42"

如果输入

unclass(Sys.time())
## [1] 1130679208

你会得到从 1970 年 1 月 1 日开始计数的秒数。日期/时间有两个基本的类: 一是POSIXct 类,作为数字型向量,代表了自 1970 年年初开始计数的(带正负符号的)秒数,这更便于在数据框中使用;二是 POSIXlt 类,作为向量的命名列表,代表了秒、分钟、小时、日、月和年,这在形式上更便于肉眼阅读。R 会用 date 函数告诉你日期和时间:

date()
## [1] "Fri Oct 21 06:37:04 2005"

默认顺序是星期几、月的名称(二者均为缩写)、日、小时(24 小时制)、分钟、秒(冒号分隔)和年。可以将Sys.time 转换成一个与 POSIXlt 类的形式一致的对象,代码如下:

date <- as.POSIXlt(Sys.time())

若要选取这个对象中的日期和时间部分,你可以使用元素名称操作符 $ 和以下名称:sec, min, hour, mday, mon, year, wday, yday 以及 isdst。其中,mday 表示月内的天数,wday 表示以星期日为零点的星期几,yday 表示以 1 月 1 日为零点的年内天数,isdst 表示“操作中是否是光节约时制”(逻辑值,1 表示 TRUE,0 表示FALSE),其他名称均可顾名思义。这里我们从 10 月23日这个日期中获取星期几(date$wday = 0 表示星期日)和儒略日(date$yday,1 月 1 日后的年内天数)的信息:

date$wday
## [1] 0
date$yday
## [1] 295

可以使用 unclassunlist 来查看日期的全部组成信息:

unlist(unclass(date))
##    sec    min   hour   mday    mon   year   wday   yday  isdst 
##     42     17     10     23      9    105      0    295      1

注意,10 月的月数是 9 (不是 10)因为 1 月的月数被记作了 0 ,而年数是从 1900 年开始计数的。

日期和时间的计算

你可以对日期和时间进行如下计算:

  • 时间 + 数字
  • 时间 - 数字
  • 时间1 - 时间2
  • 时间1 “逻辑运算” 时间2

这里,逻辑运算是指 ==,!=,<,<=,> 或者 >=。 你可以让一个日期时间对象加减一个秒数或者一个时间差(difftime)对象(见下文),但是不允许对两个日期时间对象进行相加。两个日期时间对象的相减等同于使用 difftime (见下文)。如果计算中没有指定时区的话,POSIXlt 对象会默认使用当前时区。

要掌握的要领是,在任何计算 应该把你的日期和时间转换成 POSIXlt 对象。一旦成为 POSIXlt 对象,就可以直接计算平均值、差值等等。这里我们来计算两个日期,即 2003 年 10 月 22 日和 2005 年 10 月 22 日之间的天数:

y2 <- as.POSIXlt("2003-10-22")
y1 <- as.POSIXlt("2005-10-22")

现在你可以对这两个日期进行计算了:

y1 - y2
## Time difference of 731 days

注意,你不能对两个日期 相加 。使用这个系统来计算时间之差也很容易。注意日期是用连字符分隔的,而时间是用冒号分隔的:

y3 <- as.POSIXlt("2005-10-22 09:30:59")
y4 <- as.POSIXlt("2005-10-22 12:45:06")
y4 - y3
## Time difference of 3.235278 hours

difftime 函数

日期和时间之差的计算使用 difftime 函数。它把两个日期时间对象作为自变量,返回一个 difftime 类的带有单位标识的对象。2003 年 8 月 15 日 与 2005 年 10 月 21 日之间有多少天?

difftime("2005-10-21", "2003-8-15")
## Time difference of 798 days

若只需要天数,比如需要在计算中使用时,那么可以这样写:

as.numeric(difftime("2005-10-21", "2003-8-15"))
## [1] 798

若用包括时间(冒号分隔)的小时数来表示,那么就这样写:

difftime("2005-10-21 5:12:32", "2005-10-21 6:14:21")
## Time difference of -1.030278 hours

结果是负数,这是因为第一个时间(左边)写在了第二个时间(右边)之前。一种替代方法是,你可以用一个日期时间对象直接减去另外一个:

ISOdate(2005, 10, 21) - ISOdate(2003, 8, 15)
## Time difference of 798 days

你可以使用 as.difftime 函数把字符转换成 difftime 对象:

as.difftime(c("0:3:20", "11:23:15"))
## Time differences of 3.333333, 683.250000 mins

你可以设置时间的格式。例如,你可能没有关于秒的信息,并且你的时间用小时(格式是 %H)和分钟(%M)表示,那么可以这样做:

as.difftime(c("3:20", "23:15", "2:"), format = "%H:%M")
## Time differences of 3.333333, 23.250000, NA hours

由于序列中最末的一个时间“2:”不包含分钟, 所以标记为 NA

R 中的日期和时间(译文)》上有6条评论

  1. 小语妈

    讲得很清楚!虽然记不住,但是要用的时候知道可以回来找。谢谢大鹏。——小语妈

    回复
  2. 大鹏你好!
    有人发给我一个数据集,但是我打开之后只是一个空的R软件,有什么办法可以找到数据集吗?

    回复
    1. 大鹏 文章作者

      ‘打开之后只是一个空的R软件’,这是什么意思?文件的扩展名是什么?不介意的话,可以发给我试试。

      回复
      1. 这个问题已经弄清楚了,用View()函数就可以了。谢谢大鹏!
        我有好多经度和纬度的数据,想通过这些数据简单看一下这些点的趋势?不知道用什么方法呢?我能想到的就是将他们转化为坐标轴上的点然后再连起来?不知道怎么把经度和纬度转化为X和Y坐标轴上的数据呢?

        回复
        1. 大鹏 文章作者

          记得有专门的包来处理经纬度数据,好久不用,忘了……

发表评论

电子邮件地址不会被公开。 必填项已用*标注

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax