【问题标题】:How to rewrite a "sapply" command to increase performance?如何重写“sapply”命令以提高性能?
【发布时间】:2011-03-14 20:02:38
【问题描述】:

我有一个名为“d”的 data.frame,大约有 1,300,000 行和 4 列,另一个名为“gc”的 data.frame 有大约 12,000 行和 2 列(但请参阅下面的较小示例)。

d <- data.frame( gene=rep(c("a","b","c"),4), val=rnorm(12), ind=c( rep(rep("i1",3),2), rep(rep("i2",3),2) ), exp=c( rep("e1",3), rep("e2",3), rep("e1",3), rep("e2",3) ) )
gc <- data.frame( gene=c("a","b","c"), chr=c("c1","c2","c3") )

这是“d”的样子:

   gene         val ind exp
1     a  1.38711902  i1  e1
2     b -0.25578496  i1  e1
3     c  0.49331256  i1  e1
4     a -1.38015272  i1  e2
5     b  1.46779219  i1  e2
6     c -0.84946320  i1  e2
7     a  0.01188061  i2  e1
8     b -0.13225808  i2  e1
9     c  0.16508404  i2  e1
10    a  0.70949804  i2  e2
11    b -0.64950167  i2  e2
12    c  0.12472479  i2  e2

这里是“gc”:

  gene chr
1    a  c1
2    b  c2
3    c  c3

我想通过合并“gc”中与“d”的第 1 列匹配的数据,将第 5 列添加到“d”。目前我正在使用 sapply

d$chr <- sapply( 1:nrow(d), function(x) gc[ gc$gene==d[x,1], ]$chr )

但在真实数据上,它需要“非常长”的时间(我正在运行带有“system.time()”的命令,因为超过 30 分钟,它仍然没有完成)。

你知道我可以如何巧妙地重写这个吗?或者我应该考虑使用 plyr,也许使用“并行”选项(我的计算机上有四个内核)?在这种情况下,最好的语法是什么?

提前致谢。

【问题讨论】:

    标签: performance r plyr sapply


    【解决方案1】:

    我认为您可以将因子用作索引:

    gc[ d[,1], 2]
     [1] c1 c2 c3 c1 c2 c3 c1 c2 c3 c1 c2 c3
    Levels: c1 c2 c3
    

    与以下内容相同:

     sapply( 1:nrow(d), function(x) gc[ gc$gene==d[x,1], ]$chr )
     [1] c1 c2 c3 c1 c2 c3 c1 c2 c3 c1 c2 c3
    Levels: c1 c2 c3
    

    但要快得多:

    > system.time(replicate(1000,sapply( 1:nrow(d), function(x) gc[ gc$gene==d[x,1], ]$chr )))
       user  system elapsed 
       5.03    0.00    5.02 
    > 
    > system.time(replicate(1000,gc[ d[,1], 2]))
       user  system elapsed 
       0.12    0.00    0.13 
    

    编辑:

    扩展一下我的评论。 gc 数据框要求gene 的每个级别都需要一行,按级别顺序排列才能正常工作:

     d <- data.frame( gene=rep(c("a","b","c"),4), val=rnorm(12), ind=c( rep(rep("i1",3),2), rep(rep("i2",3),2) ), exp=c( rep("e1",3), rep("e2",3), rep("e1",3), rep("e2",3) ) )
    gc <- data.frame( gene=c("c","a","b"), chr=c("c1","c2","c3") )
    
    gc[ d[,1], 2]
     [1] c1 c2 c3 c1 c2 c3 c1 c2 c3 c1 c2 c3
    Levels: c1 c2 c3
    
    sapply( 1:nrow(d), function(x) gc[ gc$gene==d[x,1], ]$chr )
     [1] c2 c3 c1 c2 c3 c1 c2 c3 c1 c2 c3 c1
    Levels: c1 c2 c3
    

    但这并不难解决:

    levels(gc$gene) <- levels(d$gene) # Seems redundant as this is done right quite often automatically
    gc <- gc[order(gc$gene),]
    
    
    gc[ d[,1], 2]
     [1] c2 c3 c1 c2 c3 c1 c2 c3 c1 c2 c3 c1
    Levels: c1 c2 c3
    
    sapply( 1:nrow(d), function(x) gc[ gc$gene==d[x,1], ]$chr )
     [1] c2 c3 c1 c2 c3 c1 c2 c3 c1 c2 c3 c1
    Levels: c1 c2 c3
    

    【讨论】:

    • 谢谢,这正是我需要的。
    • 我也不是 tbh:) 但是有一个问题。即这里的gc[,1]必须和d[,1]的因子完全相同,每一层只有一行,每一层的顺序必须一致。诀窍是一个因子在数值上对应于 1,2...
    【解决方案2】:

    另一种解决方案在时间方面没有击败 Sasha 的方法,但更通用和可读性更高,是简单地 merge 两个数据帧:

    d <- merge(d, gc)
    

    我的系统较慢,所以这是我的时间安排:

    > system.time(replicate(1000,sapply( 1:nrow(d), function(x) gc[ gc$gene==d[x,1], ]$chr )))
       user  system elapsed 
      11.22    0.12   11.86 
    > system.time(replicate(1000,gc[ d[,1], 2])) 
       user  system elapsed 
       0.34    0.00    0.35 
    > system.time(replicate(1000, merge(d, gc, by="gene"))) 
       user  system elapsed 
       3.35    0.02    3.40 
    

    好处是您可以拥有多个键、对不匹配项的精细控制等。

    【讨论】:

      猜你喜欢
      • 2014-09-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-15
      相关资源
      最近更新 更多