Эффективно вычислить суммы строк 3d-массива в R
Рассмотрим массив a
:
> a <- array(c(1:9, 1:9), c(3,3,2))
> a
, , 1
[,1] [,2] [,3]
[1,] 1 4 7
[2,] 2 5 8
[3,] 3 6 9
, , 2
[,1] [,2] [,3]
[1,] 1 4 7
[2,] 2 5 8
[3,] 3 6 9
Как мы эффективно вычисляем суммы строк матриц, индексированных третьим измерением, так что результат:
[,1] [,2]
[1,] 12 12
[2,] 15 15
[3,] 18 18
??
Сумма столбцов проста с помощью аргумента 'dims'
colSums()
:
> colSums(a, dims = 1)
но я не могу найти способ использовать rowSums()
в массиве для достижения желаемого результата, так как он имеет другую интерпретацию 'dims'
с помощью colSums()
.
Просто вычислить нужные суммы строк, используя:
> apply(a, 3, rowSums)
[,1] [,2]
[1,] 12 12
[2,] 15 15
[3,] 18 18
но это просто скрывает цикл. Существуют ли другие эффективные, действительно векторизованные способы вычисления требуемых сумм строк?
Ответы
Ответ 1
@Fojtasek ответ, упомянутый разделение массива, напомнил мне функцию aperm()
, которая позволяет переставлять размеры массива. Поскольку colSums()
работает, мы можем поменять местами первые два измерения с помощью aperm()
и запустить colSums()
на выходе.
> colSums(aperm(a, c(2,1,3)))
[,1] [,2]
[1,] 12 12
[2,] 15 15
[3,] 18 18
Некоторые сравнительные тайминги этого и других предложенных ответов на основе R:
> b <- array(c(1:250000, 1:250000),c(5000,5000,2))
> system.time(rs1 <- apply(b, 3, rowSums))
user system elapsed
1.831 0.394 2.232
> system.time(rs2 <- rowSums3d(b))
user system elapsed
1.134 0.183 1.320
> system.time(rs3 <- sapply(1:dim(b)[3], function(i) rowSums(b[,,i])))
user system elapsed
1.556 0.073 1.636
> system.time(rs4 <- colSums(aperm(b, c(2,1,3))))
user system elapsed
0.860 0.103 0.966
Итак, в моей системе решение aperm()
выглядит немного быстрее:
> sessionInfo()
R version 2.12.1 Patched (2011-02-06 r54249)
Platform: x86_64-unknown-linux-gnu (64-bit)
Однако rowSums3d()
не дает те же ответы, что и другие решения:
> all.equal(rs1, rs2)
[1] "Mean relative difference: 0.01999992"
> all.equal(rs1, rs3)
[1] TRUE
> all.equal(rs1, rs4)
[1] TRUE
Ответ 2
Вы можете нарезать массив на два измерения, вычислить суммы строк на этом, а затем поместить вывод обратно вместе так, как вы этого хотите. Например:
rowSums3d <- function(a){
m <- matrix(a,ncol=ncol(a))
rs <- rowSums(m)
matrix(rs,ncol=2)
}
> a <- array(c(1:250000, 1:250000),c(5000,5000,2))
> system.time(rowSums3d(a))
user system elapsed
1.73 0.17 1.96
> system.time(apply(a, 3, rowSums))
user system elapsed
3.09 0.46 3.74
Ответ 3
Я не знаю о наиболее эффективном способе этого, но sapply
, похоже, преуспевает
a <- array(c(1:9, 1:9), c(3,3,2))
x1 <- sapply(1:dim(a)[3], function(i) rowSums(a[,,i]))
x1
[,1] [,2]
[1,] 12 12
[2,] 15 15
[3,] 18 18
x2 <- apply(a, 3, rowSums)
all.equal(x1, x2)
[1] TRUE
Это дает улучшение скорости следующим образом:
> a <- array(c(1:250000, 1:250000),c(5000,5000,2))
> summary(replicate(10, system.time(rowSums3d(a))[3]))
Min. 1st Qu. Median Mean 3rd Qu. Max.
2.784 2.799 2.810 2.814 2.821 2.862
> summary(replicate(10, system.time(apply(a, 3, rowSums))[3]))
Min. 1st Qu. Median Mean 3rd Qu. Max.
2.730 2.755 2.766 2.776 2.788 2.839
> summary(replicate(10, system.time( sapply(1:dim(a)[3], function(i) rowSums(a[,,i])) )[3]))
Min. 1st Qu. Median Mean 3rd Qu. Max.
1.840 1.852 1.867 1.872 1.893 1.914
Сроки были выполнены:
# Ubuntu 10.10
# Kernal Linux 2.6.35-27-generic
> sessionInfo()
R version 2.12.1 (2010-12-16)
Platform: x86_64-pc-linux-gnu (64-bit)
Ответ 4
Если у вас многоядерная система, вы можете написать простую функцию C и использовать параллельную библиотеку параллельных потоков Open MP. Я сделал что-то подобное для моей проблемы, и я получаю 8-кратное увеличение на 8-ядерную систему. Код будет по-прежнему работать на однопроцессорной системе и даже компилироваться в системе без OpenMP, возможно, с небольшим количеством #ifdef _OPENMP здесь и там.
Конечно, его стоит делать, если вы знаете, что большую часть времени занимает. Перед настройкой профилируйте свой код.