Заменить rbind в цикле for на lapply? (2-й круг ада)

У меня проблемы с оптимизацией части кода R. Следующий пример кода должен проиллюстрировать мою проблему оптимизации:

Некоторые инициализации и определение функции:

a <- c(10,20,30,40,50,60,70,80)
b <- c(“a”,”b”,”c”,”d”,”z”,”g”,”h”,”r”)
c <- c(1,2,3,4,5,6,7,8)
myframe <- data.frame(a,b,c)
values <- vector(length=columns)
solution <- matrix(nrow=nrow(myframe),ncol=columns+3)

myfunction <- function(frame,columns){
athing = 0
   if(columns == 5){
   athing = 100
   }
   else{
   athing = 1000
   }
value[colums+1] = athing
return(value)}

Проблемный цикл for выглядит так:

columns = 6
for(i in 1:nrow(myframe){
   values <- myfunction(as.matrix(myframe[i,]), columns)
   values[columns+2] = i
   values[columns+3] = myframe[i,3]
   #more columns added with simple operations (i.e. sum)

   solution <- rbind(solution,values)
   #solution is a large matrix from outside the for-loop
}

Проблема, по всей видимости, связана с функцией rbind. Я часто получаю сообщения об ошибках, касающиеся размера solution, который через некоторое время кажется слишком большим (более 50 МБ). Я хочу заменить этот цикл и rbind списком и lapply и / или foreach. Я начал с преобразования myframe в список.

myframe_list <- lapply(seq_len(nrow(myframe)), function(i) myframe[i,])

На самом деле я не продвинулся дальше этого, хотя я попытался применить это очень хорошее введение в параллельную обработку.

Как мне восстановить цикл for без изменения myfunction? Очевидно, я открыт для разных решений ...

Изменить: эта проблема кажется прямо из 2-го круга ада из R Inferno < / а>. Какие-либо предложения?


person user3347232    schedule 10.11.2014    source источник
comment
Что такое columns? Я хорошо понимаю, value - это вектор с двумя возможными значениями: 100 и 1000?   -  person Adii_    schedule 10.11.2014
comment
Перед циклом for это ... columns - это изменяющееся количество столбцов values-frame и solutions-martrix. В зависимости от конкретного ввода (в реальном скрипте возможно более 10000 столбцов). myfunction гораздо сложнее в реальном сценарии. Тем не менее, это просто серия if-ветвей. Каждый values-frame создается циклом for и myfunction и r связывается с матрицей solutions.   -  person user3347232    schedule 10.11.2014
comment
Дыд, ты попробуй вместо этого solution <- rbind(solution,values) попробовать solution[i,] = values? Насколько я понимаю, у вас уже есть solution матрица, поэтому связывать следующие строки нет необходимости. Изменение существующей строки od NA на value более эффективно. Возможно, это сработает?   -  person Adii_    schedule 10.11.2014
comment
solution уже создан, но не завершен, поскольку показанный здесь цикл for находится внутри другого цикла for, который полагается на solution.   -  person user3347232    schedule 10.11.2014


Ответы (2)


Причина, по которой использование rbind в таком цикле является плохой практикой, заключается в том, что на каждой итерации вы увеличиваете свой solution фрейм данных, а затем копируете его в новый объект, что является очень медленным процессом и также может привести к проблемам с памятью. Один из способов обойти это - создать список, i-й компонент которого будет хранить выходные данные i-й итерации цикла. Последний шаг - вызвать rbind в этом списке (только один раз в конце). Это будет выглядеть примерно так

my.list <- vector("list", nrow(myframe))
for(i in 1:nrow(myframe)){
    # Call all necessary commands to create values
    my.list[[i]] <- values
}
solution <- rbind(solution, do.call(rbind, my.list))
person konvas    schedule 11.11.2014
comment
Это именно то, что я искал! Большое Вам спасибо. Это сокращает время выполнения на моей машине с 40 минут до 2 минут с минимально возможным columns. Кстати: Последнее, что я пробовал, было solution <- do.call('rbind',my.list). Это явно не сработало. Еще раз спасибо! - person user3347232; 11.11.2014

Слишком долго ждать комментариев, поэтому я помещаю его здесь: Если columns известен заранее:

    myfunction <- function(frame){
    athing = 0
       if(columns == 5){
       athing = 100
       }
       else{
       athing = 1000
       }
    value[colums+1] = athing
    return(value)}

    apply(myframe, 2, myfunction)

Если columns не указан через среду, вы можете использовать:

apply(myframe, 2, myfunction, columns) с вашим исходным myfunction определением.

person Adii_    schedule 10.11.2014
comment
Извините, но я не понимаю, как это привело бы к такому же результату. ;) Где бы в таком случае был мой solution-matrix? Куда добавлены столбцы из исходного цикла for? - person user3347232; 10.11.2014