What is the most elegant way to calculate seasonal means with R?
I this what you want?
# # create some data: daily values for three years
df <- data.frame(date = seq(from = as.Date("2007-01-01"),
to = as.Date("2009-12-31"),
by = "day"))
df$vals <- rnorm(nrow(df))
# add year
df$year <- format(df$date, "%Y")
# add season
df$seas <- mkseas(x = df, width = "DJF")
# calculate mean per season within each year
df2 <- aggregate(vals ~ seas + year, data = df, mean)
df2
# seas year vals
# 1 DJF 2007 -0.048407610
# 2 MAM 2007 0.086996842
# 3 JJA 2007 0.013864555
# 4 SON 2007 -0.081323367
# 5 DJF 2008 0.170887946
# 6 MAM 2008 0.147830260
# 7 JJA 2008 0.003008866
# 8 SON 2008 -0.057974215
# 9 DJF 2009 -0.043437437
# 10 MAM 2009 -0.048345979
# 11 JJA 2009 0.023860506
# 12 SON 2009 -0.060076870
Because mkseas
converts the dates into a seasonal factor with levels in the desired order, the order is correct also after the aggregation over year and season.
It's probably easier if you use numbers rather than strings for months and seasons, at least at first. You can get the seasons you want by simple arithmetic manipulations, including making December part of the subsequent year.
pdsi <- data.frame(date = seq(
from=as.Date("1901-01-01"),
to=as.Date("2009-12-31"),
by="day"))
pdsi$scPDSI <- rnorm(nrow(pdsi), mean=1, sd=1)
pdsi$mon<-mon(pdsi$date)+1
pdsi$seas<-floor((pdsi$mon %% 12)/3)+1
pdsi$year<-year(pdsi$date)+1900
pdsi$syear<-pdsi$year
pdsi$syear[pdsi$mon==12]<-pdsi$syear[pdsi$mon==12]+1
To compute seasonal means, you can simply do this:
meanArray<-tapply(pdsi$scPDSI,list(year=pdsi$syear,seas=pdsi$seas),mean)
And now you have
>head(meanArray)
seas
year 1 2 3 4
1901 1.0779676 1.0258306 1.1515175 0.9682434
1902 0.9900312 0.8964994 1.1028336 1.0074296
1903 0.9912233 0.9858088 1.1346901 1.0569518
1904 0.7933653 1.1566892 1.1223454 0.8914211
1905 1.1441863 1.1824074 0.9044940 0.8971485
1906 0.9900826 0.9933909 0.9185972 0.8922987
If you want it as a flat array, with appropriate names, you first take the transpose, and then flatten the array, and add the names
colnames(meanArray)<-c("DJF","MAM","JJA","SON")
meanArray<-t(meanArray)
MEAN<-array(meanArray)
names(MEAN)<-paste(colnames(meanArray)[col(meanArray)],rownames(meanArray)[row(meanArray)],sep="")
This gets you get the result you wanted
> head(MEAN)
1901DJF 1901MAM 1901JJA 1901SON 1902DJF 1902MAM
1.0779676 1.0258306 1.1515175 0.9682434 0.9900312 0.8964994