【问题标题】:Function or loop to subset moving average in R函数或循环到 R 中的子集移动平均值
【发布时间】:2015-05-29 20:14:44
【问题描述】:

我不熟悉 R 中的循环和函数。

假设我有四个样本(A、B、C、D)的每 0.1 个单位的测量值,从 1.0 到 3.5。

我想在 1.5、2.5 和 3.5 附近找到平均测量值(+/- 0.2 个单位)。因此,对于 1.5,我将 c(1.3、1.4、1.5、1.6 和 1.7) 等处的值取平均值。

如何编写语句来总结所有四个样本的这三个平均值?我认为它可能会像这样开始:

X <- (1.5, 2.5, 3.5)

for (i in X)

{
  avg <- colMeans(subset(data,data$measurement > (i - 0.2) & data$measurement < (i + 0.2)))   
}

我也考虑过改用'['

colMeans(data[data$measurement > (i-0.2) & data$measurement < (i+0.2)]) 

到目前为止,感谢您的帮助,sqldf 是一个非常好的工具,该示例正是我想要的!

但是,我无法让它与真实数据集一起使用。我修改了代码,使其看起来像(对不起,这与示例数据集不再对应):

M <- sqldf("select r.i,avg(w.X1),avg(w.X2),avg(w.X3),avg(w.X4)
           from Y r, Y w
           where w.i betreen r.i - 1 and r.i + 1
           group by r.i
           having r.i+0.0 in (600, 700, 800)")

为了将其上下文化,我试图总结 599–601、699–701 和 799–801 中所有点的平均值,这四个列分别命名为 X1、X2、X3、X4。我将此数据框命名为“Y”。这些行实际上是波长,数据指向在该波长反射的光量。

你发现上面的代码有什么问题吗? -- 它创建了一个具有正确维度的矩阵,但平均值与更大数据集中的平均值不匹配。我想知道我是否不理解代码中的某些内容,例如,'w' 变量的重要性。

【问题讨论】:

  • 使用cut创建分组变量,然后使用tapply(vals, group, mean)会容易很多,那么1.8、1.9、2.0、2.1、和2.2.你真的应该发布一个示例数据集。

标签: r apply subset moving-average


【解决方案1】:

正确的索引比循环更快。

library(zoo)
set.seed(1)
x <- as.character(seq(1,3.5,.1)) 
z <- zoo(data.frame(a=rnorm(length(x)),
                    b=rnorm(length(x)),
                    c=rnorm(length(x))),
         x)
z2 <- rollmean(z, k = 5, align = "center")[as.character(seq(1,3.5,.5)),]
> z2
              a           b          c
1.5  0.46601479  0.40153999  0.2007418
2    0.31015536 -0.22912642  0.4673692
2.5 -0.04141133  0.31978341  0.4350507
3    0.63816023 -0.07509644 -0.3622883

> data.frame(z2, index = index(z2))
              a           b          c index
1.5  0.46601479  0.40153999  0.2007418   1.5
2    0.31015536 -0.22912642  0.4673692     2
2.5 -0.04141133  0.31978341  0.4350507   2.5
3    0.63816023 -0.07509644 -0.3622883     3

如果您希望在窗口宽度小于 5 的边缘上进行部分填充:

> rollapply(z, width = 5, align = "center", partial = TRUE, FUN = mean)[as.character(seq(1,3.5,.5)),]
              a           b           c
1   -0.42614637 -0.70156598  0.21492677
1.5  0.46601479  0.40153999  0.20074176 
2    0.31015536 -0.22912642  0.46736921 
2.5 -0.04141133  0.31978341  0.43505071
3    0.63816023 -0.07509644 -0.36228832
3.5 -0.47521823  0.22239574 -0.05024676

如果窗口大小不规则,但如评论中提到的那样等间距:

> z2 <- as.data.frame(z)
> z2$i <- row.names(z2)
> library(sqldf)
> sqldf("select a.i,avg(b.a),avg(b.b),avg(b.c) 
         from z2 a, z2 b 
         where b.i between a.i - .21 and a.i + .21 
         group by a.i 
         having a.i+0.0 in (1.5,2.0,2.5,3.0,3.5)")
    i    avg(b.a)    avg(b.b)    avg(b.c)
1 1.5  0.46601479  0.40153999  0.20074176
2   2  0.31015536 -0.22912642  0.46736921
3 2.5 -0.04141133  0.31978341  0.43505071
4   3  0.63816023 -0.07509644 -0.36228832
5 3.5 -0.47521823  0.22239574 -0.05024676

【讨论】:

  • 谢谢,这很有帮助。
  • 我想知道,如果您测量的值不那么系统化并且宽度略有变化怎么办?例如,如果行不是每 0.1 个单位,而是更像:1.02, 1.11, 1.25, 1.31, 1.39, 1.52, 1.66, 1.73, 1.79, 1.80, 1.92, ... 如果数字不那么系统,在相同范围内(如 1.3-1.7)取平均值,有时值会稍多或少一些。在这个例子中,如果你想估计 1.5;该值并不完全在集合中,而 1.52 是最接近的值。有什么想法吗?
  • 在这种情况下使用 SQL 会更容易。请参阅添加的示例。
  • 感谢使用 sqldf 的好例子!您的示例完美运行,但是,当我将其应用于我自己的真实数据时,结果没有意义。您在我添加到原始问题的代码中看到任何问题吗?也许我没有正确理解命令。
  • 可能是数值精度。尝试 r.i +/- 1.01 而不是 +/- 1 以确保您正在捕获所有行。如果不是,它可以用几行实际数据执行 dput(head(Y))。
猜你喜欢
  • 2018-01-05
  • 1970-01-01
  • 1970-01-01
  • 2021-12-12
  • 1970-01-01
  • 1970-01-01
  • 2018-07-07
  • 2021-04-12
  • 1970-01-01
相关资源
最近更新 更多