成对二进制比较 - 优化R中的代码

时间:2018-04-19 20:41:09

标签: r performance for-loop pairwise

我有一个代表细菌模型基因结构的文件。每行代表一个模型。行是固定长度的二进制串,其中存在基因(1表示存在,0表示不存在)。我的任务是比较每对模型的基因序列,得到它们有多相似的分数,计算机是不相似矩阵。

一个文件中共有450个模型(行),共有250个文件。我有一个有效的代码,但是只需要一个文件就可以完成整个过程大约需要1.6小时。

#Sample Data    
Generation: 0
    [0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0]
    [1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1]
    [1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0]
    [0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0]
    [0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0]
    [1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0]

我的代码的作用:

  1. 读取文件
  2. 将二进制字符串转换为数据框架Gene,Model_1,Model_2,     Model_3,... Model_450
  3. 运行嵌套for循环以进行成对比较(仅限顶部 矩阵的一半) - 我取两个相应的列并添加 然后计算总和为2的位置(意味着存在 在两个模型中)
  4. 将数据写入文件
  5. 稍后创建矩阵
  6. 比较代码

    generationFiles = list.files(pattern = "^Generation.*\\_\\d+.txt$")
    
    start.time = Sys.time()
    
    for(a in 1:length(generationFiles)){
    
      fname = generationFiles[a]
    
      geneData = read.table(generationFiles[a], sep = "\n", header = T, stringsAsFactors = F)
    
      geneCount = str_count(geneData[1,1],"[1|0]")
    
      geneDF <- data.frame(Gene = paste0("Gene_", c(1:geneCount)), stringsAsFactors = F)
    
      #convert the string into a data frame
          for(i in 1:nrow(geneData)){
    
        #remove the square brackets
        dataRow = substring(geneData[i,1], 2, nchar(geneData[i,1]) - 1)
    
        #removing white spaces
        dataRow = gsub(" ", "", dataRow, fixed = T)
    
        #splitting the string 
        dataRow = strsplit(dataRow, ",")
    
        #converting to numeric
        dataRow = as.numeric(unlist(dataRow))
    
        colName = paste("M_",i,sep = "")
        geneDF <- cbind(geneDF, dataRow)
        colnames(geneDF)[colnames(geneDF) == 'dataRow'] <- colName
    
        dataRow <- NULL
      }
    
      summaryDF <- data.frame(Model1 = character(), Model2 = character(), Common = integer(),
                              Uncommon = integer(), Absent = integer(), stringsAsFactors = F)
    
      modelNames = paste0("M_",c(1:450))
    
      secondaryLevel = modelNames
    
      fileName = paste0("D://BellosData//GC_3//Summary//",substr(fname, 1, nchar(fname) - 4),"_Summary.txt")
    
      for(x in 1:449){
    
        secondaryLevel = secondaryLevel[-1]
    
        for(y in 1:length(secondaryLevel)){
    
          result = geneDF[modelNames[x]] + geneDF[secondaryLevel[y]]
    
          summaryDF <- rbind(summaryDF, data.frame(Model1 = modelNames[x],
                                                   Model2 = secondaryLevel[y],
                                                   Common = sum(result == 2),
                                                   Uncommon = sum(result == 1),
                                                   Absent = sum(result == 0)))
    
        }
    
    
      }
    
      write.table(summaryDF, fileName, sep = ",", quote = F, row.names = F)
      geneDF <- NULL
      summaryDF <- NULL
      geneData <-NULL
    
    }
    

    转换为矩阵

    maxNum = max(summaryDF$Common)
      normalizeData = summaryDF[,c(1:3)]
      normalizeData[c('Common')] <- lapply(normalizeData[c('Common')], function(x) 1 - x/maxNum)
    
      normalizeData[1:2] <- lapply(normalizeData[1:2], factor, levels=unique(unlist(normalizeData[1:2]))) 
    
      distMatrixN = xtabs(Common~Model1+Model2, data=normalizeData)
    
      distMatrixN = distMatrixN + t(distMatrixN)
    

    有没有办法让流程运行得更快?是否有更有效的方法进行比较?

1 个答案:

答案 0 :(得分:3)

此代码应该更快。嵌套循环在R中是噩梦慢。像rbind-ing这样的操作一次一行也是R编程中最差和最慢的想法。

生成450行,每行包含20个0,1的元素。

M = do.call(rbind, replicate(450, sample(0:1, 20, replace = T), simplify = F))

生成组合列数(450,2)

L = split(v<-t(utils::combn(450, 2)), seq(nrow(v))); rm(v)

应用您想要的任何比较功能。在这种情况下,每个行组合的相同位置的1的数量。如果要计算不同的指标,只需编写另一个函数(x),其中M[x[1],]是第一行,M[x[2],]是第二行。

O = lapply(L, function(x) sum(M[x[1],]&M[x[2],]))

代码需要约4秒,相当慢的2.6 Ghz Sandy Bridge

获取带有结果的干净data.frame,三列:第1行,第2行,两行之间的度量

data.frame(row1 = sapply(L, `[`, 1),
           row2 = sapply(L, `[`, 2),
           similarity_metric = do.call(rbind, O))

说实话,我并没有彻底梳理你的代码来复制你正在做的事情。如果这不是您正在寻找的(或无法修改以达到您所寻找的目的),请发表评论。