基于 R 中的交替列,在数据帧中滑动 window 平均值

Sliding window of mean across dataframe, based on alternate column in R

我希望使用滑动 window 方法计算变量的平均值,但基于数据框中另一列的值。很难解释所以...

以这个示例数据框为例:

dist <- c(seq(1,100,by=1),seq(101,200,by=2))
value<- runif(150, min=0, max=10)

df <- as.data.frame(cbind(dist,value))
head(df)

我知道我可以用下面的代码计算滑动 window 均值:

zoo::rollapply(df$value, width=50, by=25, FUN=mean, na.rm=TRUE,align="left")

然而,这并不是我想要的。我想计算 mean(df$value)df$dist 范围从 1-50 然后 25-75 然后 50-100 等等。

上面没有这样做,因为在我的数据集中我不能假设 df$dist 系统地计数(即不会错过随机数)。因此,简单地应用 window 向下移动一定数量的行将产生不正确的结果。

任何关于我将如何处理这个问题的建议都很棒。

提前致谢。

这里有几种选择。 (1) 没有包依赖性,(2) 使用 rollapply,因此与问题中的代码最相似,(3) 使用 SQL 并且代码最短。

1) Base R 如果问题是 dist 不包含 1 和它的最大值之间的每个数字,那么我们可以像这样迭代间隔:

Fun <- function(st, width, df, fun) {
  fun(subset(df, dist >= st & dist <= st + width - 1)$value)
}

width <- 50
step <- 25

starts <- seq(1, max(df$dist), step)

data.frame(starts, 
   ends = starts + width - 1,
   mean = sapply(starts, Fun, width, df, mean),
   N = sapply(starts, Fun, width, df, length))

给予:

  starts ends     mean  N
1      1   50 5.200910 50
2     26   75 4.710030 50
3     51  100 4.770270 50
4     76  125 4.880030 38
5    101  150 5.318415 25
6    126  175 5.575938 25
7    151  200 4.989383 25
8    176  225 3.918574 12

2) rollapply 另一种方法是扩展输入数据框,在这种情况下我们可以使用 rollapply。

library(zoo)

roll <- function(x, width, fun, step) {
  fun2 <- function(x) fun(na.omit(x))
  rollapply(x, width, by = step, fun2, partial = TRUE, align = "left")
}

width <- 50
step <- 25

m <- merge(df, data.frame(dist = 1:max(df$dist)), all = TRUE)
data.frame(starts, 
  ends = starts + width - 1,
  mean = roll(m$value, width, mean, step),
  N = roll(m$value, width, length, step)
)

给予:

  starts ends     mean  N
1      1   50 5.200910 50
2     26   75 4.710030 50
3     51  100 4.770270 50
4     76  125 4.880030 38
5    101  150 5.318415 25
6    126  175 5.575938 25
7    151  200 4.989383 25
8    176  225 3.918574 12

3) sqldf 这可以使用带有指定左连接的 SQL 紧凑地制定。

library(sqldf)

width <- 50
step <- 25

starts <- data.frame(starts = seq(1, max(df$dist), step))
fn$sqldf("select starts, starts+$width-1 ends, avg(value) mean, count(value) N
  from starts
  left join df on dist between starts and ends
  group by starts.rowid")

给予:

  starts ends     mean  N
1      1   50 5.200910 50
2     26   75 4.710030 50
3     51  100 4.770270 50
4     76  125 4.880030 38
5    101  150 5.318415 25
6    126  175 5.575938 25
7    151  200 4.989383 25
8    176  225 3.918574 12

备注

为了使输入可重现,我们必须在使用任何随机数之前设置种子,所以在上面我们使用了这个:

set.seed(123)
dist <- c(seq(1, 100, by = 1), seq(101, 200, by = 2))
value <- runif(150, min = 0, max = 10)
df <- data.frame(dist, value)