在R中更改日期格式

46

我在R中有一些非常简单的数据需要更改其日期格式:

 date midpoint
1   31/08/2011   0.8378
2   31/07/2011   0.8457
3   30/06/2011   0.8147
4   31/05/2011   0.7970
5   30/04/2011   0.7877
6   31/03/2011   0.7411
7   28/02/2011   0.7624
8   31/01/2011   0.7665
9   31/12/2010   0.7500
10  30/11/2010   0.7734
11  31/10/2010   0.7511
12  30/09/2010   0.7263
13  31/08/2010   0.7158
14  31/07/2010   0.7110
15  30/06/2010   0.6921
16  31/05/2010   0.7005
17  30/04/2010   0.7113
18  31/03/2010   0.7027
19  28/02/2010   0.6973
20  31/01/2010   0.7260
21  31/12/2009   0.7154
22  30/11/2009   0.7287
23  31/10/2009   0.7375

我想使用标准的 R 日期格式%Y-%m-%d,而不是 %d/%m/%Y。如何进行更改?我已尝试过:

nzd$date <- format(as.Date(nzd$date), "%Y/%m/%d")

但那只是删去了年份,并在日期后添加了零:

 [1] "0031/08/20" "0031/07/20" "0030/06/20" "0031/05/20" "0030/04/20"
 [6] "0031/03/20" "0028/02/20" "0031/01/20" "0031/12/20" "0030/11/20"
 [11] "0031/10/20" "0030/09/20" "0031/08/20" "0031/07/20" "0030/06/20"
 [16] "0031/05/20" "0030/04/20" "0031/03/20" "0028/02/20" "0031/01/20"
 [21] "0031/12/20" "0030/11/20" "0031/10/20" "0030/09/20" "0031/08/20"
 [26] "0031/07/20" "0030/06/20" "0031/05/20" "0030/04/20" "0031/03/20"
 [31] "0028/02/20" "0031/01/20" "0031/12/20" "0030/11/20" "0031/10/20"
 [36] "0030/09/20" "0031/08/20" "0031/07/20" "0030/06/20" "0031/05/20"

谢谢!

8个回答

79

这里有两个步骤:

  • 解析数据。你的例子不完全可重现,数据是在文件中还是在文本或因子变量中?假设是后者,那么如果你的数据框叫做X,你可以这样做:
 X$newdate <- strptime(as.character(X$date), "%d/%m/%Y")
现在,newdate列应该是Date类型。

  • 格式化数据。这是调用format()strftime()的问题:
 format(X$newdate, "%Y-%m-%d")

更完整的示例:

R> nzd <- data.frame(date=c("31/08/2011", "31/07/2011", "30/06/2011"), 
+                    mid=c(0.8378,0.8457,0.8147))
R> nzd
        date    mid
1 31/08/2011 0.8378
2 31/07/2011 0.8457
3 30/06/2011 0.8147
R> nzd$newdate <- strptime(as.character(nzd$date), "%d/%m/%Y")
R> nzd$txtdate <- format(nzd$newdate, "%Y-%m-%d")
R> nzd
        date    mid    newdate    txtdate
1 31/08/2011 0.8378 2011-08-31 2011-08-31
2 31/07/2011 0.8457 2011-07-31 2011-07-31
3 30/06/2011 0.8147 2011-06-30 2011-06-30
R> 

第三列和第四列的区别在于它们的类型:newdateDate类,而txtdate是字符类型。


1
嗯,对于新手来说,这似乎非常复杂。最终我只是在Excel中更改了格式,并将CSV文件读回到R中。我想知道如何在R中轻松地进行更改,以防有一个更大的文件,但这似乎并不像应该那么容易。这并不是对你的解决方案的贬低,我只是希望它更简单(可能有一种方法可以转换原始列而不创建新列)。是否有一种方法先更改类,然后再进行格式化? - indigo
3
@Yuri - 这基本上就是Dirk的答案所展示的,不过他在过程中创建了一些新列,以便您可以轻松地看到"引擎盖下面"发生了什么。我建议逐行浏览他的示例,并在每行之间插入str(x),这样您就可以看到操作的不同之处。 - Chase
1
@Chase 谢谢,我很感激你为教学目的添加了额外的列,这帮助我看到了类别差异以及格式;所以非常感谢你!很高兴知道额外的步骤并不是必须的。再次感谢你们两位! - indigo

17
nzd$date <- format(as.Date(nzd$date), "%Y/%m/%d")
在上面的代码片段中有两个错误。首先,在使用as.Date读取nzd$date时,没有说明您输入的date是什么格式。因此,它会尝试使用默认设置格式进行解析。如果查看help文档?as.Date,您将看到:

format
字符串类型。如果未指定,则尝试在第一个非NA元素上使用"%Y-%m-%d"然后是"%Y/%m/%d",如果都不行则报错。否则,将通过strptime处理

第二个错误是:即使您想以%Y-%m-%d格式读取它,在format中也编写了"%Y/%m/%d"

现在,正确的方法是:

> nzd <- data.frame(date=c("31/08/2011", "31/07/2011", "30/06/2011"), 
+                                       mid=c(0.8378,0.8457,0.8147))
> nzd
        date    mid
1 31/08/2011 0.8378
2 31/07/2011 0.8457
3 30/06/2011 0.8147
> nzd$date <- format(as.Date(nzd$date, format = "%d/%m/%Y"), "%Y-%m-%d")
> head(nzd)
        date    mid
1 2011-08-31 0.8378
2 2011-07-31 0.8457
3 2011-06-30 0.8147

8
您还可以使用 lubridate 包中的 parse_date_time 函数:
library(lubridate)
day<-"31/08/2011"
as.Date(parse_date_time(day,"dmy"))
[1] "2011-08-31"

parse_date_time 函数返回 POSIXct 对象,因此我们使用 as.Date 将其转换为日期对象。函数 parse_date_time 的第一个参数指定日期向量,第二个参数指定格式出现的顺序。 orders 参数使得 parse_date_time 函数非常灵活。


3

通过使用textConnection读取数据后,以下内容似乎可以正常工作:

dat <- read.table(textConnection(txt), header = TRUE)
dat$date <- strptime(dat$date, format= "%d/%m/%Y")
format(dat$date, format="%Y-%m-%d")

> format(dat$date, format="%Y-%m-%d")
 [1] "2011-08-31" "2011-07-31" "2011-06-30" "2011-05-31" "2011-04-30" "2011-03-31"
 [7] "2011-02-28" "2011-01-31" "2010-12-31" "2010-11-30" "2010-10-31" "2010-09-30"
[13] "2010-08-31" "2010-07-31" "2010-06-30" "2010-05-31" "2010-04-30" "2010-03-31"
[19] "2010-02-28" "2010-01-31" "2009-12-31" "2009-11-30" "2009-10-31"

> str(dat)
'data.frame':   23 obs. of  2 variables:
 $ date    : POSIXlt, format: "2011-08-31" "2011-07-31" "2011-06-30" ...
 $ midpoint: num  0.838 0.846 0.815 0.797 0.788 ...

我有一个关于数据框中的POSIXlt的问题,如此处所述:http://stackoverflow.com/questions/3355107/possibly-inconsistent-behavior-in-qplot当我尝试在ggplot中将日期作为x轴绘制时,我收到以下错误消息 -Error in if (length(range) == 1 || diff(range) == 0) { : missing value where TRUE/FALSE needed -- 我该如何使用POSIXct解决这个问题? - indigo
@Yuri - 类似 as.POSIXct(otherStuffHere) 这样的方法可能有效。感谢提供链接,我之前没有注意到 Hadley 提出的这些问题。 - Chase

3
使用一行代码将日期转换为首选格式:
nzd$date <- format(as.Date(nzd$date, format="%d/%m/%Y"),"%Y/%m/%d")

3

使用lubridate软件包非常简单。您只需告诉R您的日期格式已经是什么样子,它就会将其转换为标准格式。

nzd$date <- dmy(nzd$date)

就这么简单。


1

我相信

nzd$date <- as.Date(nzd$date, format = "%d/%m/%Y")

就足够了。


同意,但它依赖于“魔法”(默认格式),因此展示不同的步骤是有益的。 - Dirk Eddelbuettel

0
如果您的输入具有一致的日期格式,您可以尝试一个简单的解决方法:
sapply(date, function(x){paste(strsplit(x, '/')[[1]][c(3,2,1)], collapse = '/')})

处理缺失值,您可能需要:sapply(date, function(x){ifelse(is.na(x),NA,paste(strsplit(x, '/')[[1]][c(3,2,1)], collapse = '/'))}) - ae2487

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接