用向量计算替换函数中的循环以加速R

时间:2015-07-02 20:08:28

标签: r

假设我在数据框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循环以加快速度?我真的希望该函数能够对fread包中加载了data.table函数的数据帧进行操作。

2 个答案:

答案 0 :(得分:5)

你可以尝试

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

答案 1 :(得分:3)

通过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]))