R: Самый эффективный способ вычислить сумму массива со многими нулями? - PullRequest
0 голосов
/ 28 сентября 2019

У меня есть матрица с n строками, m столбцами.

Я хочу применить функцию f(x) к каждому элементу в матрице, а затем суммировать элементы по строкам (так, чтобы вывод был вектором).

Однако значение функции f(x) равно нулю для отрицательного x.

Итак, сейчас я зацикливаюсь на каждой строке, а затем применяю что-то вроде sum(f(row_i)).

Это работает, но как я могу сделать это быстрее?

В частности, могу ли я как-то использовать тот факт, что я знаю, что f(x) равен нулю для отрицательного x (и, следовательно, нетдобавить в sum)?Или я могу как-то избавиться от зацикливания строк?Я пытался sapply, но это не ускоряет процесс.

Ответы [ 2 ]

1 голос
/ 28 сентября 2019

Я не уверен, что это самый быстрый способ , но он, вероятно, будет очень эффективным.

В основном используйте векторизованную функцию rowSums для суммирования, сначала купите преобразование матрицы только в положительные значения.Если ваша функция f(x) векторизована, вы можете сначала применить эту функцию ко всей матрице, а затем использовать rowSums.

n <- 1e6
ncol = 10
dat <- matrix(rnorm(n), ncol= ncol)
system.time(rowSums(ifelse(dat > 0, dat, 0)) #or rowSums(f(dat)))
#output
   user  system elapsed 
   0.02    0.03    0.04 
0 голосов
/ 28 сентября 2019

Вот подход «применять / применять» со сравнениями с более наивным циклическим подходом.Ускорение примерно в 3 раза (хотя зависит от процента негативов в матрице).Если цикл, который вы выполняете, еще более наивен (например, без предварительного распределения), то ускорение будет больше, но в этом случае проблема действительно в цикле как таковом, а не в отсутствии фильтрации каждой строки.Наилучший подход - это написать полностью векторизованный код, если это вообще возможно, и пропустить циклы или полностью выполнить.Без подробностей невозможно сказать, что бы это значило в вашем случае.Я добавил, что для сравнения будет работать полностью векторизованная версия, использующая rowSums().Как вы можете видеть, вот где реальное ускорение:

#functions to create a vector of sums where f is applied to the rows of A
#The first method is a naive loop, the second takes advantage of the fact that
#x < 0 imples f(x) == 0

method1 <- function(A,f){
  m <- nrow(A)
  n <- ncol(A)
  v <- rep(0,m)
  for(i in 1:m){
    row <- rep(0,n)
    for(j in 1:n){
      row[j] = f(A[i,j])
    }
    v[i] = sum(row)
  }
  v
}

method2 <- function(A,f){
  apply(A,1,function(row){sum(sapply(row[row >= 0],f))})
}

#for testing:

f <- function(x) max(0,x)^2
g <- function(x) pmax(x,0)^2 #vectorized version of f
A <- matrix(runif(10000,-10,2),nrow = 100)

library(microbenchmark)
print(microbenchmark(method1(A,f),method2(A,f),rowSums(g(A))))

Типичный вывод (на моей машине):

Unit: microseconds
          expr       min         lq       mean     median        uq       max neval
 method1(A, f) 13826.824 14672.9285 16076.5778 15342.0195 16916.631 33829.384   100
 method2(A, f)  5629.886  6009.6550  6687.8325  6247.3185  7117.401 14411.815   100
 rowSums(g(A))   217.566   249.6535   315.7482   271.8685   303.075  2918.966   100
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...