2015-06-12 18 views
8

Bir matrisin her bir sütunu için birikimli toplamları almaya çalışıyorum. İşte benim kod R var: Kümülatif toplam hızını daha hızlı yapın

testMatrix = matrix(1:65536, ncol=256); 
microbenchmark(apply(testMatrix, 2, cumsum), times=100L); 

Unit: milliseconds 
         expr  min  lq  mean median  uq  max neval 
apply(testMatrix, 2, cumsum) 1.599051 1.766112 2.329932 2.15326 2.221538 93.84911 10000 

Ben karşılaştırma için Rcpp kullandı:
cppFunction('NumericMatrix apply_cumsum_col(NumericMatrix m) { 
    for (int j = 0; j < m.ncol(); ++j) { 
     for (int i = 1; i < m.nrow(); ++i) { 
      m(i, j) += m(i - 1, j); 
     } 
    } 
    return m; 
}'); 
microbenchmark(apply_cumsum_col(testMatrix), times=10000L); 

Unit: microseconds 
         expr  min  lq  mean median  uq  max neval 
apply_cumsum_col(testMatrix) 205.833 257.719 309.9949 265.986 276.534 96398.93 10000 

Yani C++ kodu kadar hızlı 7,5 katıdır. Saf R içinde apply(testMatrix, 2, cumsum)'dan daha iyi yapmak mümkün mü? Hiçbir sebepten ötürü bir yükün büyüklüğüne sahip olduğumu hissettiriyor.

+0

"Derleme: cmpfun" ve bu kitaplıktaki diğer araçlar aracılığıyla derlemeyi deneyebilirsiniz. Ancak, “MATLAB” ve benzeri diller gibi “R” nin komuta zamanında “derlenmesi” nedeniyle çok fazla yükü olduğu bilinmektedir. Bu yüzden insanlar maksimum hıza ihtiyaç duyduklarında Fortran veya cpp fonksiyonlarını bağırıyorlar. –

+3

Uygula (testMatrix, 2, cumsum) için hızlı alternatif 'matrixStats :: colCumsums (testMatrix)' dır. – Khashaa

+0

@Khashaa, C kodunun da kullanıldığından, sizin R'den daha hızlı olduğunu hayal ediyorum. Yazarın kesinlikle R uygulaması hakkında soru sorduğuna inanıyorum. – cdeterman

cevap

4

Bayt ile derlenmiş bir döngü kullanmak, sistemimde apply numaralı çağrıdan biraz daha hızlıdır. Daha hızlı olmasını bekledim çünkü apply'dan daha az iş yapıyor. Beklendiği gibi, R döngü hala yazdığınız basit C++ işlevinden daha yavaştır.

colCumsum <- compiler::cmpfun(function(x) { 
    for (i in 1:ncol(x)) 
    x[,i] <- cumsum(x[,i]) 
    x 
}) 

testMatrix <- matrix(1:65536, ncol=256) 
m <- testMatrix 
require(microbenchmark) 
microbenchmark(colCumsum(m), apply_cumsum_col(m), apply(m, 2, cumsum), times=100L) 
# Unit: microseconds 
#     expr  min  lq median  uq  max neval 
#  matrixCumsum(m) 1478.671 1540.5945 1586.1185 2199.9530 37377.114 100 
# apply_cumsum_col(m) 178.214 192.4375 204.3905 234.8245 1616.030 100 
# apply(m, 2, cumsum) 1879.850 1940.1615 1991.3125 2745.8975 4346.802 100 
all.equal(colCumsum(m), apply(m, 2, cumsum)) 
# [1] TRUE 
5

Sadece R kodu ile C++ atmak zordur. Bunu yapmayı düşünebilmemin en hızlı yolu, matrisinizi bir listeye ayırmaya istekli olmanızdır. Bu şekilde, R ilkel işlevler kullanıyor ve nesneyi her yineleme ile kopyalamıyor (apply aslında güzel bir döngüdür). C++ 'nın hala kazandığını görebiliyorsunuz, ancak sadece R kodunu kullanmak istiyorsanız list yaklaşımı ile önemli bir hızlanma var.

fun1 <- function(){ 
    apply(testMatrix, 2, cumsum) 
} 

testList <- split(testMatrix, col(testMatrix)) 

fun2 <- function(){ 
    lapply(testList, cumsum) 
} 

microbenchmark(fun1(), 
       fun2(), 
       apply_cumsum_col(testMatrix), 
       times=100L) 


Unit: microseconds 
         expr  min  lq  mean median  uq  max neval 
         fun1() 3298.534 3411.9910 4376.4544 3477.608 3699.2485 9249.919 100 
         fun2() 558.800 596.0605 766.2377 630.841 659.3015 5153.100 100 
apply_cumsum_col(testMatrix) 219.651 282.8570 576.9958 311.562 339.5680 4915.290 100 

DÜZENLEME bir listeye de matris bölmek için zaman eklerseniz bu yöntem fun1 daha yavaş olduğuna dikkat edin.

+2

Matrisi bir listeye bölmek için gereken süre de hesaba katılmamalıdır? – nrussell

+1

@nrussell, o zaman 'fun1' bile daha yavaş olurdu. Fakat bu, cumsum'un hızının geliştirilebileceği düşünülen tek senaryodu. Kesinlikle diğer cevaplara açık :) – cdeterman