应用sapply或其他apply函数而不是嵌套for循环的数据帧列表

时间:2014-08-09 22:44:23

标签: r list for-loop apply sapply

我有两个数据框/数据列表'humanSplit and ratSplit`,它们的格式为

> ratSplit$Kidney_F_GSM1328570
  ratGene        ratReplicate alignment RNAtype
1    Crot Kidney_F_GSM1328570         7     REV
2    Crot Kidney_F_GSM1328570        12     REV
3    Crot Kidney_F_GSM1328570         4     REV

> humanSplit$Fetal_Brain_408_AGTCAA_L009_R1_report.txt
   humanGene                            humanReplicate alignment RNAtype
53     ZFP28 Fetal_Brain_408_AGTCAA_L009_R1_report.txt         5     reg
55     RC3H1 Fetal_Brain_408_AGTCAA_L009_R1_report.txt         9     reg
56     IFI27 Fetal_Brain_408_AGTCAA_L009_R1_report.txt         4     reg

下面使用的另一个文件是formList:

ABAT,Abat
ABCA1,Abca1
ABCA12,Abca12
ABCA2,Abca2
ABCA3,Abca17
ABCA4,Abca4
ABCA5,Abca5

现在我想在一些数据操作之后对ratSplithumanSplit之间的所有元素对组合进行Fisher精确测试。最终想在csv文件中写出fisher测试的结果。现在我正在做双循环。但我想知道如何使用sapply或其他相关的东西来提高效率。

目前我正在做以下事情:这里我首先制作一个data.frame result,我在每一步中存储/追加从Fisher测试中得到的所有信息。最后,当整个循环完成后,我在result文件中写下最终的csv。我的理解是使用sapply我需要将循环内部转换为函数然后调用sapply。但我不确定优化它的最佳方法是什么。任何帮助将不胜感激

result <- data.frame(humanReplicate = "human_replicate", ratReplicate = "rat_replicate", pvalue = "p-value", alternative = "alternative_hypothesis", 
                     Conf.int1 = "conf.int1", Conf.int2 ="conf.int2", oddratio = "Odd_Ratio")
for(i in 1:length(ratSplit)) {
  for(j in 1:length(humanSplit)) {
    ratReplicateName <- names(ratSplit[i])
    humanReplicateName <- names(humanSplit[j])

    #merging above two based on the one-to-one gene mapping as in geneList defined above.
    mergedHumanData <-merge(geneList,humanSplit[[j]], by.x = "human", by.y = "humanGene")
    mergedRatData <- merge(geneList, ratSplit[[i]], by.x = "rat", by.y = "ratGene")

    mergedHumanData <- mergedHumanData[,c(1,2,4,5)] #rearrange column
    mergedRatData <- mergedRatData[,c(2,1,4,5)]  #rearrange column
    mergedHumanRatData <- rbind(mergedHumanData,mergedRatData) #now the columns are "human", "rat", "alignment", "RNAtype"

    agg <- aggregate(RNAtype ~ human+rat, data= mergedHumanRatData, FUN=getGeneType) #agg to make HmYn form
    HmRnTable <- table(agg$RNAtype) #table of HmRn ie RNAtype in human and rat.

    #now assign these numbers to variables HmYn. Consider cases when some form of HmRy is not present in the table. That's why
    #is.integer0 function is used
    HyRy <- ifelse(is.integer0(HmRnTable[names(HmRnTable) == "HyRy"]), 0, HmRnTable[names(HmRnTable) == "HyRy"][[1]])
    HnRn <- ifelse(is.integer0(HmRnTable[names(HmRnTable) == "HnRn"]), 0, HmRnTable[names(HmRnTable) == "HnRn"][[1]])
    HyRn <- ifelse(is.integer0(HmRnTable[names(HmRnTable) == "HyRn"]), 0, HmRnTable[names(HmRnTable) == "HyRn"][[1]])
    HnRy <- ifelse(is.integer0(HmRnTable[names(HmRnTable) == "HnRy"]), 0, HmRnTable[names(HmRnTable) == "HnRy"][[1]])

    contingencyTable <- matrix(c(HnRn,HnRy,HyRn,HyRy), nrow = 2)

    fisherTest <- fisher.test(contingencyTable) 
    newLine <- data.frame(t(c(humanReplicate = humanReplicateName, ratReplicate = ratReplicateName, pvalue = fisherTest$p,
                              alternative = fisherTest$alternative, Conf.int1 = fisherTest$conf.int[1], Conf.int2 =fisherTest$conf.int[2], 
                              oddratio = fisherTest$estimate[[1]])))


    result <-rbind(result,newLine)
  }
}

write.table(result, file = "newData5.csv", row.names = FALSE, append = FALSE, col.names = TRUE, sep = ",")

1 个答案:

答案 0 :(得分:1)

我们很难对此进行测试,因为我们错过了geneList,但我推断代码工作正常,所以您只想加快速度。以下是一些帮助提示:

  1. 不要像这样定义result。通过将每列的第一个条目设置为列名的字符串,可以确保将所有后续条目强制转换为字符串。虽然这并不一定会让你感到厌烦,因为无论如何你最终还是写了一个CSV,这是一个糟糕的形式。如果你打算将它读回R,它会咬你,因为第一行将用作列名,但第二行都是字符串,强制后续数据也是字符串。 (然后你必须清理你自己的数据,浪费。)

  2. 在脚本结束时,您致电rbind。这可能会好一段时间,但重复调用rbind将导致每次都复制整个data.frame。随着附加更多行,这将导致代码中的无意义的减速。这可以通过下面列出的两种方法之一来解决。

  3. 由于你每次使用names(HmRnTable) == "HyRy"两次,我的技术是首先将它保存到一个向量(如果使用which(...),则为标量),然后在子集的子集中使用此变量HmRnTable。它可能会加快一点,但也可能使代码更容易阅读。实际上,您可以将这四个任务中的每一个缩短为(未经测试):

    idx <- is.integer0(HmRnTable[names(HmRnTable) == 'HyRy'])
    HyRy <- HmRnTable[idx][[1]]
    HyRy[idx] <- 0
    ## repeat for HyRn, HnRy, and HnRn
    
  4. 我强烈建议你把大部分代码放在一个带有两个参数(ij)或四个参数(list1,index1,list2)的函数中,index2)。 (你所做的取决于你对变量范围引用有多少强迫症。)我假设该函数将从fisher.test字面上返回结果而不进行按摩。这将使得在创建函数时更容易进行测试,并且稍后将在此脚本中包含。我将下面的myfisher(i,j)引用功能。

  5. 我推断你要进行大量的比较(因为每次迭代都不应该花那么长时间)。 @ Floo0关于outer的评论可以正常工作,expand.grid也可以。无论哪种方式,您都将list1的每个元素与list2的每个元素进行比较。如果您从:

    开头
    (eg <- expand.grid(i = 1:length(ratSplit),
                       j = 1:length(humanSplit)))
    ##    i j
    ## 1  1 1
    ## 2  2 1
    ## 3  3 1
    ## 4  4 1
    ## ...
    

    这为我们提供了一个简单的data.frame,我们可以使用apply。但老实说,在这种情况下,我喜欢ddply的优雅,因为它可以轻松地从一个data.frame 一个data.frame。 / p>

    library(plyr)
    ret <- ddply(eg, .(i, j), function(df) {
        with(myfisher(df$i, df$j),
             data.frame(pv = p.value, ci1 = conf.int[1], ci2 = conf.int[2],
                        alt = alternative))
    })
    

    请注意,我明确没有包含humanReplicateNameratReplicateName,因为这些可以在ddply之前(或之后,引用ret$而不是eg$)添加:< / p>

    eg$ratReplicateName <- names(ratSplit[ eg$i ])
    eg$humanReplicateName <- names(humanSplit[ eg$i ])
    

    这些名字也会神奇地出现在输出中。较少处理循环内部。

    到目前为止,这将产生一个data.frame,然后您可以将其保存为CSV。

    我将再提出一项可能过度的建议,具体取决于此项运行的时间长短。我有时发现我长时间运行的脚本被打断了,也许是因为我必须在运行中调整一些东西;我发现了一个bug;或者如果计算机必须重新启动没有简单的方法允许中流继续,但我已经采用了一些技术来缓解这种情况。

  6. 使用不假定返回值类型的ddply而不是d_ply。而不是返回(单行)data.frame,立即将该行(带或不带标题,您的调用)保存到文件中。虽然下面的代码不是真正的&#34; atomic&#34;因此可能会出现 的竞争条件,它足以满足我们的大部分需求:

    library(plyr)
    savedir <- './output'
    ret <- ddply(eg, .(i, j), function(df) {
        fn <- file.path(savedir, sprintf('%s-%s.csv', df$i, df$j))
        if (! file.exists(fn)) {
            ret <- with(myfisher(df$i, df$j),
                        data.frame(pv = p.value, ci1 = conf.int[1], ci2 = conf.int[2],
                                   alt = alternative))
            write.table(ret, file = fn, sep = ",", append = FALSE, 
                        row.names = FALSE, col.names = TRUE)
        }
    })
    

    这样做的一个优点是,如果/当它被中断时,您需要做的 most 就是查看&#34; ./ output /&#34;中的所有文件。 ,删除0字节的文件,然后重新运行,它只会在丢失的文件上执行。哦,它变得更好。

  7. 并行化。如果你需要走得这么远(有些功能看不到其他功能),你可以在系统上使用多个内核。你可以这样做:

    library(parallel)
    cl <- makeCluster(detectCores() - 1) # I like to keep one free
    clusterEvalQ(cl, {
        load('myTwoLists.rda') # with ratSplit and humanSplit variables
        source('mycode.R') # with myfisher(i,j)
        ## any libraries you may want/need to add, if you get more advanced
    })
    eg <- expand.grid(i = 1:length(ratSplit),
                      j = 1:length(humanSplit))
    eg$ratReplicateName <- names(ratSplit[ eg$i ])
    eg$humanReplicateName <- names(humanSplit[ eg$i ])
    ign <- parApply(cl, eg, 1, function(r) {
        i <- r[1] ; j <- r[2]
        fn <- file.path(savedir, sprintf('%s-%s.csv', i, j)
        if (! file.exists(fn)) {
            ret <- with(myfisher(i, j),
                        data.frame(ratName = r[3], humanName = r[4],
                                   pv = p.value, ci1 = conf.int[1], ci2 = conf.int[2],
                                   alt = alternative))
            write.table(ret, file = fn, sep = ",", append = FALSE, 
                        row.names = FALSE, col.names = TRUE)
        }
    })
    stopCluster(cl)
    

    请注意,我们不再将该行作为data.frame引用。如果哈德利的代码本地并行工作,我会喜欢它。 (据我所知,确实如此,我还没找到它!) 编辑:我以前使用parallel的项目都没有使用ddply,反之亦然,所以我从未玩过使用ddply(..., .parallel = TRUE)的{​​{1}}。

  8. 警告Emptor :此代码尚未在此上下文中进行测试,但它几乎是从工作代码中复制/粘贴的。由于编辑,我可能是一个接一个或错过了一个paren。希望它有所帮助!