簡體   English   中英

R組合,尋找比基本R更快更有效的方式(包,代碼,並行cpu)

[英]R combinations, looking for faster and more efficient way(package,code,parallel cpu) than basic R

我正在使用基本的R組合。

例如,假設我有一個包含2行和5列的矩陣:

 z<-matrix(c(1, 2, 1, 3, 2, 2, 1, 3, 2, 1),nrow=2,ncol=5,byrow = TRUE)

[,1] [,2] [,3] [,4] [,5]

[1,]    1    2    1    3    2

[2,]    2    1    3    2    1

我正在使用以下代碼來處理5列中3組的組合:

l<- apply(X = combn(seq_len(ncol(z)), 3),MAR = 2,FUN = function(jj) {apply(z[, jj], 1, paste, collapse="") })

這導出我需要的東西:

[,1]  [,2]  [,3]  [,4]  [,5]  [,6]  [,7]  [,8]  [,9]  [,10]

[1,] "121" "123" "122" "113" "112" "132" "213" "212" "232" "132"

[2,] "213" "212" "211" "232" "231" "221" "132" "131" "121" "321"

當我在矩陣中使用大數據時,問題就出現了,例如,當我有一個包含15000行和17列的矩陣時,我需要17列中10個集合的組合。

在此示例中,此導出需要很長時間。

對於這個組合示例,是否有比基本R(可能是某些包或代碼,或使用並行cpu)更快更有效的方法?

我使用的是Windows 7 64位,FX 8320,16GB RAM。

正如@inscaven指出的那樣,實時緊縮來自paste 如果我們只需要生成所有17個選擇10個組合15000次,那么在RarrangementsRcppAlgos (我是作者)中出現幾個高度優化的包時不會花那么長時間:

set.seed(101)
testMat <- matrix(sample(1000, 15000 * 17, TRUE), nrow = 15000)

library(arrangements)
system.time(lapply(1:15000, function(x) {
    temp <- combinations(x = testMat[x, ], k = 10)
    x
}))
  user  system elapsed 
 6.879   2.133   9.014

library(RcppAlgos)
system.time(lapply(1:15000, function(x) {
    temp <- comboGeneral(testMat[x, ], 10)
    x
}))
  user  system elapsed 
 5.770   2.178   7.953

相比combn在加載base R

system.time(lapply(1:15000, function(x) {
    temp <- combn(testMat[x, ], 10)
    x
}))
    user  system elapsed 
 261.163   1.093 262.608 

如果我們必須將我們的結果組合成一個字符矩陣,那么我們可以在base R中做更多的事情。 即使我們使用上面提到的任何一個優化庫,我們仍然會在所有行上循環並粘貼結果很慢。

system.time(t1 <- lapply(1:50, function(x) {
    combn(testMat[x, ], 10, paste0, collapse = "")
}))
  user  system elapsed 
 6.847   0.070   6.933

## from package arrangements
system.time(t2 <- lapply(1:50, function(x) {
    apply(combinations(x = testMat[x, ], k = 10), 1, paste0, collapse = "")
}))
  user  system elapsed 
 6.318   0.032   6.353

這不是一場真正的勝利。 我們需要一種新方法。

輸入Rcpp

//[[Rcpp::export]]
CharacterVector pasteCombos(int n, int r, CharacterVector v, int numRows) {

    int r1 = r - 1, r2 = r - 2;
    int numIter, count = 0;
    CharacterVector comboVec = Rcpp::no_init_vector(numRows);

    std::vector<int> z(r);
    std::iota(z.begin(), z.end(), 0);

    while (count < numRows) {
        numIter = n - z[r1];
        if ((numIter + count) > numRows)
            numIter = numRows - count;

        for (int i = 0; i < numIter; ++i, ++count, ++z[r1])
            for (int k = 0; k < r; ++k)
                comboVec[count] += v[z[k]];

        for (int i = r2; i >= 0; i--) {
            if (z[i] != (n - r + i)) {
                ++z[i];
                for (int k = (i + 1); k < r; ++k) 
                    z[k] = z[k - 1] + 1;

                break;
            }
        }
    }

    return comboVec;
}

此函數只生成v選擇r所有組合,並通過+=粘貼結果。 這將生成一個向量,而無需處理矩陣的行。 讓我們看看我們是否有任何改進。

numCombs <- choose(17, 10)
charMat <- matrix(as.character(testMat), nrow = 15000)

funOP <- function(z, r) {
    apply(X = combn(seq_len(ncol(z)), r), MAR = 2,FUN = function(jj) {apply(z[, jj], 1, paste, collapse="") })
}

system.time(t1 <- funOP(testMat[1:100, ], 10))
   user  system elapsed 
 22.221   0.110  22.330 

system.time(t2 <- lapply(1:100, function(x) {
     pasteCombos(17, 10, charMat[x,], numCombs)
}))
  user  system elapsed 
 7.890   0.085   7.975

快了近3倍......不錯,但我們可以做得更好。

輸入parallel

library(parallel)
system.time(t3 <- mclapply(1:100, function(x) {
    pasteCombos(17, 10, charMat[x,], numCombs)
}, mc.cores = 8)) ## you will have to adjust this on your computer.. I'm running MacOS with 8 cores
  user  system elapsed 
 1.430   0.454   1.912

現在我們正在談論! 快了近12倍!!

這是一個完整性檢查:

all.equal(t1, do.call(rbind, t2))
# [1] TRUE
all.equal(t1, do.call(rbind, t3))
# [1] TRUE

總的來說,如果我們假設我們可以在2秒內完成100行,我們可以在相當的2 * 150 = 300 seconds = 5 minutes完成我們的任務。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM