R:通过参数传递给函数并使用apply而不是嵌套循环和递归索引失败

时间:2014-08-12 06:19:31

标签: r function nested-loops apply

我有两个列表列表。 humanSplitratSplithumanSplit具有::

形式的元素
> humanSplit[1]
$Fetal_Brain_408_AGTCAA_L001_R1_report.txt
   humanGene                            humanReplicate alignment RNAtype
66      DGKI Fetal_Brain_408_AGTCAA_L001_R1_report.txt         6     reg
68   ARFGEF2 Fetal_Brain_408_AGTCAA_L001_R1_report.txt         5     reg

如果您输入humanSplit[[1]],则会提供不带名称$Fetal_Brain_408_AGTCAA_L001_R1_report.txt

的数据

RatSplit也与humanSplit基本相似,但列顺序不同。我想将fisher's test应用于来自humanSplitratSplit的每个可能的重复配对。现在我定义了以下空载体,我将用它来存储我的渔夫测试的信息

humanReplicate <- vector(mode = 'character', length = 0)
ratReplicate <- vector(mode = 'character', length = 0)
pvalue  <- vector(mode = 'numeric', length = 0)

对于humanSplit和ratSplit的两个重复之间的Fisher测试,我定义了以下函数。在函数中我使用`geneList',这是一个通过读取文件而生成的data.frame,其格式为:

> head(geneList)
    human     rat
1 5S_rRNA 5S_rRNA
2 5S_rRNA 5S_rRNA

现在这里是main函数,我使用函数getGenetype,我已经在代码的其他部分定义了它。 xy也是整数:

fishertest <-function(x,y) {
  ratReplicateName <- names(ratSplit[x])
  humanReplicateName <- names(humanSplit[y])

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

  ## [here i do other manipulation with using already defined function
  ## getGenetype that is defined outside of this function and make things
  ## necessary to define following contingency table]

  contingencyTable <- matrix(c(HnRn,HnRy,HyRn,HyRy), nrow = 2)
  fisherTest <- fisher.test(contingencyTable)
  humanReplicate <- c(humanReplicate,humanReplicateName )
  ratReplicate <- c(ratReplicate,ratReplicateName )
  pvalue <- c(pvalue , fisherTest$p)

}

完成所有这些操作后,我会在eg中使用生成矩阵apply。在这里,我基本上尝试做类似于double for循环,然后使用fisher

eg <- expand.grid(i = 1:length(ratSplit),j = 1:length(humanSplit))
junk = apply(eg, 1, fishertest(eg$i,eg$j))

现在问题是,当我尝试运行时,它在尝试使用fishertest中的函数apply时出现以下错误

Error in humanSplit[[y]] : recursive indexing failed at level 3

Rstudio指出了以下问题:

mergedHumanData <-merge(geneList,humanSplit[[y]], by.x = "human", by.y = "humanGene")

最终,我想做以下事情:

result <- data.frame(humanReplicate,ratReplicate, pvalue ,alternative, Conf.int1, Conf.int2, oddratio)

我正在努力解决这些问题:

在定义fishertest函数时,我应该如何传递ratSplithumanSplit以及已定义的函数getGenetype

我应该如何在这里使用apply

非常感谢任何帮助。

1 个答案:

答案 0 :(得分:2)

预先:阅读?apply。此外,在搜索&#34; R应用教程&#34;时,谷歌的前三次点击是有用的摘录:onetwothree

fishertest()

中的错误

错误消息本身与apply无关。它得到的原因是因为你提供的论据实际上已经解决了。尝试单独执行eg$i,您将看到它正在返回一个向量:eg data.frame中的相应列。您将此向量作为i参数中的索引传递。您的函数错误的主要原因是因为双括号索引([[)仅适用于单个,而不是长度大于1的向量。这是生产/部署函数需要进行类型检查的一个很好的示例确保每个参数都是长度为1的数字;通常不需要快速代码,但会抓住这个错误。如果没有[[限制,您的功能可能会返回不正确的结果。 (我已被多次咬过了!)

BTW:您的代码在对pvalue等的范围访问中也是错误的。如果你让你的函数返回只是你需要的数字并将它聚合在 函数之外,你的生活就会简化。 (pvalue <- c(pvalue, ...)会在函数外部找到pvalue,但不会根据需要更新它。你打算将其写入函数中的目的是什么。在考虑编写这个函数时,尝试回答这个问题:&#34;如何将单个鼠标记录与单个人类记录进行比较?&#34; 只有在此之后才能正常且简单地工作而无需覆盖变量在父环境中,如果您尝试回答问题&#34;如何将此功能应用于所有对并聚合?&#34; 非常努力让您的功能更改其自身环境之外的任何内容。

apply()

中的错误

如果这些错误导致你的功能正常工作,你会收到来自apply的以下错误:

apply(eg, 1, fishertest(eg$i, eg$j))
## Error in match.fun(FUN) : 
##  'fishertest(eg$i, eg$j)' is not a function, character or symbol

当你在这个意义上调用apply时,它会解析第三个参数,在这个例子中,它会对它进行求值。由于只是对fishertest(eg$i, eg$j)的调用旨在返回data.frame行(从previous question推断),因此它会解析为此类,然后apply会看到类似于:

apply(eg, 1, data.frame(...))

现在您已经看到apply正在传递data.frame而不是函数。

第三个参数(FUN)需要是一个函数本身,它将第一个参数作为包含矩阵/ data.frame的row(1)或column(2)元素的向量。例如,请考虑以下设计示例:

eg <- data.frame(aa = 1:5, bb = 11:15)
apply(eg, 1, mean)
## [1]  6  7  8  9 10
# similar to your use, will not work; this error comes from mean not getting
# any arguments, your error above is because
apply(eg, 1, mean())
## Error in mean.default() : argument "x" is missing, with no default

意识到mean本身就是一个函数,而不是函数的返回值(还有更多的函数,但这个定义有效)。因为我们正在迭代eg的行(因为1),所以第一次迭代占用第一行并调用mean(c(1, 11)),返回6.这里的代码等价于{ {1}}会因为以下几个原因而失败:(1)因为mean()(c(1, 11))需要一个参数但没有得到,而且(2)无论如何,它都不会返回一个函数本身(在&#34;函数中)编程&#34;范例,在R中很容易,但对于大多数程序员来说并不常见。)

在此处的示例中,mean将接受单个参数,该参数通常是数字的向量。在您的情况下,您的函数mean需要两个参数(由我的previous answer to your question模板化),这不起作用。你有两个选择:

  1. 更改fishertest函数以接受单个向量作为参数,并从中解析索引号。以下两个选项都可以执行此操作:

    fishertest

    fishertest <- function(v) {
      x <- v[1]
      y <- v[2]
      ratReplicateName <- names(ratSplit[x])
      ## ...
    }
    

    第二个版本允许您继续使用fishertest <- function(x, y) { if (missing(y)) { y <- x[2] x <- x[1] } ratReplicateName <- names(ratSplit[x]) ## ... } 的手动形式,同时允许您逐字fishertest(1, 57)。非常可读,恕我直言。 (这里可以使用更好的错误检查和报告,我只提供MWE。)

  2. 编写一个匿名函数来获取向量并适当地将其拆分。这个匿名函数看起来像apply(eg, 1, fishertest)。这通常是针对那些不像上面的#1那样容易变换的函数,或者对于你不能或不想修改的函数。你可以将这个中间函数分配给一个变量(这使得它不再是匿名的,想象一下)并将该中介传递给function(ii) fishertest(ii[1], ii[2]),或者直接将它传递给apply,ala:

    apply

    有很多人选择命名函数有两个原因:(1)如果函数被多次使用,最好定义一次并重用; (2)它使.func <- function(ii) fishertest(ii[1], ii[2]) apply(eg, 1, .func) ## equivalently apply(eg, 1, function(ii) fishertest(ii[1], ii[2])) 行比包含复杂的多行函数定义更容易阅读。

  3. 作为附注,使用apply和家人有一些问题,如果你不理解,将会令人困惑。其中最重要的是,当您的函数返回向量时,从apply返回的矩阵将需要转置(使用apply),之后您仍然需要t()或者以其他方式聚合。

    这是使用rbind提供更易读的解决方案的一个领域。有几个教程显示它。如需快速介绍,请阅读this;有关ddply扮演重要角色的更深入讨论,请阅读JSS撰写的Hadley的Split, Apply, Combine Strategy for Data Analysis论文。