• R语言
  • 按照数据框的某一行来重复数据框的某几行-如何高效完成

问题如题:

相似的帖子:

http://cos.name/cn/topic/109025#post-241149

http://cos.name/cn/topic/107224#post-233376

例子:

<br />
df <- data.frame(a= 1:3, b= 4:6, c= 10:12)</p>
<p>newdf <- apply(df[, c("a", "b")], 2, rep, time= df$c)</p>
<p>
</p>

如果行数多的时候(比如10000行)或者第三列的数值比较大的时候显然就慢了下来,

自己尝试了一下,改成矩阵后会更快些

<br />
mm <- matrix(0, nrow= sum(df$c), ncol= 2)<br />
mm[, 1] <- rep(df$a, df$c)<br />
mm[, 2] <- rep(df$b, df$c)<br />
</p>

请问有没有高效一点的办法?

模仿这个帖子:

http://stackoverflow.com/questions/20117458/replicate-rows-of-a-matrix-in-r

还有上面的cos里面的帖子

http://cos.name/cn/topic/107224#post-233376

<br />
mm <- as.matrix(df[, c("a", "b")])<br />
ind <- rep(1:nrow(mm), df$c)<br />
mm[ind, ]<br />
</p>

比较了上面的3种方法,第3个方法最快,希望高人再来指点下。[s:12]

要想速度快,还是得用data.table

<br />
df <- data.frame(a=1:10000, b=10001:20000, c=5001:15000)<br />
system.time(<br />
  apply(df[,c("a","b")], 2, rep, time=df$c)<br />
)</p>
<p>#user  system elapsed<br />
#2.870   0.843   3.736</p>
<p>mm <- as.matrix(df[, c("a", "b")])<br />
system.time(<br />
  (function(){ind <- rep(1:nrow(mm), df$c);mm[ind, ]})()<br />
)<br />
# user  system elapsed<br />
#1.455   0.459   1.916 </p>
<p>dt <- data.table(df)<br />
system.time(<br />
  dt[,list(a=rep(a,c),b=rep(a,c))]<br />
)<br />
#  user  system elapsed<br />
#  0.392   0.371   0.763<br />
</p>

想到一个不用data.table但是速度更快的办法。

首先以d0为例:

<br />
d0 <- data.frame(a= 1:3, b= 4:6, c= 2:4)<br />
m0 <- d0[c("a","b")]<br />
mm <- as.numeric(as.matrix(m0))<br />
m1 <- rep(mm,rep(d0$c,length(mm)/length(d0$c)))<br />
dim(m1) <- c(length(m1)/ncol(m0),ncol(m0))<br />
m1<br />
</p>

输出为

<br />
      [,1] [,2]<br />
 [1,]    1    4<br />
 [2,]    1    4<br />
 [3,]    2    5<br />
 [4,]    2    5<br />
 [5,]    2    5<br />
 [6,]    3    6<br />
 [7,]    3    6<br />
 [8,]    3    6<br />
 [9,]    3    6<br />
</p>

结果符合要求。在我的电脑上测试 data.table 计算的速度

<br />
df <- data.frame(a=1:10000, b=10001:20000, c=5001:15000)<br />
dt <- data.table(df)<br />
system.time(<br />
  dt[,list(a=rep(a,c),b=rep(a,c))]<br />
)</p>
<p>#   user  system elapsed<br />
#   0.25    0.31    0.56<br />
</p>

用我的方法来看速度:

<br />
d0 <- data.frame(a=1:10000, b=10001:20000, c=5001:15000)<br />
system.time({<br />
  m0 <- d0[c("a","b")]<br />
  mm <- as.numeric(as.matrix(m0))<br />
  m1 <- rep(mm,rep(d0$c,length(mm)/length(d0$c)))<br />
  dim(m1) <- c(length(m1)/ncol(m0),ncol(m0))<br />
})</p>
<p>#   user  system elapsed<br />
#   0.25    0.20    0.45<br />
</p>

回复 第5楼 的 beader:只不过代码比较ugly,没什么太大意义,纯属娱乐。

回复 第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>

回复3楼:方法中规中矩,赞一个[s:13]

回复4楼:利用向量和矩阵的关系来解决问题,确实够tricky[s:11]

回复7楼:代码很正规,学习了,不过关于内存溢出的那段有点没看懂,还要修炼!