【问题标题】:Vectorised way to calculate mean of left and right neighbours in a vector计算向量中左右邻居平均值的向量化方法
【发布时间】:2019-08-05 21:02:27
【问题描述】:

我有一个向量

x = c(1820.0, 2235.0, 2534.0, 2580.0, 2322.0, 2317.0, 2331.0, 2345.0, 
      2305.0, 2265.0, 2277.0, 2289.0, 2338.0, 2387.0, 2152.0, 2256.0, 
      2360.0, 2590.0, 2529.0, 2468.0, 2776.0, 2909.0, 3017.0, 3081.0,
      3118.5, 3156.0, 3338.0, 3211.5)

我想计算除边之外的每个元素的左右邻居的平均值。例如,结果应该是这样的:mean(1820,2534), mean(2235,2580), mean(2534,2322) ...

我可以使用循环来做到这一点,但这很慢。我需要一个矢量化的解决方案。

我的代码使用了 for 循环:

neighbour_m = function(x) {
  newx = c(x[length(x)], x, x[1])
  for (i in 2:(length(newx) - 1)){
    m = mean(c(newx[i-1], newx[i+1]))
  }
}

【问题讨论】:

标签: r


【解决方案1】:

在基础 R 中,您可以使用过滤器:

stats::filter(x, c(1/2, 0, 1/2), sides = 2)

然后使用na.omit 删除NA

【讨论】:

  • 非常整洁。我喜欢使用stats::filter。 +1
  • @Clemsang,我们可以像mean一样使用filter函数来计算sd吗
  • @gi.rajan 您可以将 if 用于任何基于总和的事物。这包括方差/标准差。
【解决方案2】:

另一个使用 rowMeanscbind 的基本 R 选项

rowMeans(cbind(x[1:(length(x) - 2)], x[3:(length(x))]))
# [1] 2177.00 2407.50 2428.00 2448.50 2326.50 2331.00 2318.00 2305.00 2291.00
#[10] 2277.00 2307.50 2338.00 2245.00 2321.50 2256.00 2423.00 2444.50 2529.00
#[19] 2652.50 2688.50 2896.50 2995.00 3067.75 3118.50 3228.25 3183.75

【讨论】:

  • 我使用包microbenchmark的基准测试说这个答案是最快的。
【解决方案3】:

使用来自 dplyr 的lag/lead

library("dplyr")
na.omit(rowMeans(data.frame(lag(x, 1), lead(x, 1))))
#  [1] 2177 2408 2428 2448 2326 2331 2318 2305 2291 2277 2308 2338 2245 2322 2256
# [16] 2423 2444 2529 2652 2688 2896 2995 3068 3118 3228 3184
# attr(,"na.action")
# [1]  1 28
# attr(,"class")
# [1] "omit"

【讨论】:

  • 想知道为什么你没有小数?
  • options("digits") 设置为 4
【解决方案4】:

应该有一些更简单的解决方案,但这是使用zoo::rollapply 的一种方法。我们为向量的长度创建一系列奇数和偶数索引,子集 x 用于滚动 2 个点,并取其中的 mean

a1 <- zoo::rollapply(seq(2, length(x), by = 2), 2, function(i) mean(x[i]))
a2 <- zoo::rollapply(seq(1, length(x), by = 2), 2, function(i) mean(x[i]))
c(rbind(a1, a2))

#[1] 2407.50 2177.00 2448.50 2428.00 2331.00 2326.50 2305.00 2318.00 2277.00 
#    2291.00 2338.00 2307.50 2321.50 2245.00 2423.00 2256.00 2529.00 2444.50 
#    2688.50 2652.50 2995.00 2896.50 3118.50 3067.75 3183.75 3228.25

【讨论】:

    【解决方案5】:

    使用headtail,然后rowMeans

    rowMeans(cbind(head(x, -2), tail(x, -2)))
    #  [1] 2177.00 2407.50 2428.00 2448.50 2326.50 2331.00 2318.00 2305.00
    #  [9] 2291.00 2277.00 2307.50 2338.00 2245.00 2321.50 2256.00 2423.00
    # [17] 2444.50 2529.00 2652.50 2688.50 2896.50 2995.00 3067.75 3118.50
    # [25] 3228.25 3183.75
    

    同样:

    colMeans(rbind(head(x, -2), tail(x, -2)))
    

    【讨论】:

      【解决方案6】:

      1) rollapply 这表示将指示的偏移量(-1 = 先前值,+1 = 下一个值)传递给函数 mean,并将其作为 @ 的滚动应用程序运行987654322@。这确实需要一个包,但另一方面它很简洁,不需要任何索引操作。它还允许灵活处理边缘,因为如果我们想要返回与输入长度相同的向量,我们可以添加参数fill = NApartial = TRUE 以填充 NA 值或在边缘执行部分计算。

      library(zoo)
      rollapply(x, list(c(-1, 1)), mean)
      

      给予:

       [1] 2177.00 2407.50 2428.00 2448.50 2326.50 2331.00 2318.00 2305.00 2291.00
      [10] 2277.00 2307.50 2338.00 2245.00 2321.50 2256.00 2423.00 2444.50 2529.00
      [19] 2652.50 2688.50 2896.50 2995.00 3067.75 3118.50 3228.25 3183.75
      

      2) rollsum 另一种方法是使用 rollsum,然后减去当前值并除以 2。如果您希望输出与具有 NA 的 x 的长度相同,请删除 na.omit最后。

      library(zoo)
      na.omit(rollsum(x / 2, 3, fill = NA) - x / 2)
      

      给予:

       [1] 2177.00 2407.50 2428.00 2448.50 2326.50 2331.00 2318.00 2305.00 2291.00
      [10] 2277.00 2307.50 2338.00 2245.00 2321.50 2256.00 2423.00 2444.50 2529.00
      [19] 2652.50 2688.50 2896.50 2995.00 3067.75 3118.50 3228.25 3183.75
      attr(,"na.action")
      [1]  1 28
      attr(,"class")
      [1] "omit"
      

      3) 矩阵算子 该操作是线性的,因此可以用矩阵来表示。特别是,我们可以将x 乘以一个矩阵,该矩阵的上对角线和下对角线为 0.5,其他地方为零。

      d <- diag(length(x))
      y <- ((abs(row(d) - col(d)) == 1) / 2) %*% x
      

      这会在端点处进行部分评估,因此如果您不希望这样做,则将它们 NA 取出 y[c(1, length(y))] &lt;- NA 或删除端点 y &lt;- y[-c(1, length(y))]

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-05-16
        • 2014-06-26
        • 1970-01-01
        • 1970-01-01
        • 2017-09-23
        相关资源
        最近更新 更多