Это немного поздно, но в случае, если это полезно.
Я согласен с @alexis_laz, что вычисляется больше сравнений, чем необходимо. Я думаю, что идея может быть сделана еще дальше, потому что, если any
применяется катящимся образом, это также приводит к ненужным вычислениям.
Ключ в том, что данная строка всегда сравнивается с другой определенной строкой (ниже в примере ниже).Как только мы узнаем, соответствует ли эквивалентность этой строке, любые другие строки, содержащие ее в данном окне, должны иметь значение 1 (TRUE).
Полезный ярлык здесь является то, что если эквивалентность имеет место для ряда j
и что делает ряд i
TRUE, и ряд j
также в окне строки i+1
, то i+1
также TRUE (без необходимости знать о статусе другие точки в окне). Я получаю то, что нам не нужно определять any
для окна каждой строки. Если мы знаем, сколько TRUE есть в строке i
, для строки i+1
нам просто нужно определить, была ли точка, покидающая окно, ИСТИНА, и имеет ли точка, входящая в окно TRUE. По сути, мы фильтруем вектор с полем Window
-width, а затем просто проверяем, какие записи имеют по крайней мере одно значение TRUE в своем окне (это может быть сделано за один проход, но давайте проигнорируем это, поскольку дополнительное время незначительный).
Используя скользящую сумму, мы можем эффективно вычислить это, просто выполнив подсчет и включив/удалив точки, входящие/выходящие из окна. Именно здесь происходит наблюдение @ alexis_laz: состояние точек входа/выхода может быть предварительно рассчитано.
Чтобы сделать вещи более конкретными, вот какой-то код. Во-первых, позвольте мне скопировать ваш исходный цикл, ответ @Richard Telford и ответ @ alexis_laz и включить их в функции (с небольшими перезаписями в основном для личного удобства, поэтому формат вывода совпадает, и, надеюсь, без добавления каких-либо ошибок):
f_G <- function(x, window, idiff, valdiff){
base_rows = 1:(NROW(x) - idiff - 1) # can't check more than this
candidate_max = pmin(base_rows + window, NROW(x) - idiff) # maximum row to start checking against
out = rep(0, NROW(x)) #pre-allocate variable
for (i in seq_along(base_rows)) {
out[i] = as.numeric(any(x[(i + 1):candidate_max[i]]
- x[((i + 1):candidate_max[i]) + idiff] > valdiff))}
return(out)
}
f_RT <- function(x, window, idiff, valdiff){
x0 <- cbind(x[-(1)][1:NROW(x)], sapply(2:window,
function(i)x[-(1:i)][1:NROW(x)]))
x1 <- sapply((idiff+1):(idiff+window),
function(i)x[-(1:i)][1:NROW(x)])
out <- as.numeric(apply((x0 - x1) > valdiff, 1, any, na.rm = TRUE))
return(out)
}
f_AL <- function(x, window, idiff, valdiff){
check = (x[2:(NROW(x) - idiff)] - x[(2 + idiff):NROW(x)]) > valdiff
check <- c(check, rep(FALSE, idiff+1))
out <- as.integer(sapply(seq_along(check),
function(i) any(check[i:min(length(check), (i + (window - 1)))])))
return(out)
}
Тогда вот две функции для вычисления суммы прокатки, описанной выше, над вектором с заранее рассчитанными отличиями (например, предложенным @alexis_laz). Первая использует функцию filter
, а вторая использует roll_sum
из пакета RcppRoll.
f_filt <- function(x, window, idiff, valdiff){
## calculate idiff differences once
check = as.integer((x[2:(NROW(x) - idiff)] - x[(2 + idiff):NROW(x)]) > valdiff)
## extend series to filter
check <- c(check, rep(0, window+idiff))
## reverse series due to filter using "past" values
ffilt <- rev(filter(rev(check), rep(1, window), sides=1))
## check if at least one
out <- ifelse(na.omit(ffilt) > 0, 1, 0)
return(out)
}
library(RcppRoll)
f_roll <- function(x, window, idiff, valdiff){
## calculate idiff differences once
check = as.integer((x[2:(NROW(x) - idiff)] - x[(2 + idiff):NROW(x)]) > valdiff)
## extend series to filter
check <- c(check, rep(0, window+idiff))
## rolling window sum
froll <- roll_sum(check, n=window, align="right")
out <- ifelse(froll > 0, 1, 0)
return(out)
}
Как быстро проверить, мы можем проверить, что все функции дают одинаковые ответы:
f_G(df$Price, Window, IndexDifference, ValueDifference)
# 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0
f_RT(df$Price, Window, IndexDifference, ValueDifference)
# 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0
f_AL(df$Price, Window, IndexDifference, ValueDifference)
# 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0
f_filt(df$Price, Window, IndexDifference, ValueDifference)
# 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0
f_roll(df$Price, Window, IndexDifference, ValueDifference)
# 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0
позволит теперь сравнить их. Я также увеличу количество строк для тестирования.
library(microbenchmark)
w <- Window
idiff <- IndexDifference
vdiff <- ValueDifference
df2 <- rep(df$Price, 5000) #100,000 entries
microbenchmark(f_G(df2, w, idiff, vdiff),
f_RT(df2, w, idiff, vdiff),
f_AL(df2, w, idiff, vdiff),
f_filt(df2, w, idiff, vdiff),
f_roll(df2, w, idiff, vdiff)
)
Unit: milliseconds
expr min lq mean median uq max neval cld
f_G(df2, w, idiff, vdiff) 395.80227 412.05120 419.88554 413.55551 417.84907 479.47306 100 e
f_RT(df2, w, idiff, vdiff) 154.43919 192.99473 193.10029 195.61031 197.95933 236.27244 100 c
f_AL(df2, w, idiff, vdiff) 233.30237 244.01664 249.75449 245.07001 248.51249 319.04956 100 d
f_filt(df2, w, idiff, vdiff) 21.53997 22.51582 25.38218 22.59477 23.56873 63.48320 100 b
f_roll(df2, w, idiff, vdiff) 14.26333 14.35543 16.99302 15.24879 15.45127 55.49886 100 a
Наконец, мы видим, что мы получаем довольно хорошее ускорение скорости, делая это. Другая аккуратная вещь о том, чтобы приближаться к ней таким образом, заключается в том, что она остается такой же эффективной, независимо от размера окна (в частности, непосредственно делая текущую сумму, используя filter
, немного замедляется, хотя она все еще довольно быстро).
w <- 25 #Window
df3 <- rep(df$Price, 5000) #100,000 entries
microbenchmark(f_G(df3, w, idiff, vdiff),
f_RT(df3, w, idiff, vdiff),
f_AL(df3, w, idiff, vdiff),
f_filt(df3, w, idiff, vdiff),
f_roll(df3, w, idiff, vdiff)
)
Unit: milliseconds
expr min lq mean median uq max neval cld
f_G(df3, w, idiff, vdiff) 487.65798 516.67700 537.54019 541.34459 551.52128 592.05720 100 e
f_RT(df3, w, idiff, vdiff) 328.44934 366.76176 389.08534 401.39053 409.49376 518.94535 100 d
f_AL(df3, w, idiff, vdiff) 240.99006 258.66045 263.21317 260.09258 263.75917 319.02493 100 c
f_filt(df3, w, idiff, vdiff) 37.32291 37.41098 38.97167 37.47234 38.40989 79.51684 100 b
f_roll(df3, w, idiff, vdiff) 15.49264 15.52950 15.86283 15.55252 15.62852 19.77415 100 a
я должен быть более эффективным, чтобы избежать '[.data.frame' и' [<-. Data.frame', если вы, как раз, есть «Сигнал» и вектор «Цена». Например. сравните назначение одному столбцу «data.frame» в отличие от простого вектора: 'x1 = data.frame (col1 = integer (1e5)); x2 = целое число (1e5); system.time (for (i in seq_len (nrow (x1))) x1 $ col1 [i] = 1L); system.time (for (i in seq_along (x2)) x2 [i] = 1L) ' –
Спасибо, Алексис. У меня есть десятки столбцов. Это упрощенный пример. – Krug