将月,日和无年转换为时间序列

时间:2012-12-30 05:54:38

标签: r

我有一个中等大小的数据集,这是一个从数据集中获取的例子:

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") 

3 个答案:

答案 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的替代方案仍应“向量化”。这不仅限于两年,如果每年甚至有一个日期就应该成功。