我有一个看起来像这样的数据集:
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()
也需要一天,并且无法为当天设置标准值?哪个功能可以解决我的问题?
由于日期对应于数值和开始日期,因此您确实需要日期。如果您确实需要数据采用日期格式,则可以通过将日期粘贴到日期来手动将日期固定为每个月的第一天:
month <- "2009-03"
as.Date(paste(month, "-01", sep=""))
试试这个。 (在这里,我们使用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 轴不是那么漂亮,但如果您在现实中有更多数据,那可能没问题,或者您可以将代码用于 ?plot.zoo
的示例部分中显示的花哨的 X 轴。
上面创建的动物园系列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"
注意:
"yearmon"
类对象按日历顺序排序。
这将以等间隔的间隔绘制每月点,这可能是想要的;但是,如果需要以不等间距的间隔绘制点,间隔与每个月中的天数成比例,则将
z
索引转换为"Date"
类:time(z) <- as.Date(time(z))
。
如果您需要日期采用日期格式,则最简洁的解决方案:
library(zoo)
month <- "2000-03"
as.Date(as.yearmon(month))
[1] "2000-03-01"
as.Date
会为您确定每个月第一天的 yearmon 对象。
您也可以使用 lubridate
-package 中的 parse_date_time
或 fast_strptime
函数来实现这一点:
> 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_time
允许润滑式格式规范,而fast_strptime
需要与strptime
相同的格式规范。
要指定时区,可以使用 tz
-参数:
> parse_date_time(dates1, "ym", tz = "CET")
[1] "2009-01-01 CET" "2009-02-01 CET" "2009-03-01 CET"
当日期时间数据中存在不规则时,可以使用 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")
随时使用软件包:
library(anytime)
anydate("2009-01")
# [1] "2009-01-01"
事实上,正如上面提到的(以及 SO 的其他地方(,为了将字符串转换为日期,您需要一个月的特定日期。从as.Date()
手册页:
如果日期字符串未完全指定日期,则返回的答案可能是特定于系统的。最常见的行为是假设缺少的年、月或日是当前的。如果它错误地指定了日期,可靠的实现将给出错误,并将日期报告为 NA。不幸的是,一些常见的实现(如
glibc
(是不可靠的,并且猜测预期的含义。
一个简单的解决方案是将日期"01"
粘贴到每个日期,并使用strptime()
将其指示为该月的第一天。
对于那些寻求更多关于在 R 中处理日期和时间的背景知识的人:
在 R 中,时间使用POSIXct
,POSIXlt
类和日期使用Date
类。
日期存储为自 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"
我认为@ben-rollert的解决方案是一个很好的解决方案。
如果您想在新包内的函数中使用此解决方案,您只需要小心。
开发包时,建议使用语法packagename::function_name()
(请参阅 http://kbroman.org/pkg_primer/pages/depends.html(。
在这种情况下,您必须使用zoo
库定义的as.Date()
版本。
这是一个例子:
> 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"))
一种使用lubridate
ym
的方法。
可以是数字、缩写月份或带有各种分隔符(即使没有分隔符(的完整月份名称,例如
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"
请注意,如果您反过来使用它,也会有my
,例如 Sep/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))
tidyverse
最近添加了clock
包,此外还有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_resolve
处理无效日期,也可以使用invalid_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
参数指定以下值以处理无效日期:
"上一个":上一个有效的时刻。
"前一天":前一天的有效日期,保留一天中的时间。
"下一个":下一个有效的时刻。
"第二天":下一个有效日期,保持一天中的时间。
"溢出":按输入无效的天数溢出由。删除一天中的时间。
"溢出日":按输入的天数溢出无效。保留一天中的时间。
"NA":将无效日期替换为 NA。
"错误":无效日期出错。