Ответ 1
Добавление элементов в список происходит очень медленно при выполнении одного элемента за раз. См. Два примера:
Я сохраняю переменную Result
в глобальной среде, чтобы избежать копирования кадров оценки и говорить R, где искать ее с помощью .GlobalEnv$
, чтобы избежать слепого поиска с помощью <<-
:
Result <- list()
AddItemNaive <- function(item)
{
.GlobalEnv$Result[[length(.GlobalEnv$Result)+1]] <- item
}
system.time(for(i in seq_len(2e4)) AddItemNaive(i))
# user system elapsed
# 15.60 0.00 15.61
Slow. Теперь попробуем второй подход:
Result <- list()
AddItemNaive2 <- function(item)
{
.GlobalEnv$Result <- c(.GlobalEnv$Result, item)
}
system.time(for(i in seq_len(2e4)) AddItemNaive2(i))
# user system elapsed
# 13.85 0.00 13.89
Все еще медленно.
Теперь попробуйте использовать environment
и создайте новые переменные в этой среде вместо добавления элементов в список. Проблема здесь в том, что переменные должны быть названы, поэтому я буду использовать счетчик в виде строки, чтобы назвать каждый элемент "слот":
Counter <- 0
Result <- new.env()
AddItemEnvir <- function(item)
{
.GlobalEnv$Counter <- .GlobalEnv$Counter + 1
.GlobalEnv$Result[[as.character(.GlobalEnv$Counter)]] <- item
}
system.time(for(i in seq_len(2e4)) AddItemEnvir(i))
# user system elapsed
# 0.36 0.00 0.38
Гораздо быстрее.:-) Это может быть немного неудобно работать, но он работает.
Конечный подход использует список, но вместо увеличения его размера по одному элементу он удваивает размер каждый раз, когда список заполняется. Размер списка также сохраняется в выделенной переменной, чтобы избежать замедления с помощью length
:
Counter <- 0
Result <- list(NULL)
Size <- 1
AddItemDoubling <- function(item)
{
if( .GlobalEnv$Counter == .GlobalEnv$Size )
{
length(.GlobalEnv$Result) <- .GlobalEnv$Size <- .GlobalEnv$Size * 2
}
.GlobalEnv$Counter <- .GlobalEnv$Counter + 1
.GlobalEnv$Result[[.GlobalEnv$Counter]] <- item
}
system.time(for(i in seq_len(2e4)) AddItemDoubling(i))
# user system elapsed
# 0.22 0.00 0.22
Это еще быстрее. И так же легко работать как любой список.
Попробуйте эти последние два решения с большим количеством итераций:
Counter <- 0
Result <- new.env()
system.time(for(i in seq_len(1e5)) AddItemEnvir(i))
# user system elapsed
# 27.72 0.06 27.83
Counter <- 0
Result <- list(NULL)
Size <- 1
system.time(for(i in seq_len(1e5)) AddItemDoubling(i))
# user system elapsed
# 9.26 0.00 9.32
Ну, последнее, безусловно, способ пойти.