Окно zoo::rollapply со значениями столбцов, а не строк

dat = structure(list(index = c(10505L, 10506L, 10511L, 10539L, 10542L, 
10579L, 10642L, 11008L, 11012L, 13011L, 13110L, 13116L, 13118L, 
13156L, 13259L, 13273L, 13313L, 13365L, 13380L, 13382L, 13445L, 
13453L, 13482L, 13483L, 13494L, 13543L, 13550L, 14462L, 14464L, 
14564L, 14599L, 14604L, 14674L, 14719L, 14728L, 14775L, 14860L, 
14874L, 14930L, 14933L, 14975L, 15031L, 15089L, 15117L, 15179L, 
15211L, 15241L, 15245L, 15255L, 15260L, 15418L, 15585L, 15627L, 
15644L, 15774L, 15776L, 15777L, 15790L, 15791L, 15833L, 15849L, 
15850L, 15886L, 16042L, 16127L, 16140L, 16141L, 16142L, 16365L, 
16485L, 16489L, 16515L, 16542L, 16738L, 16834L, 16949L, 17272L, 
17462L, 17569L, 17571L, 17641L, 17654L, 17694L, 17695L, 17709L, 
17748L, 17836L, 17922L, 18643L, 20113L, 20131L, 28914L, 29318L, 
30524L, 30741L, 30912L, 30923L, 30998L, 46650L, 46698L), V2 = c(3L, 
3L, 3L, 2L, 2L, 2L, 2L, 1L, 0L, 3L, 2L, 2L, 2L, 0L, 1L, 1L, 0L, 
0L, 0L, 0L, 1L, 1L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 1L, 1L, 0L, 
0L, 0L, 1L, 2L, 2L, 2L, 2L, 1L, 0L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 
2L, 1L, 0L, 0L, 1L, 0L, 0L, 0L, 0L, 0L, 0L, 1L, 1L, 1L, 0L, 0L, 
0L, 0L, 0L, 2L, 3L, 5L, 3L, 0L, 0L, 3L, 1L, 0L, 3L, 0L, 0L, 2L, 
1L, 0L, 0L, 0L, 0L, 1L, 1L, 1L, 2L, 1L, 0L, 0L, 1L, 0L, 0L, 0L, 
1L, 1L, 1L)), row.names = c(NA, -100L), class = "data.frame")

Допустим, я хочу вычислить функцию через dat в скользящем окне.

n_sites = function(x) {
    return(sum(x > 1))
}
zoo::rollapply(dat$V2, FUN=n_sites, width=100)

Однако вместо того, чтобы использовать количество строк в качестве размера окна, я хотел бы использовать фактические числовые значения в столбце index. Поэтому я хотел бы, чтобы каждое окно содержало примерно 100 единиц в столбце индекса. Учитывая, что между 1-й и 7-й строками примерно 100 единиц index, первое окно будет включать эти строки. Это возможно?

Рад за решение, использующее zoo или data.table или подобное.


person user438383    schedule 11.05.2021    source источник


Ответы (3)


Ширина в rollapply может быть вектором таким, что i-й элемент является шириной, используемой для i-й строки. Существует несколько интерпретаций вопроса. Мы могли бы использовать наибольшую ширину, охватывающую не более 100 единиц индекса, наименьшую ширину, по крайней мере, 100 единиц индекса или ширину, ближайшую к 100 единицам индекса. Вопрос, кажется, требует третьего, но ширина примера 7 не согласуется с этим и предполагает, что, возможно, требуется вторая интерпретация. Мы даем все три ширины в конце. Выбирайте, что хотите. Также в вопросе говорится, что первое окно равно 7, что указывает на то, что требуется выравнивание по левому краю.

library(zoo)

w <- w2 # see calcs of w1, w2 and w3 at end.  Use whichever you want.
transform(dat, roll = rollapplyr(V2, w, n_sites, fill = NA, align = "left"))

Если n_sites является просто заменой реальной функции, мы можем использовать приведенное выше, но если это реальная функция, мы можем исключить ее и написать так:

transform(dat, roll = rollapplyr(V2 > 1, w, sum, fill = NA, align = "left"))

Ширина

Возможны многие варианты этого, и мы вычисляем три упомянутых здесь.

В приведенном ниже коде используется findInterval базы R. Напомним, что findInterval(x, vec), где x и vec — векторы, а vec — неубывающее число, возвращает вектор той же длины, что и x, так что i-й компонент результата равен sum(x[i] ›= vec), но делает это более эффективно. То есть, если x[i] находится в vec, то он находит последнюю позицию в vec, которая равна x[i], а если x[i] не находится в vec, то он находит последнюю позицию в vec, которая меньше x[ я]. Обратите внимание, что он возвращает позиции, то есть индексы, а не значения vec. Например, findInterval(c(20, 30), c(10, 30, 30, 30, 40)) возвращает c(1, 4), поскольку 1 — это позиция наибольшего значения в vec меньше 20, а 4 — это позиция последнего значения в vec равна 30.

n <- nrow(dat)
index <- dat$index

# i1 is row number of last index no more than current index + 100
i1 <- findInterval(index + 100, index)
w1 <- i1 - 1:n + 1

# i2 is row number of first index at least equal to index + 100
i2 <- pmin(findInterval(index + 100 - 1, index) + 1, n)
w2 <- i2 - 1:n + 1
w2[1]
## [1] 7

# i is row number of index closest to current index + 100
i <- ifelse(index + 100 - index[i1] <= index[i2] - (index + 100), i1, i2)
w3 <- i - 1:n + 1
person G. Grothendieck    schedule 11.05.2021

Вы также можете использовать пакет runner, где аргумент idx — это именно то, что вам нужно.

dat$n_sites <- runner::runner(x = dat$V2,
                              idx = dat$index,
                              k = 100,
                              f = n_sites)

head(dat, 10)
   index V2 n_sites
1  10505  3       1
2  10506  3       2
3  10511  3       3
4  10539  2       4
5  10542  2       5
6  10579  2       6
7  10642  2       2
8  11008  1       0
9  11012  0       0
10 13011  3       1

person AnilGoyal    schedule 14.05.2021

Вы можете использовать slider::slide_index вместо zoo::rollapply:

library(slider)
dat$n_sites <- slider::slide_index(.x = dat$V2,
                                   .i = dat$index,
                                   .f = n_sites,
                                   .before = 100)

head(dat,10)
   index V2 n_sites
1  10505  3       1
2  10506  3       2
3  10511  3       3
4  10539  2       4
5  10542  2       5
6  10579  2       6
7  10642  2       3
8  11008  1       0
9  11012  0       0
10 13011  3       1
person Waldi    schedule 11.05.2021
comment
Почему вывод row7 равен 3? когда размер окна 100, а не 101? - person AnilGoyal; 14.05.2021
comment
@Anilgoyal, .before — это количество значений перед текущим индексом, поэтому 10642-100 уменьшается до 10542. В зависимости от того, что ожидает OP, значение аргумента может быть 99 - person Waldi; 14.05.2021
comment
Ok. Спасибо за разъяснение. :) - person AnilGoyal; 14.05.2021