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's)更快更有效的方法?
我正在使用 Windows 7 64 位、FX 8320、16GB 内存。
正如@inscaven 指出的那样,实时紧缩来自 paste
。如果我们只需要生成所有 17 选 10 组合 15000 次,随着 R
、arrangements
和 RcppAlgos
中几个高度优化包的出现,这不会花那么长时间(我是作者):
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
与 base R
中加载的 combn
相比:
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
。
我正在使用基本的 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's)更快更有效的方法?
我正在使用 Windows 7 64 位、FX 8320、16GB 内存。
正如@inscaven 指出的那样,实时紧缩来自 paste
。如果我们只需要生成所有 17 选 10 组合 15000 次,随着 R
、arrangements
和 RcppAlgos
中几个高度优化包的出现,这不会花那么长时间(我是作者):
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
与 base R
中加载的 combn
相比:
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
。