1

我有一个中等大小的数据集,下面是一个取自数据集的示例:

2011.2012
9/7 
11/5
12/15
1/5
2/5

我想将此数据转换为时间序列格式。

在将它们从因子转换为字符后,我使用了 as.Dates 函数,但我遇到了一个小故障。

结果假设缺少的年份是当前年份。我的目标是能够将 1/1 之前的日期转换为 2011 年,将 1/1 之后的日期转换为 2012 年。数据范围介于 2011 年 9 月和 2012 年 4 月之间。

我试过使用 origin 和 start ,但无济于事。这是我的代码:

date1 <- as.character(2011.2012)
date1 <- as.Date(date1, format="%m/%d") 
4

3 回答 3

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

这是您可以矢量化@MarkMiller 的方法的一种方法,使用这些Date函数并在当月寻找回归:

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 倍(如果去掉 finalas.Date则为 2.6 倍),仅限于 2011 年和 2012 年。我没有测量 Mark 的代码,但它可能比上面更快三个sapply和一个显式for循环。

于 2012-12-30T06:11:06.877 回答
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"
于 2012-12-30T07:19:53.847 回答
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“矢量化”。这不仅限于两年,而且即使每年只有一个日期,也应该是成功的。

于 2012-12-30T16:54:33.887 回答