Как мне взять прокатный продукт, используя data.table
dt <- data.table(x=c(1, .9, .8, .75, .5, .1))
dt
x
1: 1.00
2: 0.90
3: 0.80
4: 0.75
5: 0.50
6: 0.10
Как получить произведение x для этой строки и следующих двух строк для каждой строки?
x Prod.3
1: 1.00 0.7200
2: 0.90 0.5400
3: 0.80 0.3000
4: 0.75 0.0375
5: 0.50 NA
6: 0.10 NA
В более общем смысле, для каждой строки, как получить произведение x для этой строки и следующих n строк?
3 ответа
Ты можешь попробовать
library(zoo)
rollapply(dt, 3, FUN = prod)
x
[1,] 0.7200
[2,] 0.5400
[3,] 0.3000
[4,] 0.0375
Чтобы соответствовать ожидаемому результату
dt[, Prod.3 :=rollapply(x, 3, FUN=prod, fill=NA, align='left')]
Вот еще одна возможная версия с использованием data.table::shift
в сочетании с Reduce
(согласно комментарию @Aruns)
library(data.table) #v1.9.6+
N <- 3L
dt[, Prod3 := Reduce(`*`, shift(x, 0L:(N - 1L), type = "lead"))]
shift
Векторизация означает, что он может создавать несколько новых столбцов одновременно в зависимости от вектора, переданного в n
аргумент. Затем, Reduce
в основном применяется *
для всех векторов одновременно поэлементно.
Вот два пути... хотя и не самые эффективные реализации возможны:
require(data.table)
N = 3L
dt[, prod := prod(dt$x[.I:(.I+N-1L)]), by=1:nrow(dt)]
Еще один, использующий embed()
:
tmp = apply(embed(dt$x, N), 1, prod)
dt[seq_along(tmp), prod := tmp]
тесты:
set.seed(1L)
dt = data.table(x=runif(1e6))
zoo_fun <- function(dt, N) {
rollapply(dt$x, N, FUN=prod, fill=NA, align='left')
}
dt1_fun <- function(dt, N) {
dt[, prod := prod(dt$x[.I:(.I+N-1L)]), by=1:nrow(dt)]
dt$prod
}
dt2_fun <- function(dt, N) {
tmp = apply(embed(dt$x, N), 1L, prod)
tmp[1:nrow(dt)]
}
david_fun <- function(dt, N) {
Reduce(`*`, shift(dt$x, 0:(N-1L), type="lead"))
}
system.time(ans1 <- zoo_fun(dt, 3L))
# user system elapsed
# 8.879 0.264 9.221
system.time(ans2 <- dt1_fun(dt, 3L))
# user system elapsed
# 10.660 0.133 10.959
system.time(ans3 <- dt2_fun(dt, 3L))
# user system elapsed
# 1.725 0.058 1.819
system.time(ans4 <- david_fun(dt, 3L))
# user system elapsed
# 0.009 0.002 0.011
all.equal(ans1, ans2) # [1] TRUE
all.equal(ans1, ans3) # [1] TRUE
all.equal(ans1, ans4) # [1] TRUE
Сейчас data.table
имеет функции быстрой прокатки. Итак, подход @Mamoun Benghezal можно использовать как
dt[, Prod.3 := frollapply(x, 3, FUN=prod, fill=NA, align='left')]
Это очень быстро, хотя и не так быстро, как функция @David Arenburg. Используя тест @Arun:
set.seed(1L)
dt = data.table(x=runif(1e6))
froll_fun <- function(dt, N) {
frollapply(dt$x, N, FUN = prod, fill = NA, align = 'left')
}
system.time(ans5 <- froll_fun(dt, 3L))
# user system elapsed
# 0.191 0.000 0.191