#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))
}
Мой вопрос:
— Было бы это хорошим решением для:
- создайте векторы индексов, а затем
- 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 🙂 Тем не менее, я все еще не могу придумать векторизованный способ ускорить кучу кода, который я опубликовал в своем вопросе. Он работает со списками списков. Должен ли я предоставить больше кода? Возможно, решив эту единственную проблему, вы могли бы показать мне общий подход, который лучше циклов.