【问题标题】:How to find rolling sd over several months with daily data in R如何使用 R 中的每日数据找到几个月的滚动 sd
【发布时间】:2018-02-20 20:49:05
【问题描述】:

我对 R 相当陌生,我试图在整数组中找到几个月(3、6、9)内的滚动标准偏差。例如,对于一年的数据和三个组,我想找到(Jan、Feb、Mar)、(Feb、Mar、Apr)、(Mar、Apr、五月)等。

在我的数据框 df 中,我有列 NUM:具有要从中查找标准偏差的值,列 NO:定义组的整数,以及列 date:具有每日日期。我还创建了 Yr_Mo 列,它是一个整数,对应于日期的年月。因此,例如,2017 年 1 月的所有日期在 Yr_Mo 列中的值为 1701

一个月,我使用聚合: 新

这是非常基本的。但是,对于 3 个月以上的团体来说,这似乎更复杂。因为并非所有月份的长度都相同,并且有些月份缺少日期,所以我无法对某些时间间隔进行硬编码。我看过很多关于我的类似问题的帖子,但这些问题似乎一般都是关于寻找滚动标准开发人员或分组但不是两者兼而有之。我正在考虑使用诸如rollapply之类的动物园功能,但又看不到如何考虑我的问题的两个部分。

在此先感谢您提供的任何帮助或指向我可能从中学习的文档的指示!

NO date       Yr_Mo  NUM
1  2017-01-01 1701   3.4
1  2017-01-02 1701   5
1  2017-01-12 1701   4.2
1  2017-01-13 1701   1
1  2017-01-20 1701   6
1  2017-02-03 1702   3.9
1  2017-02-08 1702   5.5
1  2017-02-15 1702   8
1  2017-02-22 1702   1.1
1  2017-02-26 1702   4
1  2017-03-02 1703   1
1  2017-03-07 1703   7.5
1  2017-03-11 1703   2
1  2017-03-20 1703   3.1
1  2017-03-28 1703   2
1  2017-04-01 1704   2
1  2017-04-05 1704   3.5
1  2017-04-12 1704   1
1  2017-04-19 1704   4.1
1  2017-04-23 1704   5
1  2017-05-02 1705   1
1  2017-05-03 1705   4.5
1  2017-05-04 1705   2
1  2017-05-10 1705   6.1
1  2017-05-20 1705   7
2  2017-01-01 1701   3
2  2017-01-02 1701   53
2  2017-01-11 1701   2
2  2017-01-15 1701   4.1
2  2017-01-22 1701   1
2  2017-02-01 1702   8.9
2  2017-02-08 1702   1.5
2  2017-02-15 1702   3
2  2017-02-27 1702   7.2
2  2017-02-28 1702   4
2  2017-03-02 1703   1
2  2017-03-07 1703   5.2
2  2017-03-11 1703   2
2  2017-03-21 1703   1
2  2017-03-28 1703   2
2  2017-04-01 1704   2.4
2  2017-04-05 1704   3.5
2  2017-04-11 1704   1
2  2017-04-19 1704   4.1
2  2017-04-23 1704   3
2  2017-05-02 1705   1.2
2  2017-05-03 1705   4.5
2  2017-05-04 1705   2
2  2017-05-10 1705   6.1
2  2017-05-21 1705   9

【问题讨论】:

  • 欢迎来到 Stack Overflow,为了在这里寻求帮助,请考虑how to write a reproducible example,谢谢。
  • 没有看到您的数据很难说更多,但一般的想法是您创建一个“组”变量,将您的数据拆分为每个 3 个月以上的非重叠块。那么通常的group_by 语义应该是直截了当的。
  • 嗯,我不明白如何使用非重叠块来获得滚动标准开发人员?我添加了我正在使用的数据版本,以防我的问题更清楚。

标签: r standard-deviation rollapply


【解决方案1】:

使用方差的定义(参见sample variance)和OP在问题中提到的内容(即aggregaterollapply),我们可以计算滚动3个月的标准差,如下所示。更多 cmets 内联。

winsize <- 3

#calculate sum of squares of NUM by month and group
sumxsq <- aggregate(NUM ~ Yr_Mo + NO, df, function(x) sum(x^2))
names(sumxsq) <- c("Yr_Mo", "NO", "SUM_X_SQ")

#calculate sum of NUM by month and group
sumx <- aggregate(NUM ~ Yr_Mo + NO, df, sum)
names(sumx) <- c("Yr_Mo", "NO", "SUM_X")

#count number of observations by month and group
nobs <- aggregate(NUM ~ Yr_Mo + NO, df, length)
names(nobs) <- c("Yr_Mo", "NO", "N")

#merge all stats together
mySD <- merge(merge(sumxsq, sumx, by=c("NO","Yr_Mo")), nobs, by=c("NO","Yr_Mo"))

#calculate rolling sample variance using zoo::rollapplyr by group, then take sqrt for sd
mySD$STD_DEV <- sqrt(unlist(by(mySD, mySD$NO, function(submySD) {
    zoo::rollapplyr(submySD, 
        width=winsize, 
        FUN=function(x) (sum(x[,"SUM_X_SQ"]) - sum(x[,"SUM_X"])^2 / sum(x[,"N"])) / (sum(x[,"N"]) - 1), 
        by.column=FALSE,
        fill=NA)
})))
mySD

解决方案假定每个组每个月至少有 1 个数据点。如果这有帮助,请告诉我。

数据:

df <- read.csv(text="NO,date,Yr_Mo,NUM
1,2017-01-01,1701,3.4
1,2017-01-02,1701,5
1,2017-01-12,1701,4.2
1,2017-01-13,1701,1
1,2017-01-20,1701,6
1,2017-02-03,1702,3.9
1,2017-02-08,1702,5.5
1,2017-02-15,1702,8
1,2017-02-22,1702,1.1
1,2017-02-26,1702,4
1,2017-03-02,1703,1
1,2017-03-07,1703,7.5
1,2017-03-11,1703,2
1,2017-03-20,1703,3.1
1,2017-03-28,1703,2
1,2017-04-01,1704,2
1,2017-04-05,1704,3.5
1,2017-04-12,1704,1
1,2017-04-19,1704,4.1
1,2017-04-23,1704,5
1,2017-05-02,1705,1
1,2017-05-03,1705,4.5
1,2017-05-04,1705,2
1,2017-05-10,1705,6.1
1,2017-05-20,1705,7
2,2017-01-01,1701,3
2,2017-01-02,1701,53
2,2017-01-11,1701,2
2,2017-01-15,1701,4.1
2,2017-01-22,1701,1
2,2017-02-01,1702,8.9
2,2017-02-08,1702,1.5
2,2017-02-15,1702,3
2,2017-02-27,1702,7.2
2,2017-02-28,1702,4
2,2017-03-02,1703,1
2,2017-03-07,1703,5.2
2,2017-03-11,1703,2
2,2017-03-21,1703,1
2,2017-03-28,1703,2
2,2017-04-01,1704,2.4
2,2017-04-05,1704,3.5
2,2017-04-11,1704,1
2,2017-04-19,1704,4.1
2,2017-04-23,1704,3
2,2017-05-02,1705,1.2
2,2017-05-03,1705,4.5
2,2017-05-04,1705,2
2,2017-05-10,1705,6.1
2,2017-05-21,1705,9", header=TRUE)

【讨论】:

    【解决方案2】:

    您可以创建一个函数来拆分数据,使用您的Yr_Mo 列为子集创建上限和下限,然后只需获取子集范围的sd() 值。其中df是您上面提供的数据集,首先重新排列数据集(不需要,但更容易检查输出是否正确)

    抱歉,完全错过了您也想保留NO 分组。这应该可以解决问题(df 这是您在上面提供的示例数据):

    此函数遍历每个唯一的 Yr_Mo 值以生成范围的上限和下限(在本例中为 x - 1 : x + 1)。然后它根据这些边界对提供的数据帧进行子集化,并为NUM 计算sd。如果子集无效(时间范围内可用的时间少于三个月),则输出为NA

    roll_sd <- function(df_, lead = 1, lag = -1) {
      id_sd <- do.call(rbind, lapply(unique(df_$Yr_Mo), function(x) {
        start = x + lag
        end = x + lead
        group = df_[df_$Yr_Mo >= start & df_$Yr_Mo <= end,]
        group_sd = sd(group$NUM)
        group_sd = ifelse(length(unique(group$Yr_Mo)) < 3, NA, sd(group$NUM))
        out = data.frame(central_value = x, group_sd)
      })
      )
    }
    

    然后,使用group_by 将此函数应用于NO 的每个分组:

    library(dplyr)
    
    df2 <- df %>% 
      group_by(NO) %>%
      do(roll_sd(data.frame(.)))
    
    > as.data.frame(df2)
       NO central_value  group_sd
    1   1          1701        NA
    2   1          1702  2.248449
    3   1          1703  2.209460
    4   1          1704  2.179406
    5   1          1705        NA
    6   2          1701        NA
    7   2          1702 13.046809
    8   2          1703  2.311833
    9   2          1704  2.270305
    10  2          1705        NA
    

    central_value 列是滑动窗口的“中间”月份值。

    【讨论】:

      猜你喜欢
      • 2021-10-13
      • 2021-06-19
      • 2020-02-07
      • 2015-10-31
      • 2023-04-01
      • 2021-12-09
      • 2021-08-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多