移動平均の計算


185

Rを使用して、行列の一連の値の移動平均を計算しようとしています。通常のRメーリングリストの検索は、あまり役に立ちませんでした。Rには移動平均を計算できる組み込み関数がないようです。提供しているパッケージはありますか?それとも自分で書く必要がありますか?

回答:


140

1
特定のタイムスタンプの将来の値を含まないRの移動平均とは何ですか?私がチェックしたところforecast::ma、それは正しくないすべての近所が含まれています。
hhh 2018

214

または、フィルターを使用して単純に計算することもできます。これが私が使用する関数です。

ma <- function(x, n = 5){filter(x, rep(1 / n, n), sides = 2)}

を使用する場合は、上記の関数でのdplyr指定stats::filterに注意してください。


49
「sides = 2」は、見逃したくない多くの人々のユースケースで重要なオプションになる可能性があることを指摘しておきます。移動平均の末尾情報のみが必要な場合は、sides = 1を使用する必要があります。
evanrsparks 2012

36
数年後、dplyrにフィルター機能が追加されました(このパッケージをロードして使用する場合)stats::filter
blmoore

sides = 2zoo :: rollmeanまたはRcppRoll :: roll_meanのalign = "center"と同等です。sides = 1「右」の配置と同等です。「左」配置を行う方法、または「部分」データ(2つ以上の値)で計算する方法がわかりませんか?
マットL.

29

使用cumsumは十分かつ効率的でなければなりません。ベクトルxがあり、n個の数値の合計を実行したいとします。

cx <- c(0,cumsum(x))
rsum <- (cx[(n+1):length(cx)] - cx[1:(length(cx) - n)]) / n

@mzutherのコメントで指摘されているように、これはデータにNAがないことを前提としています。それらに対処するには、各ウィンドウを非NA値の数で除算する必要があります。@Ricardo Cruzからのコメントを組み込んだ方法の1つを次に示します。

cx <- c(0, cumsum(ifelse(is.na(x), 0, x)))
cn <- c(0, cumsum(ifelse(is.na(x), 0, 1)))
rx <- cx[(n+1):length(cx)] - cx[1:(length(cx) - n)]
rn <- cn[(n+1):length(cx)] - cn[1:(length(cx) - n)]
rsum <- rx / rn

これには、ウィンドウ内のすべての値がNAの場合、ゼロ除算エラーが発生するという問題があります。


8
この解決策の一つの欠点は、missingsを扱うことができないということである:cumsum(c(1:3,NA,1:3))
Jthorpe

NAを処理することで簡単にできますcx <- c(0, cumsum(ifelse(is.na(x), 0, x)))
Ricardo Cruz

@Ricardo Cruz:NAを削除し、それに応じてベクトルの長さを調整することをお勧めします。NAが多いベクトルを考えてみてください。ゼロは平均をゼロに近づけ、NAを削除すると平均はそのままになります。もちろん、それはすべてあなたのデータと答えたい質問に依存します。:)
mzuther 2018年

@mzuther、私はあなたのコメントに続いて答えを更新しました。入力いただきありがとうございます。欠損データを処理する正しい方法は、ウィンドウを拡張することではなく(NA値を削除することにより)、各ウィンドウを正しい分母で平均することです。
pipefish

1
rn <-cn [(n + 1):length(cx)]-cx [1:(length(cx)-n)]は実際にはrn <-cn [(n + 1):length(cx)]- cn [1:(length(cx)-n)]
adrianmcmenamin

22

data.table 1.12.0新しいfrollmean機能、高速かつ正確な平均転がり慎重に取り扱いを計算するために追加されているNANaN+Inf-Inf値。

質問には再現可能な例がないため、ここで対処することはこれ以上ありません。

詳細について?frollmeanはマニュアルをご覧ください。オンラインでもご覧いただけます。?frollmean。。

以下のマニュアルの例:

library(data.table)
d = as.data.table(list(1:6/2, 3:8/4))

# rollmean of single vector and single window
frollmean(d[, V1], 3)

# multiple columns at once
frollmean(d, 3)

# multiple windows at once
frollmean(d[, .(V1)], c(3, 4))

# multiple columns and multiple windows at once
frollmean(d, c(3, 4))

## three above are embarrassingly parallel using openmp

10

caToolsパッケージには、非常に高速平均/最小/最大/ SDといくつかの他の機能を転がりました。私だけで働いてきたrunmeanrunsd、彼らは、これまでに述べた他のパッケージのいずれかの最速です。


1
これはすごい!これは、素晴らしくシンプルな方法でこれを行う唯一の関数です。そして、それは今、2018年です...
フェリペジェラール

9

RcppRollC ++で書かれた非常に迅速な移動平均に使用できます。roll_mean関数を呼び出すだけです。ドキュメントはここにあります

それ以外の場合は、この(遅い)forループでうまくいくはずです。

ma <- function(arr, n=15){
  res = arr
  for(i in n:length(arr)){
    res[i] = mean(arr[(i-n):i])
  }
  res
}

3
このアルゴリズムはどのように機能しますか?アイデアが理解できないため
Daniel Yefimov

まず、と同じ長さのベクトルを初期化しres = arrます。次にn、15番目の要素から、または配列の最後まで反復するループがあります。それは、彼が平均をとる最初のサブセットは、arr[1:15]スポットを満たすことres[15]です。今では、15個の要素の完全平均をとることができなかった数値ではなく、NA に等しい各要素のres = rep(NA, length(arr))代わりに設定することを好みます。res = arrres[1:14]
エヴァンフリードランド2018

7

実際にRcppRollは非常に良いです。

cantdutchthisによって投稿されたコードは、ウィンドウの4行目で修正する必要があります。

ma <- function(arr, n=15){
  res = arr
  for(i in n:length(arr)){
    res[i] = mean(arr[(i-n+1):i])
  }
  res
}

欠落を処理する別の方法をここに示します

3番目の方法は、部分平均を計算するかどうかをcantdutchthisコードで改善する方法です。

  ma <- function(x, n=2,parcial=TRUE){
  res = x #set the first values

  if (parcial==TRUE){
    for(i in 1:length(x)){
      t<-max(i-n+1,1)
      res[i] = mean(x[t:i])
    }
    res

  }else{
    for(i in 1:length(x)){
      t<-max(i-n+1,1)
      res[i] = mean(x[t:i])
    }
    res[-c(seq(1,n-1,1))] #remove the n-1 first,i.e., res[c(-3,-4,...)]
  }
}

5

cantdutchthisRodrigo Remedioの答えを補足するために ;

moving_fun <- function(x, w, FUN, ...) {
  # x: a double vector
  # w: the length of the window, i.e., the section of the vector selected to apply FUN
  # FUN: a function that takes a vector and return a summarize value, e.g., mean, sum, etc.
  # Given a double type vector apply a FUN over a moving window from left to the right, 
  #    when a window boundary is not a legal section, i.e. lower_bound and i (upper bound) 
  #    are not contained in the length of the vector, return a NA_real_
  if (w < 1) {
    stop("The length of the window 'w' must be greater than 0")
  }
  output <- x
  for (i in 1:length(x)) {
     # plus 1 because the index is inclusive with the upper_bound 'i'
    lower_bound <- i - w + 1
    if (lower_bound < 1) {
      output[i] <- NA_real_
    } else {
      output[i] <- FUN(x[lower_bound:i, ...])
    }
  }
  output
}

# example
v <- seq(1:10)

# compute a MA(2)
moving_fun(v, 2, mean)

# compute moving sum of two periods
moving_fun(v, 2, sum)

2

これは、zooパッケージの関数を使用して、中央移動平均後続移動平均を計算する方法を示すサンプルコードです。rollmean

library(tidyverse)
library(zoo)

some_data = tibble(day = 1:10)
# cma = centered moving average
# tma = trailing moving average
some_data = some_data %>%
    mutate(cma = rollmean(day, k = 3, fill = NA)) %>%
    mutate(tma = rollmean(day, k = 3, fill = NA, align = "right"))
some_data
#> # A tibble: 10 x 3
#>      day   cma   tma
#>    <int> <dbl> <dbl>
#>  1     1    NA    NA
#>  2     2     2    NA
#>  3     3     3     2
#>  4     4     4     3
#>  5     5     5     4
#>  6     6     6     5
#>  7     7     7     6
#>  8     8     8     7
#>  9     9     9     8
#> 10    10    NA     9

1

少し遅いですが、zoo :: rollapplyを使用して行列の計算を実行することもできます。

reqd_ma <- rollapply(x, FUN = mean, width = n)

ここで、xはデータセット、FUN =平均は関数です。また、min、max、sdなどに変更することもでき、widthはローリングウィンドウです。


2
遅くありません;。ベースRと比較すると、はるかに高速です。 set.seed(123); x <- rnorm(1000); system.time(apply(embed(x, 5), 1, mean)); library(zoo); system.time(rollapply(x, 5, mean)) 私のマシンでは非常に高速なので、0秒の時間を返します。
G.グロタンディーク2018

1

runner機能を移動するためのパッケージを使用できます。この場合はmean_run機能します。問題cummeanは、NA値を処理しないが、処理することですmean_runrunnerパッケージは不規則な時系列もサポートし、ウィンドウは日付に依存する可能性があります。

library(runner)
set.seed(11)
x1 <- rnorm(15)
x2 <- sample(c(rep(NA,5), rnorm(15)), 15, replace = TRUE)
date <- Sys.Date() + cumsum(sample(1:3, 15, replace = TRUE))

mean_run(x1)
#>  [1] -0.5910311 -0.2822184 -0.6936633 -0.8609108 -0.4530308 -0.5332176
#>  [7] -0.2679571 -0.1563477 -0.1440561 -0.2300625 -0.2844599 -0.2897842
#> [13] -0.3858234 -0.3765192 -0.4280809

mean_run(x2, na_rm = TRUE)
#>  [1] -0.18760011 -0.09022066 -0.06543317  0.03906450 -0.12188853 -0.13873536
#>  [7] -0.13873536 -0.14571604 -0.12596067 -0.11116961 -0.09881996 -0.08871569
#> [13] -0.05194292 -0.04699909 -0.05704202

mean_run(x2, na_rm = FALSE )
#>  [1] -0.18760011 -0.09022066 -0.06543317  0.03906450 -0.12188853 -0.13873536
#>  [7]          NA          NA          NA          NA          NA          NA
#> [13]          NA          NA          NA

mean_run(x2, na_rm = TRUE, k = 4)
#>  [1] -0.18760011 -0.09022066 -0.06543317  0.03906450 -0.10546063 -0.16299272
#>  [7] -0.21203756 -0.39209010 -0.13274756 -0.05603811 -0.03894684  0.01103493
#> [13]  0.09609256  0.09738460  0.04740283

mean_run(x2, na_rm = TRUE, k = 4, idx = date)
#> [1] -0.187600111 -0.090220655 -0.004349696  0.168349653 -0.206571573 -0.494335093
#> [7] -0.222969541 -0.187600111 -0.087636571  0.009742884  0.009742884  0.012326968
#> [13]  0.182442234  0.125737145  0.059094786

のような他のオプションを指定してlagat特定のインデックスのみをロールすることもできます。パッケージ関数のドキュメントの詳細。


1

これには、スライダーパッケージを使用できます。それは、ゴロゴロと同じように感じるように特別に設計されたインターフェースを備えています。任意の関数を受け入れ、任意のタイプの出力を返すことができます。データフレームは行ごとに反復されます。pkgdownサイトはこちらです。

library(slider)

x <- 1:3

# Mean of the current value + 1 value before it
# returned as a double vector
slide_dbl(x, ~mean(.x, na.rm = TRUE), .before = 1)
#> [1] 1.0 1.5 2.5


df <- data.frame(x = x, y = x)

# Slide row wise over data frames
slide(df, ~.x, .before = 1)
#> [[1]]
#>   x y
#> 1 1 1
#> 
#> [[2]]
#>   x y
#> 1 1 1
#> 2 2 2
#> 
#> [[3]]
#>   x y
#> 1 2 2
#> 2 3 3

スライダーとdata.tableの両方のオーバーヘッドはfrollapply()かなり低くなければなりません(zooよりはるかに高速です)。frollapply()この単純な例では、ここでは少し高速に見えますが、数値の入力のみを受け取り、出力はスカラー数値でなければならないことに注意してください。スライダー関数は完全に汎用的であり、任意のデータ型を返すことができます。

library(slider)
library(zoo)
library(data.table)

x <- 1:50000 + 0L

bench::mark(
  slider = slide_int(x, function(x) 1L, .before = 5, .complete = TRUE),
  zoo = rollapplyr(x, FUN = function(x) 1L, width = 6, fill = NA),
  datatable = frollapply(x, n = 6, FUN = function(x) 1L),
  iterations = 200
)
#> # A tibble: 3 x 6
#>   expression      min   median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 slider      19.82ms   26.4ms     38.4    829.8KB     19.0
#> 2 zoo        177.92ms  211.1ms      4.71    17.9MB     24.8
#> 3 datatable    7.78ms   10.9ms     87.9    807.1KB     38.7

0
vector_avg <- function(x){
  sum_x = 0
  for(i in 1:length(x)){
    if(!is.na(x[i]))
      sum_x = sum_x + x[i]
  }
  return(sum_x/length(x))
}

2
詳細については、説明を追加してください。
Farbod Ahmadian

質問に対する回答を関連付け、質問が回答されたことを示す出力を含めてください。適切な回答を行うためのガイダンスについては、回答方法をご覧ください。
ピーター
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.