将年月(“yyyy-mm”格式)转换为日期?

Converting year and month ("yyyy-mm" format) to a date?

提问人:R_User 提问时间:6/5/2011 最后编辑:AndrewGBR_User 更新时间:11/28/2022 访问量:216809

问:

我有一个数据集,如下所示:

Month    count
2009-01  12
2009-02  310
2009-03  2379
2009-04  234
2009-05  14
2009-08  1
2009-09  34
2009-10  2386

我想绘制数据(月份为 x 值,计数为 y 值)。由于数据中存在差距,我想将本月的信息转换为日期。我试过了:

as.Date("2009-03", "%Y-%m")

但它没有用。怎么了?似乎还需要一天,无法为当天设置标准值?哪个功能解决了我的问题?as.Date()

Date POSIX 动物园 R-FAQ

评论


答:

101赞 Sacha Epskamp 6/5/2011 #1

由于日期对应于数值和开始日期,因此您确实需要日期。如果您确实需要数据采用日期格式,只需将日期粘贴到日期即可手动将日期固定为每个月的第一天:

month <- "2009-03"
as.Date(paste(month, "-01", sep=""))

评论

0赞 R_User 6/5/2011
日期还有哪些其他格式?我看到了 POSIX 和 ISO 的东西,但我不确定它们是否是不同的格式。我以为这些只是函数,...
24赞 JBecker 5/25/2013
值得注意的是,您可以在格式化程序中将日期指定为相同,因此您可以执行并实现相同的结果。这对我来说“感觉”更可取,因为在每个月中指定相同的日期更多的是关于日期的格式,而不是字符串操作,但也许这是无稽之谈。as.Date(month, format='%Y-%m-01')
32赞 n8sty 1/21/2017
@JBecker你的建议对我不起作用。.我正在使用 R 3.3.1> as.Date("2016-01", format="%Y-%m-01") # [1] NA
67赞 G. Grothendieck 6/5/2011 #2

试试这个。(在这里,我们习惯于将示例保持为自包含,但实际上我们会将其替换为文件名。text=Lines

Lines <- "2009-01  12
2009-02  310
2009-03  2379
2009-04  234
2009-05  14
2009-08  1
2009-09  34
2009-10  2386"

library(zoo)
z <- read.zoo(text = Lines, FUN = as.yearmon)
plot(z)

对于这些数据,X 轴不是那么漂亮,但如果实际上您有更多数据,它可能没问题,或者您可以使用示例部分中所示的花哨 X 轴的代码。?plot.zoo

上面创建的动物园系列,有一个时间索引,如下所示:z"yearmon"

> z
Jan 2009 Feb 2009 Mar 2009 Apr 2009 May 2009 Aug 2009 Sep 2009 Oct 2009 
      12      310     2379      234       14        1       34     2386 

"yearmon"也可以单独使用:

> as.yearmon("2000-03")
[1] "Mar 2000"

注意:

  1. "yearmon"类对象按日历顺序排序。

  2. 这将以等间隔绘制每月点,这可能是想要的;但是,如果希望以不等距间隔绘制点,这些点的间隔与每个月的天数成比例,则将 的索引转换为 class: 。z"Date"time(z) <- as.Date(time(z))

37赞 Ben Rollert 2/7/2014 #3

如果您需要日期格式,最简洁的解决方案:

library(zoo)
month <- "2000-03"
as.Date(as.yearmon(month))
[1] "2000-03-01"

as.Date将每个月的第一天固定为yearmon对象。

7赞 Megatron 12/18/2015 #4

事实上,正如上面(以及 SO 的其他地方)所提到的,为了将字符串转换为日期,您需要一个特定的月份日期。从手册页:as.Date()

如果日期字符串未完全指定日期,则返回的答案可能是特定于系统的。最常见的行为是假设缺少的年、月或日是当前年、月或日。如果错误地指定了日期,可靠的实现将给出错误,并且该日期将报告为 NA。不幸的是,一些常见的实现(例如 )是不可靠的,并且猜测预期的含义。glibc

一个简单的解决方案是将日期粘贴到每个日期,并用于将其指示为该月的第一天。"01"strptime()


对于那些想要了解更多有关在 R 中处理日期和时间的背景信息的人:

在 R 中,times use 和 classes 和 dates 使用该类。POSIXctPOSIXltDate

日期存储为自 1970 年 1 月 1 日以来的天数,时间存储为 1970 年 1 月 1 日以来的秒数。

因此,例如:

d <- as.Date("1971-01-01")
unclass(d)  # one year after 1970-01-01
# [1] 365

pct <- Sys.time()  # in POSIXct
unclass(pct)  # number of seconds since 1970-01-01
# [1] 1450276559
plt <- as.POSIXlt(pct)
up <- unclass(plt)  # up is now a list containing the components of time
names(up)
# [1] "sec"    "min"    "hour"   "mday"   "mon"    "year"   "wday"   "yday"   "isdst"  "zone"  
# [11] "gmtoff"
up$hour
# [1] 9

要对日期和时间执行操作:

plt - as.POSIXlt(d)
# Time difference of 16420.61 days

要处理日期,您可以使用(从手册页中借用这些示例):strptime()

strptime("20/2/06 11:16:16.683", "%d/%m/%y %H:%M:%OS")
# [1] "2006-02-20 11:16:16 EST"

# And in vectorized form:
dates <- c("1jan1960", "2jan1960", "31mar1960", "30jul1960")
strptime(dates, "%d%b%Y")
# [1] "1960-01-01 EST" "1960-01-02 EST" "1960-03-31 EST" "1960-07-30 EDT"
2赞 PAC 11/9/2016 #5

我认为 @ben-rollert 的解决方案是一个很好的解决方案。

如果您想在新包内的函数中使用此解决方案,您只需要小心。

开发包时,建议使用语法(参见 http://kbroman.org/pkg_primer/pages/depends.html)。packagename::function_name()

在这种情况下,您必须使用库定义的版本。as.Date()zoo

下面是一个例子:

> devtools::session_info()
Session info ----------------------------------------------------------------------------------------------------------------------------------------------------
 setting  value                       
 version  R version 3.3.1 (2016-06-21)
 system   x86_64, linux-gnu           
 ui       RStudio (1.0.35)            
 language (EN)                        
 collate  C                           
 tz       <NA>                        
 date     2016-11-09                  

Packages --------------------------------------------------------------------------------------------------------------------------------------------------------

 package  * version date       source        
 devtools   1.12.0  2016-06-24 CRAN (R 3.3.1)
 digest     0.6.10  2016-08-02 CRAN (R 3.2.3)
 memoise    1.0.0   2016-01-29 CRAN (R 3.2.3)
 withr      1.0.2   2016-06-20 CRAN (R 3.2.3)

> as.Date(zoo::as.yearmon("1989-10", "%Y-%m")) 
Error in as.Date.default(zoo::as.yearmon("1989-10", "%Y-%m")) : 
  do not know how to convert 'zoo::as.yearmon("1989-10", "%Y-%m")' to class “Date”

> zoo::as.Date(zoo::as.yearmon("1989-10", "%Y-%m"))
[1] "1989-10-01"

因此,如果您正在开发一个包,最好的做法是使用:

zoo::as.Date(zoo::as.yearmon("1989-10", "%Y-%m"))
16赞 zx8754 3/8/2017 #6

使用 anytime 包:

library(anytime)

anydate("2009-01")
# [1] "2009-01-01"

评论

1赞 lmo 9/2/2017
它选择“01-01”有点奇怪,文档中是否有关于选择的内容?也许更能说明问题,也可以显示它是否总是选择每月的第一天。anydate("2009-03")
1赞 zx8754 9/2/2017
@lmo没有检查文档,我会说这是缺少 dd 选择第一天的“常见”做法。
2赞 lmo 9/2/2017
这是有道理的。我模糊地记得,然后找到了触发评论的原因。在 的 Note 部分,输入字符串不需要完全指定日期:假设未指定的秒、分钟或小时为零,未指定的年、月或日为当前日期。(但是,如果指定了月份,则该月的日期必须由 %d 或 %e 指定,因为该月的当前日期不必对指定的月份有效。看起来威震天的答案包含了类似的文档。?strptimeas.Date
2赞 msh855 4/13/2019
在 1900 年之前的几年里,它不起作用。例如,我试过这个anytime('1870-01')
2赞 schroederadrian 11/8/2021
唯一适合我的解决方案!使用 anydate() 而不是 anytime()
32赞 Jaap 6/25/2017 #7

您也可以使用 -package 中的 or 函数来实现此目的:parse_date_timefast_strptimelubridate

> parse_date_time(dates1, "ym")
[1] "2009-01-01 UTC" "2009-02-01 UTC" "2009-03-01 UTC"

> fast_strptime(dates1, "%Y-%m")
[1] "2009-01-01 UTC" "2009-02-01 UTC" "2009-03-01 UTC"

这两者之间的区别在于,允许润滑油样式的格式规范,而需要与 相同的格式规范。parse_date_timefast_strptimestrptime

若要指定时区,可以使用 -parameter:tz

> parse_date_time(dates1, "ym", tz = "CET")
[1] "2009-01-01 CET" "2009-02-01 CET" "2009-03-01 CET"

当日期时间数据中存在不规则性时,可以使用 -parameter 指定允许的不规则性数量:truncated

> parse_date_time(dates2, "ymdHMS", truncated = 3)
[1] "2012-06-01 12:23:00 UTC" "2012-06-01 12:00:00 UTC" "2012-06-01 00:00:00 UTC"

使用的数据:

dates1 <- c("2009-01","2009-02","2009-03")
dates2 <- c("2012-06-01 12:23","2012-06-01 12",'2012-06-01")

评论

0赞 user63230 7/30/2019
将字符变量转换为格式后,有没有办法以与使用包不同的顺序查看它?我更愿意在我的数据集中首先看到这一天,例如.dateparse_date_time"2009-01-01 UTC"lubridate01-01-2009
1赞 Jaap 7/31/2019
@user63230 见 ;例如:.不过,这样做有一个缺点:您将获得字符值而不是日期。?formatformat(your_date, "%d-%m-%Y")
0赞 user63230 7/31/2019
谢谢,但由于您提到的原因,我试图避免,我认为可能有一种方法可以将其合并到包中,但似乎没有。formatlubridate
1赞 LMc 8/23/2022 #8

tidyverse最近添加了时钟包,除此之外还有一些不错的功能:lubridate

library(clock)

x <- year_month_day_parse(df$Month, format = "%Y-%m", precision = "month") 
# <year_month_day<month>[8]>
# [1] "2009-01" "2009-02" "2009-03" "2009-04" "2009-05" "2009-08" "2009-09" "2009-10"

日期操作和提取

它的输出是一个年-月-日向量,您仍然可以在其中执行日期算术并按预期应用其他常用函数:

sort(x, decreasing = T)
# <year_month_day<month>[8]>
# [1] "2009-10" "2009-09" "2009-08" "2009-05" "2009-04" "2009-03" "2009-02" "2009-01"

add_months(x, 3)
# <year_month_day<month>[8]>
# [1] "2009-04" "2009-05" "2009-06" "2009-07" "2009-08" "2009-11" "2009-12" "2010-01"

add_years(x, -2)
# <year_month_day<month>[8]>
# [1] "2007-01" "2007-02" "2007-03" "2007-04" "2007-05" "2007-08" "2007-09" "2007-10"

get_month(x)
# [1]  1  2  3  4  5  8  9 10

如果需要,您还可以使用以下命令设置日期:set_day

set_day(x, 1)
<year_month_day<day>[8]>
[1] "2009-01-01" "2009-02-01" "2009-03-01" "2009-04-01" "2009-05-01" "2009-08-01"
[7] "2009-09-01" "2009-10-01"

处理无效日期

或者,如果您想使用此结构干净地获取每个月的最后一天,则函数集可以提供帮助:invalid_*

# not 31 days in Feb, Apr, Sep
y <- set_day(x, 31)
# <year_month_day<day>[8]>
# [1] "2009-01-31" "2009-02-31" "2009-03-31" "2009-04-31" "2009-05-31" "2009-08-31"
# [7] "2009-09-31" "2009-10-31"

invalid_any(y)
[1] TRUE

invalid_detect(y)
[1] FALSE  TRUE FALSE  TRUE FALSE FALSE  TRUE FALSE

您可以使用以下命令处理无效日期,也可以使用以下命令删除它们:invalid_resolveinvalid_remove

invalid_resolve(y, invalid = "previous")
<year_month_day<day>[8]>
[1] "2009-01-31" "2009-02-28" "2009-03-31" "2009-04-30" "2009-05-31" "2009-08-31"
[7] "2009-09-30" "2009-10-31"

在文档中,您可以为参数指定以下值以处理无效日期:invalid

“previous”:上一个有效时刻。

“previous-day”:前一个有效日期,保持一天中的时间。

“next”:下一个有效时刻。

“next-day”:时间的下一个有效日期,保持一天中的时间。

“overflow”:按输入无效的天数溢出 由。删除了一天中的时间。

“overflow-day”:按输入的天数溢出 无效。保留一天中的时间。

“NA”:将无效日期替换为 NA。

“error”:无效日期错误。

1赞 Andre Wildberg 11/28/2022 #9

一种使用 from 的方法。ymlubridate

月份可以是数字、缩写月份或带有各种分隔符(即使没有分隔符)的完整月份名称,例如

library(lubridate)

ym(c("2012/September", "2012-Aug", "2012.07", 201204))
[1] "2012-09-01" "2012-08-01" "2012-07-01" "2012-04-01"

在给定的数据上:

ym(dat$Month)
[1] "2009-01-01" "2009-02-01" "2009-03-01" "2009-04-01" "2009-05-01"
[6] "2009-08-01" "2009-09-01" "2009-10-01"

请注意,如果您以相反的方式拥有它,例如.mySep/2022

数据

dat <- structure(list(Month = c("2009-01", "2009-02", "2009-03", "2009-04",
"2009-05", "2009-08", "2009-09", "2009-10"), count = c(12L, 310L,
2379L, 234L, 14L, 1L, 34L, 2386L)), class = "data.frame", row.names = c(NA,
-8L))