使用R计算季节均值的最优雅方法是什么?

8
我有一组等间距的时间序列,其中包含每日平均观测数据。
最简单的方法是如何计算季节平均值? 季节应该遵循气象学术语,即DJF(=冬季:12月,1月,2月),MAM,JJA和SON。
这意味着12月的值来自x-1年。
月平均值的计算在此处得到了很好的展示: How to calculate a monthly mean? 在计算季节平均值时,可以按照此思路进行。 然而,有几个注意事项使其不太透明,因此必须小心!
我之前在一个旧帖子中已经处理了这个问题的一小部分: How to switch rows in R? 现在这里是完整的说明: 0:制作随机时间序列
ts.pdsi <- data.frame(date = seq(
                from=as.Date("1901-01-01"), 
                to=as.Date("2009-12-31"), 
                by="day"))
ts.pdsi$scPDSI <- rnorm(dim(ts.foo)[1],  mean=1, sd=1)    # add some data

第一步:使用seas包并将季节性添加到您的时间序列中,该序列必须格式化为一个数据框。

library(seas)
# add moth/seasons
ts.pdsi$month  <- mkseas(ts.pdsi,"mon")   # add months
ts.pdsi$seas <- mkseas(ts.pdsi,"DJF")     # add seasons
ts.pdsi$seasyear <- paste(format(ts.pdsi[,1],"%Y"), 
                          ts.pdsi$seas ,sep="")   # add seasyears, e.g. 1950DJF

这是给出的。
> head(ts.pdsi)
    date      scPDSI month seas seasyear
1 1901-01-01 -0.10881074   Jan  DJF  1901DJF
2 1901-02-01 -0.22287750   Feb  DJF  1901DJF
3 1901-03-01 -0.12233192   Mär  MAM  1901MAM
4 1901-04-01 -0.04440915   Apr  MAM  1901MAM
5 1901-05-01 -0.36334082   Mai  MAM  1901MAM
6 1901-06-01 -0.52079030   Jun  JJA  1901JJA

第二步:你可以使用列$seasyear,按照上述方法计算季节平均值。

> MEAN <- tapply(pdsi$scPDSI, ts.pdsi$seasyear, mean, na.rm = T)
> head(MEAN)
1901DJF     1901JJA     1901MAM     1901SON     1902DJF     1902JJA 
-0.45451556 -0.72922229 -0.17669396 -1.12095590 -0.86523850 -0.04031273 

注意:春季(MAM)和夏季(JJA)由于严格的字母排序而被交换了。

第三步:将其切换回来。

foo <- MEAN
for(i in 1:length(MEAN)) {
    if (mod (i,4) == 2) {
        foo[i+1] <- foo[i]    #switch 2nd 3rd row (JJA <-> MAM)
        foo[i] <- MEAN[i+1]
    }
}
# and generate new names for the array
d <- data.frame(date=seq(from=as.Date("1901-01-01"), to=as.Date("2009-12-31"), by="+3 month"))
d$seas <- mkseas(d,"DJF") 
d$seasyear <- paste(format(d[,1],"%Y"), d$seas ,sep="")
names(foo)<-d$seasyear  # add right order colnames
MEAN <-foo

最终,这导致了一个季节平均数的时间序列。我认为这太复杂了,而且我猜想周围有更简单的解决方案。

此外,这个解决方案在冬季DJF也存在一个非常大的问题:12月份还没有从前一年中选择。这很容易修复(我猜),但使给定的方式变得更加复杂。

我真的希望有更好的想法!


这段代码可能有所帮助:dd <- c(Sys.Date(), as.Date(c("2013-11-30", "2013-12-01"))); season_year <- as.numeric(format(dd + 31, "%Y")). - Josh O'Brien
好的,这段代码片段可能会有所帮助。 - stephan
为了解决冬季问题(在DJF中,D应该是n-1年的D),一个想法是创建一个“虚假”的年份列,除了12月使用n+1外,每个月都使用当前年份的值。 - user2165907
4个回答

5

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

因为 mkseas 将日期转换为按所需顺序排列的季节因子级别,所以在对年度和季节进行汇总后,顺序仍然正确。

Henrik,这个看起来真的很漂亮/优美!没错,mkseas也为DJF保持了正确的顺序。 - stephan
我最终添加了一个日期变量,以便绘制时间序列图。
df2$date <- seq(from=min(df$date), to=max(df$date), by="+3 month")
- stephan
这个不能处理月度数据(其中DJF跨越两年),已添加月度解决方案作为答案。 - mlcyo

2

起初,使用数字而不是字符串表示月份和季节可能更加容易。通过简单的算术运算,包括将12月作为下一年的一部分,您可以得到想要的季节。

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

要计算季节平均值,您可以简单地执行以下操作:
meanArray<-tapply(pdsi$scPDSI,list(year=pdsi$syear,seas=pdsi$seas),mean)

现在你拥有了

>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

如果您想将其作为一个扁平数组,并带有适当的名称,您首先需要对其进行转置,然后将数组展平,并添加名称。
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="")

这将获得您想要的结果。
> head(MEAN)
  1901DJF   1901MAM   1901JJA   1901SON   1902DJF   1902MAM 
1.0779676 1.0258306 1.1515175 0.9682434 0.9900312 0.8964994  

1

我遇到了同样的问题,但是针对每月数据,aggregate无法按年份分割DJF。为了解决这个问题,您可以添加一个合成年列,将12月的值分配给下一年。

library(dplyr)
library(seas)
library(lubridate)

df <- data.frame(yearmonth = c("187601", "187602", "187603", "187604", "187605", "187606", "187607","187608", "187609", "187610", "187611", "187612", "187701", "187702", "187703", "187704", "187705", "187706", "187707", "187708", "187709", "187710", "187711", "187712", "187801", "187802", "187803", "187804", "187805", "187806", "187807", "187808", "187809", "187810", "187811", "187812", "187901", "187902", "187903", "187904", "187905", "187906", "187907", "187908", "187909", "187910", "187911", "187912"), 
                 SOI = rnorm(n = 48, mean = 0, sd = 4))


df %>% 
  mutate(yearmonth = lubridate::ymd(yearmonth, truncated = 1),
         year = year(yearmonth),
         month = month(yearmonth),
         seas = mkseas(yearmonth, width = "DJF"),
         year2 = ifelse(test = month == 12,
                        yes = year + 1,
                        no = year)) %>% 
  group_by(year2, seas) %>% 
  summarise(meanSOI = mean(SOI))

1

正如所提到的,可以有非常简单的解决方案(也在这里发布)。我会使用zooseas软件包的组合按季节进行聚合,看起来类似于:

library(zoo); library(seas)

seasTS <- aggregate(dataTS, mkseas(x=time(dataTS),width="DJF"), sum)

要为每年完成这个任务,只需要按照年份循环 mkseas()。请帮我加点语法糖来调味我的咖啡。
谢谢,
Adam

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接