กำลังคำนวณค่าเฉลี่ยเคลื่อนที่


185

ฉันพยายามใช้ R เพื่อคำนวณค่าเฉลี่ยเคลื่อนที่ของชุดค่าในเมทริกซ์ การค้นหารายชื่ออีเมล R ปกติไม่ได้ช่วยอะไรมาก ดูเหมือนจะไม่มีฟังก์ชั่นในตัวใน R ที่จะช่วยให้ฉันคำนวณค่าเฉลี่ยเคลื่อนที่ แพ็คเกจใดมีให้หรือไม่ หรือฉันจะต้องเขียนของตัวเอง?

คำตอบ:


140
  • หมายถึงการหมุน / สูงสุด / Medians ในแพคเกจสวนสัตว์ (rollmean)
  • ค่าเฉลี่ยเคลื่อนที่เป็นTTR
  • ในการพยากรณ์

1
ค่าเฉลี่ยเคลื่อนที่ใน R ไม่ได้มีค่าในอนาคตของการประทับเวลาที่กำหนดคืออะไร? ฉันตรวจสอบforecast::maแล้วและมีพื้นที่ใกล้เคียงทั้งหมดไม่ถูกต้อง
hhh

213

หรือคุณสามารถคำนวณโดยใช้ตัวกรองนี่คือฟังก์ชันที่ฉันใช้:

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

หากคุณใช้dplyrโปรดระมัดระวังในการระบุstats::filterในฟังก์ชั่นด้านบน


49
ฉันควรชี้ให้เห็นว่า "sides = 2" อาจเป็นตัวเลือกที่สำคัญในกรณีการใช้งานของคนจำนวนมากที่พวกเขาไม่ต้องการมองข้าม หากคุณต้องการเพียงข้อมูลต่อท้ายในค่าเฉลี่ยเคลื่อนที่ของคุณคุณควรใช้ sides = 1
evanrsparks

35
หลายปีต่อมา แต่ตอนนี้ dplyr มีฟังก์ชั่นตัวกรองถ้าคุณใช้แพ็คเกจนี้โหลดstats::filter
blmoore

sides = 2เทียบเท่ากับ align = "center" สำหรับ zoo :: rollmean หรือ RcppRoll :: roll_mean sides = 1เทียบเท่ากับการจัดตำแหน่ง "ขวา" ฉันไม่เห็นวิธีการจัดตำแหน่ง "ซ้าย" หรือคำนวณด้วยข้อมูล "บางส่วน" (2 ค่าขึ้นไป) หรือไม่
Matt L.

29

การใช้cumsumควรเพียงพอและมีประสิทธิภาพ สมมติว่าคุณมีเวกเตอร์xและคุณต้องการหาผลรวมของตัวเลขn

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

ดังที่ระบุไว้ในความคิดเห็นโดย @mzuther สิ่งนี้จะถือว่าไม่มีข้อมูล NAs เพื่อจัดการกับสิ่งเหล่านั้นจะต้องมีการหารแต่ละหน้าต่างด้วยจำนวนของค่า non-NA นี่เป็นวิธีหนึ่งในการทำเช่นนั้นโดยรวมความคิดเห็นจาก @Ricardo Cruz:

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

นี่ยังคงมีปัญหาว่าถ้าค่าทั้งหมดในหน้าต่างเป็น NAs จะมีการหารด้วยศูนย์ข้อผิดพลาด


8
ข้อเสียอย่างหนึ่งของวิธีนี้คือมันไม่สามารถรับมือกับการพลาดได้:cumsum(c(1:3,NA,1:3))
Jthorpe

คุณสามารถทำให้จัดการกับ NA ได้โดยcx <- c(0, cumsum(ifelse(is.na(x), 0, x)))ง่าย
Ricardo Cruz

@Ricardo Cruz: อาจเป็นการดีกว่าที่จะลบ NAs และปรับความยาวเวคเตอร์ตามลำดับ นึกถึงเวกเตอร์ที่มี NA จำนวนมาก - ศูนย์จะดึงค่าเฉลี่ยไปที่ศูนย์ในขณะที่การลบ NAs จะทำให้ค่าเฉลี่ยเหมือนเดิม ทุกอย่างขึ้นอยู่กับข้อมูลและคำถามที่คุณต้องการตอบ :)
mzuther

@mzuther ฉันอัปเดตคำตอบตามความคิดเห็นของคุณ ขอบคุณสำหรับการป้อนข้อมูล ฉันคิดว่าวิธีที่ถูกต้องในการจัดการกับข้อมูลที่หายไปนั้นไม่ได้เป็นการขยายหน้าต่าง (โดยลบค่า NA) แต่โดยเฉลี่ยแต่ละหน้าต่างโดยตัวส่วนที่ถูกต้อง
pipefish

1
rn <- cn [(n + 1): length (cx)] - cx [1: (ความยาว (cx) - n)] ที่จริงควรเป็น rn <- cn [(n + 1): length (cx)] - cn [1: (ความยาว (cx) - n)]
adrianmcmenamin

22

ในdata.table 1.12.0ใหม่frollmeanฟังก์ชั่นได้รับการเพิ่มในการคำนวณอย่างรวดเร็วและแน่นอนกลิ้งเฉลี่ยอย่างรอบคอบจัดการNA, NaNและ+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แพคเกจได้อย่างรวดเร็วกลิ้งเฉลี่ย / min max / / SD และไม่กี่ฟังก์ชั่นอื่น ๆ ฉันเคยทำงานด้วยrunmeanและrunsdพวกเขาก็เร็วที่สุดของแพ็คเกจอื่น ๆ ที่กล่าวถึงจนถึงปัจจุบัน


1
นี่มันเจ๋งมาก! มันเป็นฟังก์ชั่นเดียวที่ทำสิ่งนี้ในวิธีที่ดีและเรียบง่าย และตอนนี้ก็เป็นปี 2018 ...
เฟลิเป้เจอราร์ด

9

คุณสามารถใช้RcppRollค่าเฉลี่ยเคลื่อนที่อย่างรวดเร็วเขียนใน C ++ เพียงแค่เรียกroll_meanฟังก์ชั่น เอกสารที่สามารถพบได้ที่นี่

มิฉะนั้นสิ่งนี้ (ช้ากว่า) สำหรับการวนซ้ำควรทำเคล็ดลับ:

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]ตอนนี้ฉันชอบตั้งค่าres = rep(NA, length(arr))มากกว่าres = arrดังนั้นแต่ละองค์ประกอบของres[1:14]เท่ากับ NA แทนที่จะเป็นตัวเลขซึ่งเราไม่สามารถหาค่าเฉลี่ย 15 องค์ประกอบได้ทั้งหมด
Evan Friedland

7

ในความRcppRollเป็นจริงดีมาก

รหัสที่โพสต์โดยcantdutch นี้จะต้องได้รับการแก้ไขในบรรทัดที่สี่ไปยังหน้าต่างได้รับการแก้ไข:

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

อีกวิธีหนึ่งซึ่งจัดการ missings จะได้รับที่นี่

วิธีที่สามคือการปรับปรุงรหัสcantdutch นี้เพื่อคำนวณค่าเฉลี่ยบางส่วนหรือไม่ดังต่อไปนี้:

  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

เพื่อเสริมคำตอบของ cantdutchthisและRodrigo 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

นี่คือตัวอย่างรหัสที่แสดงวิธีการคำนวณค่าเฉลี่ยเคลื่อนที่แบบกึ่งกลางและค่าเฉลี่ยเคลื่อนที่แบบต่อเนื่องโดยใช้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 = mean คือฟังก์ชัน นอกจากนี้คุณยังสามารถเปลี่ยนเป็น 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. Grothendieck

1

หนึ่งสามารถใช้runnerแพคเกจสำหรับฟังก์ชั่นการเคลื่อนย้าย ในกรณีนี้mean_runฟังก์ชั่น ปัญหาcummeanคือมันไม่ได้จัดการกับNAค่า แต่เป็นmean_runเช่นนั้น runnerแพคเกจยังรองรับอนุกรมเวลาที่ผิดปกติและหน้าต่างสามารถขึ้นอยู่กับวันที่:

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

หนึ่งยังสามารถระบุตัวเลือกอื่น ๆ เช่นlagและม้วนatดัชนีเฉพาะเท่านั้น เพิ่มเติมในเอกสารแพคเกจและฟังก์ชั่น


1

แพคเกจตัวเลื่อนสามารถใช้สำหรับสิ่งนี้ มันมีส่วนต่อประสานที่ได้รับการออกแบบมาโดยเฉพาะให้ความรู้สึกคล้ายกับเสียงฟี้อย่างแมว มันยอมรับฟังก์ชั่นโดยพลการใด ๆ และสามารถส่งกลับประเภทใด ๆ ของการส่งออก Data data นั้นวนซ้ำกว่าแถวอย่างชาญฉลาด เว็บไซต์ 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()ควรจะค่อนข้างต่ำ (เร็วกว่าสวนสัตว์) 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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.