将dd/mm/yy和dd/mm/yyyy转换为日期格式。

10

我有一个字符向量,其中包含各种格式的日期,例如:

dates <- c("23/11/12", "20/10/2012", "22/10/2012" ,"23/11/12")

我想将它们转换为日期。我已经尝试了lubridate包中非常好的dmy函数,但是它不起作用:

    dmy(dates)
[1] "0012-11-23 UTC" "2012-10-20 UTC" "2012-10-22 UTC" "0012-11-23 UTC"

它将/12年视为0012年。

因此,我现在尝试使用正则表达式选择每种类型并使用as.Date()单独将其转换为日期。 但是,我尝试仅选择dd/mm/yy的正则表达式无效。

dates[grep('[0-9]{2}/[0-9]{2}/[0-9]{2,2}', dates)]

返回

[1] "23/11/12"   "20/10/2012" "22/10/2012" "23/11/12"

我认为{2,2}应该只包含恰好两个数字,而不是全部数字。我不太擅长正则表达式,希望能得到帮助。

谢谢

编辑

实际上我有三种不同类型的日期,如下所示:

dates <- c("23-Jul-2013", "23/11/12", "20/10/2012", "22/10/2012" ,"23/11/12")

我希望将这些转换为日期

parse_date_time(dates,c('dmy'))

给我

[1] "2013-07-23" "0012-11-23" "2012-10-20" "2012-10-22" "0012-11-23"

然而,这是错误的,0012 应该是 2012。我希望有一个(相当简单的)解决方案。

现在我有一个解决方案(感谢 @plannapus),它是使用正则表达式。事实上,我最终创建了这个函数,因为有一些情况下,使用 lubridate 的方法会将 12 转换成 0012。

    asDateRegex <- function(dates, 
        #selects strings from the vector dates using regexes and converts these to Dates
        regexes = c('[0-9]{2}/[0-9]{2}/[0-9]{4}', #dd/mm/yyyy
            '[0-9]{2}/[0-9]{2}/[0-9]{2}$', #dd/mm/yy
            '[0-9]{2}-[[:alpha:]]{3}-[0-9]{4}'), #dd-mon-yyyy
        orders = 'dmy',
        ...){
        require(lubridate)
        new_dates <- as.Date(rep(NA, length(dates)))
        for(reg in regexes){
            new_dates[grep(reg, dates)] <- as.Date(parse_date_time(dates[grep(reg, dates)], order = orders))
        }
        new_dates
    }

asDateRegex (dates)
[1] "2012-10-20" "2013-07-23" "2012-11-23" "2012-10-22" "2012-11-23"

但这并不是很优雅。有更好的解决方案吗?


1
你可以在这里查看:https://dev59.com/bXfZa4cB1Zd3GeqPSX4Z - Henrik
6个回答

16
您可以使用lubridate中的parse_date_time函数:
some.dates <- c("23/11/12", "20/10/2012", "22/10/2012" ,"23/11/12")
parse_date_time(some.dates,c('dmy'))
[1] "2012-11-23 UTC" "2012-10-20 UTC" "2012-10-22 UTC" "2012-11-23 UTC"

但是,请注意格式的顺序很重要:

some.dates <- c("20/10/2012","23/11/12",  "22/10/2012" ,"23/11/12")
parse_date_time(some.dates,c('dmY','dmy'))

[1] "2012-10-20 UTC" "2012-11-23 UTC" "2012-10-22 UTC" "2012-11-23 UTC"

编辑

在内部,parse_date_time 使用了 guess_formats (我猜测它使用了一些正则表达式):

guess_formats(some.dates,c('dmy'))
       dmy        dmy        dmy        dmy 
"%d/%m/%Y" "%d/%m/%y" "%d/%m/%Y" "%d/%m/%y" 

如评论中所提到的,您可以像这样使用parse_date_time

as.Date(dates, format = guess_formats(dates,c('dmy')))

1
我也是。+1 我需要花更多时间学习lubridate。 - Simon O'Hanlon
1
只是出于兴趣,你使用的lubridate版本是什么?在lubridate 1.3.0,R 2.15.2(是的,过时了,我知道),Win 7下,当我复制并粘贴你的parse_date_time代码时,我仍然得到0012年。 - Richie Cotton
1
请注意,parse_date_time 返回的是 POSIXlt 向量,而不是 Datedmy 返回 POSIXct)。 - Richie Cotton
1
我在我的上一个问题中描述了类似的现象。它也被发布为lubridate github上的一个问题。 - Henrik
1
as.Date(dates, format = guess_formats(dates,c('dmy'))) 对我有效。我不确定为什么它可以工作而 parse_date_time 无法。@agstudy 如果您将其添加到您的答案中,我会将其标记为已回答(我不太确定这里的正确礼仪...) - Tom Liptrot
显示剩余6条评论

7
您可以根据日期输入的长度选择格式。
y <- ifelse(nchar(dates) == 8, "y", "Y")
as.Date(dates, format = paste0("%d/%m/%", y))

1
你可以使用 strsplitnchar 来获取年份为两个字符的日期子向量:
> dates[sapply(strsplit(dates,"/"),function(x)nchar(x)[3]==2)]
[1] "23/11/12" "23/11/12"

1

在您最初尝试使用正则表达式解决问题后,您可以尝试使用此正则表达式进行 gsub,然后将其转换为任何日期时间格式...

#  Replace 4 digit years with two digit years
short <- gsub( "([0-9]{2})([0-9]{2})$" , "\\2" , dates )
#[1] "23/11/12" "20/10/12" "22/10/12" "23/11/12"


as.Date( short , format = "%d/%m/%y" )
#[1] "2012-11-23" "2012-10-20" "2012-10-22" "2012-11-23"

1
如果您真的想使用正则表达式来完成这个任务,您应该使用$来表示最后两位数字之后没有任何内容(即字符串末尾):
dates[grep('[0-9]{2}/[0-9]{2}/[0-9]{2}$', dates)]
[1] "23/11/12" "23/11/12"

否则,除了其他答案之外,您还可以查看此处此处以获取处理多个日期格式的其他方法。

1
这是一个基于R语言的方法,适用于更一般的情况,尚未在未被接受的答案中提及。
dates <- c("23-Jul-2013", "23/11/12", "20/10/2012", "22/10/2012" ,"23/11/12")
fmts <- list('%d-%b-%Y', '%d/%m/%y', '%d/%m/%Y')
d <- mapply(as.Date, list(dates), fmts, SIMPLIFY=FALSE)
max.d <- do.call(function(...) pmax(..., na.rm=TRUE), d)
min.d <- do.call(function(...) pmin(..., na.rm=TRUE), d)
max.d[max.d > Sys.Date()] <- min.d[max.d > Sys.Date()]
max.d
# [1] "2012-11-23" "2012-10-20" "2012-10-22" "2012-11-23"

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