Как ускорить цикл R?
Я выполняю следующий цикл for для функции gwr.basic в пакете GWmodel в R. Что мне нужно сделать, это собрать среднее значение параметра оценки для любой заданной полосы пропускания.
код выглядит так:
library(GWmodel)
data("DubVoter")
#Dub.voter
LARentMean = list()
for (i in 20:21)
{
gwr.res <- gwr.basic(GenEl2004 ~ DiffAdd + LARent + SC1 + Unempl + LowEduc + Age18_24 + Age25_44 + Age45_64, data = Dub.voter, bw = i, kernel = "bisquare", adaptive = TRUE, F123.test = TRUE)
a <- mean(gwr.res$SDF$LARent)
LARentMean[i] <- a
}
outcome = unlist(LARentMean)
> outcome
[1] -0.1117668 -0.1099969
Однако он очень медленно возвращает результат. Мне нужен гораздо более широкий диапазон, например 20:200. Есть ли способ ускорить процесс? Если нет, то как получить ступенчатый диапазон, скажем, от 20 до 200 с шагом 5, чтобы уменьшить количество операций?
Я - пользователь Python, плохо знакомый с R. Я читал на SO, что R хорошо известен своей медлительностью для циклов for и что есть более эффективные альтернативы. Больше ясности в этом вопросе будет приветствоваться.
2 ответа
У меня такое же впечатление, как у @musically_ut. Цикл для и традиционный for-vs.apply
Дискуссия вряд ли поможет вам здесь. Попробуйте пойти на распараллеливание, если у вас есть более одного ядра. Есть несколько пакетов, таких как parallel
или же snowfall
, Какой пакет в конечном итоге является лучшим и быстрым, зависит от вашей машины и операционной системы.
Лучшие не всегда равны самым быстрым здесь. Код, который работает кроссплатформенно и может стоить больше, чем дополнительная производительность. Также прозрачность и простота использования могут перевесить максимальную скорость. При этом мне очень нравится стандартное решение, и я рекомендую использовать parallel
который поставляется с R и работает в Windows, OSX и Linux.
РЕДАКТИРОВАТЬ: вот полностью воспроизводимый пример, используя пример OP.
library(GWmodel)
data("DubVoter")
library(parallel)
bwlist <- list(bw1 = 20, bw2 = 21)
cl <- makeCluster(detectCores())
# load 'GWmodel' for each node
clusterEvalQ(cl, library(GWmodel))
# export data to each node
clusterExport(cl, varlist = c("bwlist","Dub.voter"))
out <- parLapply(cl, bwlist, function(e){
try(gwr.basic(GenEl2004 ~ DiffAdd + LARent + SC1 +
Unempl + LowEduc + Age18_24 + Age25_44 +
Age45_64, data = Dub.voter,
bw = e, kernel = "bisquare",
adaptive = TRUE, F123.test = TRUE ))
} )
LArent_l <- lapply(lapply(out,"[[","SDF"),"[[","LARent")
unlist(lapply(LArent_l,"mean"))
# finally, stop the cluster
stopCluster(cl)
Помимо использования распараллеливания, как предлагает Мэтт Баннер, вы должны предварительно выделить вектор LARentMean
, Часто это не for
Сам цикл, который медленный, но тот факт, что for
соблазняет вас делать медленные вещи, такие как создание растущих векторов.
Рассмотрим следующий пример, чтобы увидеть влияние растущего вектора по сравнению с предварительным выделением памяти:
library(microbenchmark)
growing <- function(x) {
mylist <- list()
for (i in 1:x) {
mylist[[i]] <- i
}
}
allocate <- function(x) {
mylist <- vector(mode = "list", length = x)
for (i in 1:x) {
mylist[[i]] <- i
}
}
microbenchmark(growing(1000), allocate(1000), times = 1000)
# Unit: microseconds
# expr min lq mean median uq max neval
# growing(1000) 3055.134 4284.202 4743.4874 4433.024 4655.616 47977.236 1000
# allocate(1000) 867.703 917.738 998.0719 956.441 995.143 2564.192 1000
Растущий список примерно в 5 раз медленнее, чем версия, которая предварительно выделяет память.