Избегание циклов, но сохранение информации об индексе

#r

#r

Вопрос:

Я работаю с некоторыми графоподобными данными, в основном собранными в виде векторов или списков.
Большую часть времени мне нужно проверять векторы / списки по заданным индексам и выполнять некоторую логику для определения результирующего значения для текущего элемента.

Чтобы быть немного более точным, рассмотрим такой фрагмент кода:

 for (i in 1:(length1 - 1))
    for (j in (i   1):length2)
        for (k in 1:length3) {
            d1 <- data[i, k]
            d2 <- data[j, k]
            if (d1 != d2)
                otherData[i, j, k] <- list(c(min(d1, d2), max(d1, d2)))
            else
                otherData[i, j, k] <- list(c(1, 1))
        }
  

Мой вопрос:
— Было бы это хорошим решением для:

  1. создайте векторы индексов, а затем
  2. lapply внутренние функции (которые принимают вектор индексов), которые видят внешние (объявленные во внешней функции) объекты данных и используют предоставленный вектор индексов для выполнения логики

Пример кода (более простой, без связи с кодом выше):

 someFunc <- function(data) {
    n <- length(data)
    f <- function(i) {
        return (doSthWith(data[i], i))
        # do some logic with both the data and the index
    }
    return (sapply(1:n, f))
}
  

Другое решение, которое я придумал, — создать data.frame и сделать индексы частью данных, чтобы функции lapply в основном также имели индексы во входной строке.

Я буду очень признателен за ваши мысли об этих подходах.

Комментарии:

1. Это бутылочное горлышко в ваших расчетах? Мое эмпирическое правило таково — если это работает с циклами for / while (и у вас нет больших данных), циклы просто великолепны. Возможно ли вообще получить небольшой воспроизводимый пример?

2. Я еще не в фазе измерения производительности. Скорее хочется разработать R-стиль вместо постоянного зацикливания. Как только я настроюсь на правильный лад, я мог бы придумать векторные решения, не думая циклически в первую очередь. Вот почему я спрашиваю. К сожалению, я не могу предоставить какой-либо воспроизводимый пример прямо сейчас — я думал о некоторых общих подсказках вместо решения одной заданной проблемы 🙂 Спасибо за ответ.

Ответ №1:

Что ж, вы можете выполнить векторизованное индексирование, что должно дать вам шанс на значительное ускорение. В общем, вместо:

 for(a in A) for(b in B) something(x[a,b])
  

Вы можете сделать:

 something_vectorized(x[as.matrix(expand.grid(A,B))])
  

* apply — это, по сути, обертки циклов, поэтому вы получите максимально понятный код, преобразовав циклы в них.

РЕДАКТИРОВАТЬ: Небольшая иллюстрация в дополнение к комментарию:

 > system.time(replicate(100,sum(sapply(1:1000,function(x) x^2))))
   user  system elapsed 
  0.385   0.001   0.388 
> system.time(replicate(100,sum((1:1000)^2)))
   user  system elapsed 
  0.002   0.001   0.003
  

Комментарии:

1. Спасибо за этот совет. Я попробую поэкспериментировать с этим подходом. Одна вещь заставляет меня задуматься — вы говорите, что * apply может самое большее очистить мой код — означает ли это, что использование * apply не даст никакого толчка, когда я попытаюсь использовать возможности R для использования нескольких ядер?

2. @chemical Вы должны учитывать накладные расходы при интерпретации R-кода, которые могут быть значительными. R создан для выполнения векторных операций, поэтому выполнение скалярных операций очень неоптимально; из-за этого вы можете получить 20-30-кратное замедление, никакой параллелизм здесь не поможет.

3. Использование apply поможет вам переключаться между кодом, выполняющим параллельные вычисления, и однопоточным. Это не означает, что вы получите повышение производительности (на это влияет ряд факторов, см. Комментарии mbq).

4. спасибо за отличное объяснение векторизации, это улучшило мое понимание R 🙂 Тем не менее, я все еще не могу придумать векторизованный способ ускорить кучу кода, который я опубликовал в своем вопросе. Он работает со списками списков. Должен ли я предоставить больше кода? Возможно, решив эту единственную проблему, вы могли бы показать мне общий подход, который лучше циклов.