用于基于列索引设置矩阵条目的显式 for 循环的替代方法
Alternative to explicit for loop for setting matrix entries based on column indexes
如何在不使用 for 循环的情况下实现与下面相同的效果?
df1 = data.frame( val = c("a", "c", "c", "b", "e") )
m1 = matrix(0, nrow=nrow(df1), ncol=length( c("a", "b", "c", "d", "e") ) )
colnames(m1) = c("a", "b", "c", "d", "e")
for(i in 1:nrow(df1)){
m1[i, df1[i, 1] ] = 1 #For each entry in dataframe, mark the respective column as 1
}
这个
f<-function(m1,df) {
for(i in 1:nrow(df1))
m1[i, df1[i, 1] ] = 1
return(m1)
}
等同于
g<-function(m1,df) {
m1[cbind(seq_len(nrow(df)),df1[,1])]<-1
return(m1)
}
对于这个特定的例子,后者更快
> microbenchmark(f(m1,df1),g(m1,df1))
Unit: microseconds
expr min lq mean median uq max neval cld
f(m1, df1) 167.085 174.885 194.58999 185.969 200.132 342.379 100 b
g(m1, df1) 20.116 22.990 27.12403 24.222 27.300 158.053 100 a
但是请注意,
- 两者都在使用因子水平而不是字符列名称
- 你应该编写最清晰的代码而不是最快的代码,除非并且直到你确定真正的瓶颈
您的代码中有几处奇怪的地方。首先 df1 根本不需要,因为 data.frame 不应该存储一维向量。 val = c("a", "c", "c", "b", "e")
就够了。此外,正如其他人所建议的那样,有更紧凑(和一些更有效)的方法来实现同样的事情。但是,如果在您的实际问题中,您处理的数据量要大得多,并且您发现使用 for 循环更容易,那么您应该考虑使用 C++ 代码(而且它的 for 速度要快得多)。
这是我通过创建一个函数来比较 R 和 C++ for 的基准测试(我对 n = 100K 进行了测试)。
代码如下:
library(Rcpp)
library(rbenchmark)
cppFunction(
'int cppSum(int n) {
int s = 0;
for(int i = 0; i <= n; i++) {
s += i;
}
return s;
}'
)
rSum <- function(n) {
s = 0
for (i in c(1:n)) {
s = s + i
}
return(s)
}
n = 100000
benchmark(rSum(n), cppSum(n))
结果如下:
test replications elapsed relative user.self sys.self user.child sys.child
2 cppSum(n) 100 0.008 1.00 0.00 0 0 0
1 rSum(n) 100 2.790 348.75 2.79 0 0 0
您可以在 relative
列中注意到 R 函数比 C++ 函数慢 348.75 倍。在计算密集型进程中,使用 C++ 进行循环是一个很好的优化。曾经,我在其他循环中 运行 for。这将需要很长时间才能完成。当我用 C++ 更改 R 时,它在几分钟内就完成了。
[编辑]
此示例不能解决您的实际问题。最初的问题是寻找慢速 R for 循环的替代方法,因此我建议您选择更快的 for 循环,即 C++
for 循环。工作示例没有使用您的数据,因为它对于任何基准测试来说都太小了。相反,我使用具有 100K
迭代的循环,因此可以看到 2 个不同循环之间的差异。
如何在不使用 for 循环的情况下实现与下面相同的效果?
df1 = data.frame( val = c("a", "c", "c", "b", "e") )
m1 = matrix(0, nrow=nrow(df1), ncol=length( c("a", "b", "c", "d", "e") ) )
colnames(m1) = c("a", "b", "c", "d", "e")
for(i in 1:nrow(df1)){
m1[i, df1[i, 1] ] = 1 #For each entry in dataframe, mark the respective column as 1
}
这个
f<-function(m1,df) {
for(i in 1:nrow(df1))
m1[i, df1[i, 1] ] = 1
return(m1)
}
等同于
g<-function(m1,df) {
m1[cbind(seq_len(nrow(df)),df1[,1])]<-1
return(m1)
}
对于这个特定的例子,后者更快
> microbenchmark(f(m1,df1),g(m1,df1))
Unit: microseconds
expr min lq mean median uq max neval cld
f(m1, df1) 167.085 174.885 194.58999 185.969 200.132 342.379 100 b
g(m1, df1) 20.116 22.990 27.12403 24.222 27.300 158.053 100 a
但是请注意,
- 两者都在使用因子水平而不是字符列名称
- 你应该编写最清晰的代码而不是最快的代码,除非并且直到你确定真正的瓶颈
您的代码中有几处奇怪的地方。首先 df1 根本不需要,因为 data.frame 不应该存储一维向量。 val = c("a", "c", "c", "b", "e")
就够了。此外,正如其他人所建议的那样,有更紧凑(和一些更有效)的方法来实现同样的事情。但是,如果在您的实际问题中,您处理的数据量要大得多,并且您发现使用 for 循环更容易,那么您应该考虑使用 C++ 代码(而且它的 for 速度要快得多)。
这是我通过创建一个函数来比较 R 和 C++ for 的基准测试(我对 n = 100K 进行了测试)。
代码如下:
library(Rcpp)
library(rbenchmark)
cppFunction(
'int cppSum(int n) {
int s = 0;
for(int i = 0; i <= n; i++) {
s += i;
}
return s;
}'
)
rSum <- function(n) {
s = 0
for (i in c(1:n)) {
s = s + i
}
return(s)
}
n = 100000
benchmark(rSum(n), cppSum(n))
结果如下:
test replications elapsed relative user.self sys.self user.child sys.child
2 cppSum(n) 100 0.008 1.00 0.00 0 0 0
1 rSum(n) 100 2.790 348.75 2.79 0 0 0
您可以在 relative
列中注意到 R 函数比 C++ 函数慢 348.75 倍。在计算密集型进程中,使用 C++ 进行循环是一个很好的优化。曾经,我在其他循环中 运行 for。这将需要很长时间才能完成。当我用 C++ 更改 R 时,它在几分钟内就完成了。
[编辑]
此示例不能解决您的实际问题。最初的问题是寻找慢速 R for 循环的替代方法,因此我建议您选择更快的 for 循环,即 C++
for 循环。工作示例没有使用您的数据,因为它对于任何基准测试来说都太小了。相反,我使用具有 100K
迭代的循环,因此可以看到 2 个不同循环之间的差异。