Как ускорить цикл 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 раз медленнее, чем версия, которая предварительно выделяет память.

Другие вопросы по тегам