thiagoveloso thiagoveloso - 2 months ago 11
R Question

R - Calculate sum within date range using zoo

Suppose I have a data frame with ten years of daily temperature data (in degree C) like this:

mydf <- data.frame(Date = seq(as.Date("2001/1/1"), as.Date("2010/12/31"), by = "day"), Temp = runif(3652, 0, 40))

I am trying to calculate growing degree days for plants. This is how it works: within a date range, I need to integrate the difference between the daily temperature and a base temperature, let's say 10 degrees C. To make it harder, the date range goes across years. For example, I need to calculate the growing days between november 1st and march 31st for all years in the time series. In terms of an "algorithm", the logic would be something like this:

t_base <- 10

for (each day between nov 1st and mar 31st) {
sum (Temp - t_base)

How to do this using the zoo package?


Note that "yearmon" class variables are of the form year + frac where the frac is 0 for Jan, 1/12 for Feb, 2/12 for Mar, etc. Below ym is a "yearmon" vector corresponding to the Date except that we have added two months. ym is then split into year y (the season-end year) and month m (where month is 0 for the first month of the season, 1 for the second month, ..., 4 for the 5th and last month in season and higher numbers for months not in season) . in.seas is TRUE for those data points in Nov, Dec, Jan, Feb or Mar (which corresponds to m <= 4). Finally use ave to calculate the cumulative sum among dates having the same season-end year or aggregate to calculate the sum.


z <- read.zoo(mydf)
ym <- as.numeric(as.yearmon(index(z)) + 2/12)
y <- floor(ym) # year of date's season end or this year if not in season
m <- round(12 * (ym - y))  # month Nov = 0, Dec = 1, Jan = 2, Feb = 3, Mar = 4, ...
in.seas <- m <= 4
Cum <- ave(z[in.seas], y[in.seas], FUN = function(x) cumsum(x - t_base))

or to just get the sum of each season:

Sum <- aggregate(z[in.seas], y[in.seas], function(x) sum(x - t_base))

Note that fortify.zoo(x) will convert zoo object x back to a data frame should that be necessary.