【问题标题】:replacing for loops in a function with vector calculations to speed up R用向量计算替换函数中的 for 循环以加速 R
【发布时间】:2015-07-03 04:33:21
【问题描述】:

假设我在数据框d1 中有一些数据,描述了不同样本个体吃不同食物的频率,最后一列描述了这些食物是否凉快吃。数据的结构是这样的。

OTU.ID<- c('pizza','taco','pizza.taco','dirt')
s1<-c(5,20,14,70)
s2<-c(99,2,29,5)
s3<-c(44,44,33,22)
cool<-c(1,1,1,0)

d1<-data.frame(OTU.ID,s1,s2,s3,cool)
print(d1)
      OTU.ID s1 s2 s3 cool
1      pizza  5 99 44    1
2       taco 20  2 44    1
3 pizza.taco 14 29 33    1
4       dirt 70  5 22    0

我编写了一个函数,对于每个样本,s1:s3,消耗的凉爽食物的数量,以及消耗的食物总数。它在数据表的每一行上作为 for 循环运行(非常慢)。

cool.food.abundance<- function(food.table){
samps<-colnames(food.table)
#remove column names that are not sample names
samps<-samps[!samps %in% c("OTU.ID","cool")]

#create output vectors for for loop
    id<-c()
    cool.foods<-c()
    all.foods<-c()
    #run a loop that stores output ids and results as vectors
    for(i in 1:length(samps)){
        x<- samps[i]
        y1<-sum(food.table[samps[i]]*food.table$cool)
        y2<-sum(food.table[samps[i]])
        id<-c(id,x)
        cool.foods<-c(cool.foods,y1)
        all.foods<-c(all.foods,y2)
    }
    #save results as a data frame and return the data frame object
    results<-data.frame(id,cool.foods,all.foods)
    return(results)
}

因此,如果您运行此函数,您将获得一个新表,其中包含样本 ID、样本食用的冷食数量以及样本食用的食物总数。

cool.food.abundance(d1)
  id cool.foods all.foods
1 s1         39       109
2 s2        130       135
3 s3        121       143

如何用矢量计算替换这个 for 循环以加快速度?我真的希望该函数能够对使用 data.table 包中的 fread 函数加载的数据帧进行操作。

【问题讨论】:

    标签: r


    【解决方案1】:

    你可以试试

    library(data.table)#v1.9.5+
    dcast(melt(setDT(d1), id.var=c('OTU.ID', 'cool'))[,
             sum(value) ,.(cool, variable)], variable~c('notcool.foods',
           'cool.foods')[cool+1L], value.var='V1')[,
        all.foods:= cool.foods+notcool.foods][, notcool.foods:=NULL]
    #      variable cool.foods all.foods
    #1:       s1         39       109
    #2:       s2        130       135
    #3:       s3        121       143
    

    或者不使用dcast,我们可以总结结果(如@jeremycg 的帖子),因为只有两组

     melt(setDT(d1), id.var=c('OTU.ID', 'cool'), variable.name='id')[,
         list(all.foods=sum(value), cool.foods=sum(value[cool==1])) , id]
     #   id all.foods cool.foods
     #1: s1       109         39
     #2: s2       135        130
     #3: s3       143        121
    

    或者你可以使用base R

    nm1 <- paste0('s', 1:3)
    res <- t(addmargins(rowsum(as.matrix(d1[nm1]), group=d1$cool),1)[-1,])
    
    colnames(res) <- c('cool.foods', 'all.foods')
    res
     #   cool.foods all.foods
     #s1         39       109
     #s2        130       135
     #s3        121       143
    

    【讨论】:

    • 我收到以下错误:“错误:找不到函数“dcast”。”
    • @colin 我使用的是data.table 的开发版,否则你可以加载library(reshape2) 并获取dcast
    • 啊是的-最新版本的数据表。有没有办法不使用软件包安装程序无法使用的最新版本?我希望与可能不知道如何或想要在 R 界面之外安装软件包的其他用户分享这个脚本。
    • @colin 你可以安装library(reshape2),它也有dcast的功能。在这种特殊情况下,这也应该有效
    • @colin 输出是一个带有行名的矩阵。我们可以从中创建一个 data.frame。 d2 &lt;- data.frame(id=row.names(res), res); rownames(d2) &lt;- NULL
    【解决方案2】:

    我会这样做,reshape2dplyr

    library(reshape2)
    library(dplyr)
    d1 <- melt(d1, id = c("OTU.ID", "cool"))
    d1 %>% group_by(variable) %>% 
           summarise(all.foods = sum(value), cool.foods = sum(value[cool == 1]))
    

    【讨论】:

    • 也许您也可以将所有步骤放在链中并使用gather
    猜你喜欢
    • 2021-09-24
    • 1970-01-01
    • 2021-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多