Код R слишком долго выполняется

У меня работает следующий код, и мне нужно много времени для запуска. Откуда мне знать, работает ли он по-прежнему или где-то застрял.

noise4<-NULL;
for(i in 1:length(noise3))
{
    if(is.na(noise3[i])==TRUE)
    {
    next;
    }
    else
    {
    noise4<-c(noise4,noise3[i]);
    }
}

Noise3 — это вектор с 2418233 точками данных.


person Concerned_Citizen    schedule 06.09.2011    source источник
comment
цикл for с 2 миллионами итераций не покажет R в очень хорошем свете.   -  person David Heffernan    schedule 06.09.2011
comment
@David Heffernan: особенно когда вы увеличиваете объект вместо предварительного выделения. @GTyler: похоже, вы могли бы просто использовать noise4 <- na.omit(noise3).   -  person Joshua Ulrich    schedule 06.09.2011
comment
Что ты имеешь в виду? Это просто R не подходит с таким количеством точек данных?   -  person Concerned_Citizen    schedule 06.09.2011
comment
Так поможет ли предварительное выделение размера вектора? Я думаю, что знаю результирующее измерение этого вектора.   -  person Concerned_Citizen    schedule 06.09.2011
comment
@GTyler большие циклы for не имеют хороших характеристик производительности на R из-за его интерпретируемого характера. Я считаю, что последние разработки улучшили ситуацию. Тем не менее, вы всегда должны искать версию, которая по возможности избегает циклов for.   -  person David Heffernan    schedule 06.09.2011
comment
@GTyler Прочтите этот вопрос и ответ, чтобы узнать, почему циклы for в этом случае работают медленно и как этого избежать: stackoverflow.com/q/6502444 /602276   -  person Andrie    schedule 06.09.2011
comment
@GTyler: R отлично работает с миллионами точек данных. Но циклы for — один из наименее эффективных способов использования R. Большинство функций R естественным образом перебирают векторы. Взгляните на эту статью: yihui.name/ en/2010/10/on-the-gory-loops-in-r Здесь объясняется, чем подход R отличается от обычного программирования.   -  person dnagirl    schedule 06.09.2011


Ответы (4)


Вы просто хотите удалить значения NA. Делай это так:

noise4 <- noise3[!is.na(noise3)]

Это будет почти мгновенно.

Или, как предлагает Джошуа, более читаемая альтернатива:

noise4 <- na.omit(noise3)

Ваш код был медленным, потому что:

  1. Он использует явные циклы, которые, как правило, работают медленно в интерпретаторе R.
  2. Вы перераспределяете память на каждой итерации.

Перераспределение памяти, вероятно, является самым большим препятствием для вашего кода.

person David Heffernan    schedule 06.09.2011

Я хотел проиллюстрировать преимущества предварительного распределения, поэтому попытался запустить ваш код... но убил его примерно через 5 минут. Я рекомендую вам использовать noise4 <- na.omit(noise3), как я сказал в своих комментариях. Этот код предназначен исключительно для иллюстративных целей.

# Create some random data
set.seed(21)
noise3 <- rnorm(2418233)
noise3[sample(2418233, 100)] <- NA

noise <- function(noise3) {
  # Pre-allocate
  noise4 <- vector("numeric", sum(!is.na(noise3)))
  for(i in seq_along(noise3)) {
    if(is.na(noise3[i])) {
      next
    } else {
      noise4[i] <- noise3[i]
    }
  }
}

system.time(noise(noise3)) # MUCH less than 5+ minutes
#    user  system elapsed 
#    9.50    0.44    9.94 

# Let's see what we gain from compiling
library(compiler)
cnoise <- cmpfun(noise)
system.time(cnoise(noise3))  # a decent reduction
#    user  system elapsed 
#    3.46    0.49    3.96 
person Joshua Ulrich    schedule 06.09.2011
comment
+1 Я начинаю чувствовать себя плохо из-за нескомпилированных функций. - person Iterator; 06.09.2011
comment
@Iterator: не расстраивайся. В основном они помогают зацикливать конструкции, где есть несколько повторяющихся вызовов функций. Вы не увидите большого выигрыша, если уже используете векторизованные функции. - person Joshua Ulrich; 06.09.2011
comment
@iterator На самом деле я думаю, что разница между интерпретируемым и скомпилированным показывает, что интерпретатор отлично справляется со своей работой. Я впечатлен. - person David Heffernan; 06.09.2011
comment
@JoshuaUllrich: Итак, вы говорите, что это помогает в функциях, которые часто повторяются, а? Угадайте, что делает мой код. :) (Правда, я много векторизирую.) - person Iterator; 06.09.2011
comment
@David Heffernan: хотя компилятор байтового кода помогает, перенос цикла на C/C++ с использованием встроенных пакетов и/или пакетов Rcpp обеспечит почти мгновенные результаты. - person Joshua Ulrich; 06.09.2011
comment
Если вы занимаетесь педагогикой, почему бы не показать время и для na.omit(noise3)? (0,5 секунды на моем компьютере) - person Ben Bolker; 06.09.2011

Другие ответы дали вам намного, гораздо лучшие способы выполнить задачу, которую вы действительно поставили перед собой (удаление NA значений в ваших данных), но ответ на конкретный вопрос, который вы задали («как я узнаю, действительно ли R работает или вместо этого он застрял?") состоит в том, чтобы ввести в ваш цикл некоторые операторы вывода (cat), как показано ниже:

rpt <- 10000  ## reporting interval
noise4<-NULL;
for(i in 1:length(noise3))
{
    if (i %% rpt == 0) cat(i,"\n")
    if(is.na(noise3[i])==TRUE)
    {
    next;
    }
    else
    {
    noise4<-c(noise4,noise3[i]);
    }
}

Если вы запустите этот код, вы сразу увидите, что он радикально замедляется по мере продвижения в цикле (следствие отказа от предварительного выделения пространства)...

person Ben Bolker    schedule 06.09.2011

Все остальные дали правильные способы решения одной и той же задачи, так что вам не нужно беспокоиться о скорости. @BenBolker также дал хороший совет относительно регулярного вывода.

Следует также отметить, что если вы окажетесь в цикле, вы можете выйти из него и найти значение i. Предполагая, что повторный запуск с этого значения i не повредит, то есть использование этого значения дважды не будет проблемой, вы можете перезапустить. Или вы можете просто закончить работу, как заявили другие.

Отдельный трюк заключается в том, что если цикл медленный (и его нельзя векторизовать, или же вы не хотите выходить из цикла), И у вас нет отчетов, вы можете по-прежнему ищите внешний метод, чтобы увидеть, действительно ли R потребляет циклы на вашем компьютере. В Linux лучше всего подходит команда top. В Windows с этим справится диспетчер задач (я предпочитаю использовать программу SysInternals/Microsoft Process Explorer). «top» также существует на Mac, хотя я считаю, что есть и другие более популярные инструменты.

Еще один совет: если вам нужно запустить очень длинный цикл, я настоятельно рекомендую регулярно сохранять результаты. Обычно я создаю файл с именем вроде: myPrefix_YYYYMMDDHHMMSS.rdat . Таким образом, все может пойти к черту, и вы все еще можете начать цикл с того места, на котором остановились.

Я не всегда итерирую, но когда делаю, использую эти приемы. Будь быстрым, мой друг.

person Iterator    schedule 06.09.2011