回复 第6楼 的 renkun-ken:
<br />
d0 <- data.frame(a=1:10000, b=10001:20000, c=5001:15000)<br />
ellap.1<-function(x,rep.col='c'){<br />
if (is.integer(rep.col)) {<br />
ll=rep.col<br />
}else{<br />
ll=match(rep.col,colnames(x))<br />
}<br />
if (!is.na(ll)){<br />
x1=x[,-ll]<br />
x2=as.numeric(as.matrix(x[,-ll]))<br />
m1 <- rep(x2,rep(x[,ll],dim(x)[2]-1))<br />
}<br />
dim(m1)=c(sum(x[,ll]),dim(x)[2]-1)<br />
m1<br />
}<br />
system.time(<br />
re<-ellap.1(d0)<br />
)<br />
user system elapsed<br />
0.529 1.336 1.864<br />
</p>
不过R真的有点问题啊,我直接产生一个sum(x[,ll])*(dim(x)[2]-1)这么大的numeric向量时间和这个差不多。与垃圾回收机制看来关系联系紧密。
<br />
system.time( re<-numeric((dim(x)[2]-1)*lll))<br />
user system elapsed<br />
0.800 1.035 1.835<br />
</p>
也就是说,其实大部分时间用来分配内存。一个基本等价的例子是这样的:
<br />
ellap.2<-function(x,rep.col='c'){<br />
x=data.matrix(x)<br />
if (is.integer(rep.col)) {<br />
ll=rep.col<br />
}else{<br />
ll=match(rep.col,colnames(x))<br />
}<br />
if (!is.na(ll)){<br />
lll=sum(x[,ll])<br />
re=numeric((dim(x)[2]-1)*lll)<br />
for (i in (1:dim(x)[2])[-ll]) re[((i-1)*lll+1):(i*lll)]=rep(x[,i],x[,ll])### MARK LINE<br />
}<br />
#dim(re)=c(sum(x[,ll]),2)<br />
re<br />
}<br />
也就是在对每一列操作时分配一次内存并执行清理,这样在矩阵行数比较大的时候会导致内存溢出,因为每次都要做一次内存的拷贝,不清理会很麻烦的:在编译型语言里这两种方式差不多,因为不要做额外的###MARK LINE那样的rep(x[,i],x[,ll])拷贝再释放(可以调用gc 去trace)。
ellap.1有一个大家都熟悉的名字: 向量化计算。用慢得出奇的Rcpp的话:</p>
<br />
sourceCpp('rep2.cpp',showOutput=T,verbose=T,rebuild=T)<br />
system.time(re<-rep2(data.matrix(x),3))<br />
user system elapsed<br />
1.135 1.354 2.488<br />
</p>
Cpp 代码
<br />
#include <Rcpp.h><br />
using namespace Rcpp;</p>
<p>NumericMatrix rep2(SEXP x, int id) {<br />
NumericMatrix xx(x);<br />
double ll=0.;<br />
int ind=0;<br />
int nrow=xx.nrow();<br />
int ncol=xx.ncol();<br />
id--;<br />
for (int i=0;i<nrow;i++) {<br />
ll+=xx(i,id);<br />
}<br />
NumericMatrix re(ll,ncol-1);<br />
for (int i=0;i<nrow;i++){<br />
for (int j=0;j<xx(i,id);j++){<br />
re(ind,0)=xx(i,0);<br />
re(ind,1)=xx(i,1);<br />
ind++;<br />
}<br />
}<br />
return (re);<br />
}<br />
</p>