我有一个中等大小的数据集,这是一个从数据集中获取的例子:
2011.2012
9/7
11/5
12/15
1/5
2/5
我想将此数据转换为时间序列格式。
将它们转换为因子后的字符后,我使用了as.Dates函数,但是我遇到了一个小故障。
结果假设缺少的一年是当年。我的目标是能够将2011年1月1日之前的日期转换为2012年1月1日之后的日期。数据范围介于2011年9月至2012年4月。
我尝试过使用原点并开始,但无济于事。这是我的代码:
date1 <- as.character(2011.2012)
date1 <- as.Date(date1, format="%m/%d")
答案 0 :(得分:6)
六月/七月分裂怎么样?这取决于您的日期格式。
> x=c("9/7", "11/5", "12/15", "1/5", "2/5" )
> sapply(strsplit(x, '/')
, function(x) paste(if(as.numeric(x[1]) > 6) 2011 else 2012, x[1], x[2]
, sep="/"
)
)
[1] "2011/9/7" "2011/11/5" "2011/12/15" "2012/1/5" "2012/2/5"
以上是上述的矢量化方法,使用ifelse
而不是if
:
mm <- matrix(nrow=2, unlist(strsplit(x, '/')))
paste(ifelse(as.numeric(mm[1,]) > 6, 2011, 2012), mm[1,], mm[2,], sep='/')
[1] "2011/9/7" "2011/11/5" "2011/12/15" "2012/1/5" "2012/2/5"
矢量化方法可读性较差,但速度更快(1.7x)。
这是一种可以使用Date
函数对@MarkMiller的方法进行矢量化并在本月寻找回归的方法:
initialYear <- 2011
dd <- as.Date(x, "%m/%d")
mon <- format(dd, "%m")
as.Date(paste(initialYear + c(0, cumsum(diff(as.numeric(mon))<0))
, mon
, format(dd, "%d")
, sep="-"
)
)
[1] "2011-09-07" "2011-11-05" "2011-12-15" "2012-01-05" "2012-02-05"
可能由于所有Date
函数,运行时间比上面的矢量化方法长3.6倍(如果删除最终as.Date
则为2.6倍),仅限于2011年和2012年。我没有测量Mark的代码,但它可能比三个sapply
和一个明确的for
循环更快。
答案 1 :(得分:3)
这是我想出的。我不知道这段代码会一直有效,但它似乎与我使用的示例数据集一起工作。该代码似乎处理了> 2年和一年中的任何一天。
代码无法处理没有数据的年份,但如果年份不在数据集中,则无论如何都无法识别出这样的差距。
另请注意,此方法将失败,并显示以下两个日期:“1/30”和“3/1”,如果这两个日期是 连续两年。那是因为两个日期之间存在这么长的差距,没有办法 让计算机意识到这两个日期并非来自同一年。
换句话说,如果两个连续日期之间存在很长的差距,任何方法都可能会失败 附加信息。例如,如果每个季度或半年至少有一个日期,那么我认为所有发布的答案都能正常工作,因为计算机能够确定连续月份的减少,以表示新的一年。
如果两个连续日期之间的最长差距为11个月,则两种方法都可行。如果代码被修改为也可以检查连续两个日期的每个月的日期,那么可能会有363天的差距。
# specify the initial year and create dates from the data
initial.year = 2010
date <- c("12/30", "1/1", "6/1", "6/1", "10/25", "11/27", "12/28",
"1/16", "2/17", "2/17", "2/17")
DDD3 <- as.Date(date, format="%m/%d")
# deconstruct dates into month, day and erroneous year
dtstr <- as.character(DDD3)
month <- as.numeric(as.character(sapply(strsplit(dtstr, "-") , "[", 2)))
day <- as.numeric(as.character(sapply(strsplit(dtstr, "-") , "[", 3)))
year <- as.numeric(as.character(sapply(strsplit(dtstr, "-") , "[", 1)))
DDD4 <- data.frame(month, day, year)
# obtain correct year for each date
year2=rep(NA, nrow(DDD4))
year2[1] = initial.year
for(i in 2:length(year2)) {
if(DDD4[i,1] < DDD4[(i-1),1]) (year2[i] = year2[(i-1)]+1)
if(DDD4[i,1] >= DDD4[(i-1),1]) (year2[i] = year2[(i-1)])
}
# create new dates using correct year
day2 <- sprintf("%02d", day)
month2 <- sprintf("%02d", month)
year2 <- as.character(year2)
DDD5 <- data.frame(month2, day2, year2)
DDD6 <- paste(DDD5[,1], DDD5[,2], DDD5[,3], sep='/')
DDD7 <- as.Date(DDD6, "%m/%d/%Y")
DDD7
# [1] "2010-12-30" "2011-01-01" "2011-06-01" "2011-06-01"
# "2011-10-25" "2011-11-27" "2011-12-28" "2012-01-16"
# "2012-02-17" "2012-02-17" "2012-02-17"
答案 2 :(得分:0)
我发布的这个可以称为矢量化方法,与目前提供的方法不同。我认为ifelse
是伪矢量化,因为需要构造然后选择三个矢量。
dat <- read.table(text="2011.2012
9/7
11/5
12/15
1/5
2/5", header=TRUE)
dat$date1 <- as.Date(dat$X2011.2012, format="%m/%d")
dat$GT <- c(FALSE, diff(dat$date1) < 0)
startyr <- cumsum( as.numeric( substr(names(dat)[1], 2,5) ) )
dat$truedate <- paste( format(dat$date1, format="%m/%d") ,
dat$GT+startyr, sep="-")
dat
#-------------------------
X2011.2012 date1 GT truedate
1 9/7 2012-09-07 0 09/07-2011
2 11/5 2012-11-05 0 11/05-2011
3 12/15 2012-12-15 0 12/15-2011
4 1/5 2012-01-05 1 01/05-2012
5 2/5 2012-02-05 1 02/05-2012
我认为将第一行作为标题阅读是完全合法的,但如果需要,使用cumsum
操作的diff.Date
的替代方案仍应“向量化”。这不仅限于两年,如果每年甚至有一个日期就应该成功。