考虑leap年,如何将一年的每日数据复制到多年?

时间:2019-09-16 14:33:27

标签: r time-series xts replicate leap-year

我一年有365天的每日价值。现在,我要复制2014年至2018年的这些值。如果有a年,则将2月29日和2月28日的平均值设为2月29日。

如何自动执行此操作?到目前为止,这就是我所拥有的。提前非常感谢您!

library(xts)

set.seed(1)
myday <- seq(1, 365, by = 1)
myvalue <- rnorm(length(myday))
mydata <- data.frame(myday, myvalue)
head(mydata)
#>   myday    myvalue
#> 1     1 -0.6264538
#> 2     2  0.1836433
#> 3     3 -0.8356286
#> 4     4  1.5952808
#> 5     5  0.3295078
#> 6     6 -0.8204684

myyear <- seq(2014, 2018, by = 1)
myyear
#> [1] 2014 2015 2016 2017 2018

leapyearvalue <- 0.5 * (mydata$myvalue[mydata$myday == 28] + mydata$myvalue[mydata$myday == 29])
leapyearvalue
#> [1] -0.9744512

repdata <- coredata(mydata)[rep(seq(nrow(mydata)), length(myyear)), ]
head(repdata)
#>   myday    myvalue
#> 1     1 -0.6264538
#> 2     2  0.1836433
#> 3     3 -0.8356286
#> 4     4  1.5952808
#> 5     5  0.3295078
#> 6     6 -0.8204684

2 个答案:

答案 0 :(得分:1)

编辑:添加了2014-18的输出。

这是一个可以帮助您解决此问题的功能。输入year和day_num(用于该年份),并输出该日期的标准值。我假设您要在year年的4月1日从标准年输出4月1日,这需要从365天的年中的第92天(the年)转换为第91天。

daily_value <- function(year, day_num) {
  leap <- year %in% c(2008, 2012, 2016, 2020, 2024)
  leap_day_val <- 0.5 * (mydata[59,2] + mydata[60,2])
  day_num_adj <- day_num + ifelse(leap & day_num >= 61, -1, 0)
  day_value <- ifelse(leap & day_num == 60, 
                       leap_day_val,
                       mydata[day_num_adj,2])
  day_value
}

测试

mydata[59,]
#   myday   myvalue
#59    59 0.5697196
daily_value(2016,59)
#[1] 0.5697196

mydata[59:60,]
#   myday    myvalue
#59    59  0.5697196
#60    60 -0.1350546
mean(c(0.5697196, -0.1350546))
#[1] 0.2173325
daily_value(2016,60)
#[1] 0.2173325

# Day 61 of 2016 was March 1, which is day 60 in years with 365 days
mydata[60,]
#   myday    myvalue
#60    60 -0.1350546
daily_value(2016,61)
#[1] -0.1350546

现在,我们可以将其应用于2014-18的所有日子:

output <- data.frame(dates = seq.Date(as.Date("2014-01-01"), as.Date("2018-12-31"), 1))
output$day_of_year = lubridate::yday(output$dates)
output$value       = daily_value(lubridate::year(output$dates), output$day_of_year)


subset(output, day_of_year > 58 & day_of_year <= 61)
#          dates day_of_year      value
#59   2014-02-28          59  0.5697196
#60   2014-03-01          60 -0.1350546
#61   2014-03-02          61  2.4016178

#424  2015-02-28          59  0.5697196
#425  2015-03-01          60 -0.1350546
#426  2015-03-02          61  2.4016178

#789  2016-02-28          59  0.5697196
#790  2016-02-29          60  0.2173325  # Leap day gets avg of 2/28 and 3/01
#791  2016-03-01          61 -0.1350546  # Rest of leap year shifted back one day

#1155 2017-02-28          59  0.5697196
#1156 2017-03-01          60 -0.1350546
#1157 2017-03-02          61  2.4016178

#1520 2018-02-28          59  0.5697196
#1521 2018-03-01          60 -0.1350546
#1522 2018-03-02          61  2.4016178

答案 1 :(得分:1)

您可以使用leap_year中的lubridate函数。我编写了两个函数来自动化您的任务:

set.seed(1)
mydata <- rnorm(365)

generate_days <- function(years){
  unlist(sapply(years, function(x) {
  if (lubridate::leap_year(x)){
    1:366} else {
      1:365}
  }))
}

generate_data <- function(years, my_data){ 
  unlist(sapply(years, function(x) {
  if (lubridate::leap_year(x)){
    c(my_data[1:59], mean(my_data[59:60]), my_data[60:365])} else {
      my_data}
}))
}

df <- data.frame(days = generate_days(2014:2018),
                 value = generate_data(2014:2018, mydata))

df[730+60,]