我试着用R来计算矩阵中一系列值的移动平均值。R中似乎没有一个内置函数可以让我计算移动平均线。有任何软件包提供这样的服务吗?还是需要我自己写?
当前回答
我使用聚合和一个由rep()创建的向量。这样做的好处是可以使用cbind()一次在数据帧中聚合1个以上的列。下面是一个长度为1000的向量(v)的移动平均值为60的例子:
v=1:1000*0.002+rnorm(1000)
mrng=rep(1:round(length(v)/60+0.5), length.out=length(v), each=60)
aggregate(v~mrng, FUN=mean, na.rm=T)
请注意,rep中的第一个参数只是根据向量的长度和要平均的数量,为移动范围获取足够的唯一值;第二个参数保持长度等于向量长度,最后一个参数重复第一个参数的值的次数与平均周期相同。
总的来说,你可以使用几个函数(中值,最大值,最小值)-例如所示的平均值。同样,could可以使用cbind公式对数据帧中的多个(或所有)列执行此操作。
其他回答
您可以使用RcppRoll来实现用c++编写的快速移动平均线。只需调用roll_mean函数。文档可以在这里找到。
否则,这个(较慢的)for循环应该可以做到:
ma <- function(arr, n=15){
res = arr
for(i in n:length(arr)){
res[i] = mean(arr[(i-n):i])
}
res
}
caTools包具有非常快速的滚动mean/min/max/sd和其他一些功能。我只使用过runmean和runsd,它们是迄今为止提到的其他包中最快的。
或者你可以简单地计算它使用过滤器,这是我使用的函数:
ma <- function(x, n = 5){filter(x, rep(1 / n, n), sides = 2)}
如果使用dplyr,请注意在上面的函数中指定stats::filter。
使用费用应充分、有效。假设你有一个向量x,你想要n个数的和
cx <- c(0,cumsum(x))
rsum <- (cx[(n+1):length(cx)] - cx[1:(length(cx) - n)]) / n
正如@mzuther在评论中指出的那样,这假设数据中没有NAs。要处理这些问题,需要将每个窗口除以非na值的数量。这里有一种方法,结合@里卡多·克鲁兹的评论:
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,那么将会有一个零误差的除法。
编辑:非常喜欢添加侧参数,例如,一个日期向量的过去7天的移动平均值(或总和,或…)。
对于那些只想自己计算的人来说,它无非是:
# x = vector with numeric data
# w = window length
y <- numeric(length = length(x))
for (i in seq_len(length(x))) {
ind <- c((i - floor(w / 2)):(i + floor(w / 2)))
ind <- ind[ind %in% seq_len(length(x))]
y[i] <- mean(x[ind])
}
y
但是让它独立于mean()会很有趣,所以你可以计算任何“移动”函数!
# our working horse:
moving_fn <- function(x, w, fun, ...) {
# x = vector with numeric data
# w = window length
# fun = function to apply
# side = side to take, (c)entre, (l)eft or (r)ight
# ... = parameters passed on to 'fun'
y <- numeric(length(x))
for (i in seq_len(length(x))) {
if (side %in% c("c", "centre", "center")) {
ind <- c((i - floor(w / 2)):(i + floor(w / 2)))
} else if (side %in% c("l", "left")) {
ind <- c((i - floor(w) + 1):i)
} else if (side %in% c("r", "right")) {
ind <- c(i:(i + floor(w) - 1))
} else {
stop("'side' must be one of 'centre', 'left', 'right'", call. = FALSE)
}
ind <- ind[ind %in% seq_len(length(x))]
y[i] <- fun(x[ind], ...)
}
y
}
# and now any variation you can think of!
moving_average <- function(x, w = 5, side = "centre", na.rm = FALSE) {
moving_fn(x = x, w = w, fun = mean, side = side, na.rm = na.rm)
}
moving_sum <- function(x, w = 5, side = "centre", na.rm = FALSE) {
moving_fn(x = x, w = w, fun = sum, side = side, na.rm = na.rm)
}
moving_maximum <- function(x, w = 5, side = "centre", na.rm = FALSE) {
moving_fn(x = x, w = w, fun = max, side = side, na.rm = na.rm)
}
moving_median <- function(x, w = 5, side = "centre", na.rm = FALSE) {
moving_fn(x = x, w = w, fun = median, side = side, na.rm = na.rm)
}
moving_Q1 <- function(x, w = 5, side = "centre", na.rm = FALSE) {
moving_fn(x = x, w = w, fun = quantile, side = side, na.rm = na.rm, 0.25)
}
moving_Q3 <- function(x, w = 5, side = "centre", na.rm = FALSE) {
moving_fn(x = x, w = w, fun = quantile, side = side, na.rm = na.rm, 0.75)
}