【问题标题】:Sliding window of mean across dataframe, based on alternate column in R基于 R 中的备用列,跨数据帧的均值滑动窗口
【发布时间】:2021-08-31 14:33:20
【问题描述】:

我希望使用滑动窗口方法计算变量的平均值,但要基于数据框中另一列的值。这很难解释……

以这个示例数据框为例:

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)

我知道我可以使用以下代码计算滑动窗口平均值:

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

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

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

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

提前谢谢你。

【问题讨论】:

    标签: r dataframe mean sliding-window


    【解决方案1】:

    这里有几种选择。 (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)
    

    【讨论】:

    • 非常感谢@G。格洛腾迪克这看起来正是我所追求的。有没有办法在mean 函数中也包含na.rm=T?看看这个脚本,这也适用于每个距离的多个观测?再次感谢您
    • 也很抱歉忘记set.seed()
    • 我最初使用了 na.rm = TRUE 但后来似乎因为我们同时考虑了平均值和长度,因此将其分解出来更有意义。您可以提供一个 function(x) mean(x, na.rm = TRUE) 和 function(x) length(na.omit(x)) 来滚动然后直接使用它而不是定义 fun2 但这会扩展代码。
    • 好的,非常感谢您非常全面和有用的回答。赞成并接受 - 祝您有美好的一天!
    猜你喜欢
    • 2011-04-28
    • 2019-02-09
    • 1970-01-01
    • 2020-08-10
    • 2017-06-20
    • 2013-10-27
    • 2018-11-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多