R中跨年的时间总计

时间:2020-06-03 01:31:09

标签: r aggregate

我有10年的日降水量数据。我试图获取例如下一年的12月至4月之间的总降水量。 另一个复杂之处在于,该时期可能会发生变化,例如从12月15日更改为3月15日

如果我不必花费数年的时间,我知道如何使用aggregategroup_by来做到这一点。但是我完全不知道如何解决今年跨界问题。

这是我要获取的代码示例。

library(lubridate)

precip <- data.frame(d = seq.Date(from = as.Date('2001-01-01'),
                                  to = as.Date('2004-12-31'),
                                  by = 'day'),
                     prec = runif(1461))

precip$y <- year(precip$d)
precip$m <- month(precip$d)

# I can aggregate by year
aggregate(precip$prec, by = list(precip$y), sum)

# I can aggregate by year, month
aggregate(precip$prec, by = list(precip$y, precip$m), sum)

# How can I aggregate by a period that crosses between years?
# my desired output would be something like
# Group.1        x
# 1 2001-12-15 to 2002-03-15 184.4885
# 2 2002-12-15 to 2003-03-15 192.8315
# 3 2003-12-15 to 2004-03-15 178.8507

我不需要组名来将句点包含为字符串。它可能只是一个索引。

2 个答案:

答案 0 :(得分:1)

该问题后来被更新为询问不是整个月的季节,因此这是对原始答案的更新以解决该问题。它使用“注释”中的输入以及原型季节的开始日期和结束日期。如果季节跨越2月底,请确保选择choose年(例如下面的示例中所示的2000年)。

我们创建一个从开始日期到结束日期的所有日期的序列,称为模板。将其转换为季节中所有可能的月/日的字符向量,mmdd。

接下来定义in_season,它在每行沉淀中都有一个元素,如果该行的月和日与模板中的任何月和日匹配,则为TRUE。

然后定义season_no,每行悬崖有一个元素,以唯一的编号标识每个季节。对于日期不在季节中的行,该数字为0,否则为正数。

将数据细分为第几季中的行,并计算每个季节中的最小和最大日期,并给出precip0。

最后在开始/结束日期之前汇总prec,并使用汇总来查找每个季节中有多少天。如果存在的话,这将包括部分季节。如果不需要此子集,则数据首先或子集结果,如代码中的注释行所示。

不使用任何软件包。

# to change definition of season change next 2 lines
start_template <- as.Date("1999-12-15")
end_template <- as.Date("2000-03-15")  # note that year 2000 incl Feb 29

# mmdd character vector contains the mm-dd values in season
template <- seq(start_template, end_template, "day")
mmdd <- format(template, "%m-%d")

in_season <- format(precip$d, "%m-%d") %in% mmdd
season_no <- with(rle(in_season), rep(seq_along(lengths), lengths)) * in_season

precip0 <- transform(subset(cbind(precip, season_no), in_season),
  start_date = ave(d, season_no, FUN = min),
  end_date = ave(d, season_no, FUN = max))

ag <- aggregate(cbind(days = 1, prec) ~ start_date + end_date, precip0, sum)

# uncomment if partial seasons not wanted
# ag <- subset(ag, days >= length(mmdd) - 1)  

给予:

> ag
  start_date   end_date days      prec
2 2001-01-01 2001-03-15   74 37.963828
3 2001-12-15 2002-03-15   91 44.543114
4 2002-12-15 2003-03-15   91 43.182177
5 2003-12-15 2004-03-15   92 44.083236
1 2004-12-15 2004-12-31   17  9.180353

假设答案是整个月

输入是Precip(在末尾和季节的Note中给出,这是月份数字(Jan = 1,Feb = 2,...,Dec = 12)的向量),以便它们在季节内出现。在下面的示例中,我们使用c(12,1:3),即Dec-Mar。

下面的代码使用precip和season来设置以下变量:

  • last_month是季节中最后一个月的月数
  • ym是Yearmon类向量,具有相应的年/月。在内部,它表示为年份加1月的0、2月的1/12,...,12月的11/12。ym与x相同。
  • cross是一个逻辑标量,如果季节越过年份边界则为TRUE,否则为FALSE
  • 如果相应的日期在季节内,则
  • in_season为TRUE。 in_season与x的长度相同。
  • 如果日期是在季节中,则
  • start_year和end_year是季节开始和结束的相应年份,如果不是季节,则为0。 start_year和end_year的长度分别与x相同。
  • 开始日期和结束日期是相应的季节开始日期和结束日期

然后,将start_year和end_year以及子集插入到季节为TRUE的那些行中。最后,我们按start_year和end_year进行汇总。

library(zoo)

# define season as Dec - Mar
season <- c(12, 1:3)  # month numbers in order they appear in season

last_month <- tail(season, 1)
ym <- as.yearmon(precip$d)
cross <- last_month < season[1]
in_season <- cycle(ym) %in% season

start_year <- as.integer(ym - cross * last_month / 12) * in_season
end_year <- start_year + cross * in_season

start_date <- as.Date(paste(start_year, season[1], 1, sep = "-"))
end_date <- as.Date(as.yearmon(paste(end_year, last_month, sep = "-")), frac = 1)

precip0 <- subset(data.frame(start_date, end_date, precip), in_season)
aggregate(prec ~ start_date + end_date, precip0, sum)

给予:

  start_date   end_date     prec
1 2000-12-01 2001-03-31 45.70959
2 2001-12-01 2002-03-31 58.67224
3 2002-12-01 2003-03-31 57.93712
4 2003-12-01 2004-03-31 59.66424
5 2004-12-01 2005-03-31 16.69944

或者也许使用start_year和end_year,从那时起,我们可以很容易地得出prec与end_year的关系。

precip0 <- subset(data.frame(start_year, end_year, precip), in_season)
aggregate(prec ~ start_year + end_year, precip0, sum)

注意

我们假设输入的前缀如下。除了我们添加了set.seed以使其可重现外,该问题与问题相同。

set.seed(123)
precip <- data.frame(d = seq.Date(from = as.Date('2001-01-01'),
                                  to = as.Date('2004-12-31'),
                                  by = 'day'),
                     prec = runif(1461))

答案 1 :(得分:0)

我相信我使用cut解决了它。这样,我可以设置任意的开始日期和结束日期,只要它们不在同一个月即可。那是因为我使用开始月份作为过滤器来剔除季节观测值。

我不确定这是最简单或最优雅的解决方案。但是...

startDate <- as.Date('2003-12-01')
endDate <- as.Date('2004-04-01')

start_month <- month(startDate)
start_day <- day(startDate)

end_month <- month(endDate)
end_day <- day(endDate)

start_year <- min(year(precip$d))
end_year <- max(year(precip$d))

breaks <- lapply(start_year:end_year, function (x) {
  c(paste (x, start_month, start_day), paste ( x + 1, end_month, end_day))
})

breaks <- unlist(breaks)

precip$season <- cut(precip$d, ymd(breaks))

precip0 <- precip[month(ymd(precip$season)) == start_month,]

aggregate(prec ~ season, precip0, sum)

这导致

      season     prec
1 2001-12-01 58.67224
2 2002-12-01 57.93712
3 2003-12-01 59.66424
4 2004-12-01 16.69944