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