#r #dataframe #mean #sliding-window
Вопрос:
Я надеюсь вычислить среднее значение переменной, используя скользящее окно, но на основе значений другого столбца в кадре данных. Это трудно объяснить так…
Возьмем этот пример фрейма данных:
dist <- c(seq(1,100,by=1),seq(101,200,by=2))
value<- runif(150, min=0, max=10)
df <- as.data.frame(cbind(dist,value))
head(df)
Я понимаю, что могу рассчитать среднее значение скользящего окна с помощью приведенного ниже кода:
zoo::rollapply(df$value, width=50, by=25, FUN=mean, na.rm=TRUE,align="left")
Однако это не совсем то, чего я хочу. Я хотел бы рассчитать mean(df$value)
df$dist
диапазоны «когда 1-50
25-75
«, «тогда», «тогда 50-100
» и так далее.
Вышеизложенное не делает этого, так как в моем наборе данных я не могу предположить, что df$dist
подсчитывает систематически (т. Е. не пропускает случайные числа). Следовательно, простое применение окна, перемещающегося вниз на определенное количество строк, приведет к неправильному результату.
Любой совет о том, как бы я подошел к этому, был бы фантастическим.
Заранее спасибо.
Ответ №1:
Вот несколько альтернатив. (1) не имеет зависимостей от пакетов, (2) использует rollapply и поэтому наиболее похож на код в вопросе и (3) использует SQL и является самым коротким с точки зрения кода.
1) База R Если проблема в том, что dist не содержит каждое число от 1 до его максимального значения, то мы можем перебирать интервалы следующим образом:
Fun <- function(st, width, df, fun) {
fun(subset(df, dist >= st amp; 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)
Комментарии:
1. Большое вам спасибо @G. Гротендик, это похоже именно на то, что мне было нужно. Есть ли способ также включить
na.rm=T
вmean
функцию? Глядя на этот сценарий, это также будет работать для нескольких наблюдений на каждом расстоянии? Еще раз спасибо2. Также извините, что забыл
set.seed()
3. Первоначально я использовал na.rm = TRUE, но потом мне показалось, что, поскольку мы делали это как для среднего, так и для длины, имело больше смысла учитывать это. Вы могли бы указать среднее значение функции(x) (x, na.rm = TRUE) и длину функции(x) (na.опустить(x)) для отката, а затем использовать ее напрямую, а не определять fun2, но это расширило бы код.
4. Хорошо, большое вам спасибо за ваш очень исчерпывающий и полезный ответ. Поддержано и принято — хорошего дня!