R-将一个函数应用于矩阵的每一行,并改变参数?

时间:2014-06-22 21:12:09

标签: r apply

我有一个带有两个参数的函数。第一个参数采用向量,第二个参数采用标量。我想将此函数应用于矩阵的每一行,但此函数每次都会使用不同的第二个参数。我试过以下,它没有用。我希望计算每一行的p.value,然后将p.value除以行号。我期望结果是一个向量,但我得到了一个矩阵。这是一个伪示例,但它说明了我的目的。

> foo = matrix(rnorm(100),ncol=20)
> f = function (x,y) t.test(x[1:10],x[11:20])$p.value/y
> goo = 1:5
> apply(foo,1,f,y=goo)
          [,1]      [,2]      [,3]       [,4]       [,5]
[1,] 0.9406881 0.6134117 0.5484542 0.11299535 0.20420786
[2,] 0.4703440 0.3067059 0.2742271 0.05649767 0.10210393
[3,] 0.3135627 0.2044706 0.1828181 0.03766512 0.06806929
[4,] 0.2351720 0.1533529 0.1371135 0.02824884 0.05105196
[5,] 0.1881376 0.1226823 0.1096908 0.02259907 0.04084157

以下for循环策略产生预期结果,对于实际数据预期会非常慢。

> res = numeric(5)
> for (i in 1:5){
    res[i]=f(foo[i,],i)
    }
> res
[1] 0.94068810 0.30670585 0.18281807 0.02824884 0.04084157

任何建议都将不胜感激!

2 个答案:

答案 0 :(得分:2)

如果你的真实目的与你的榜样相似,你可以对分工进行矢量化:

f <- function(x) t.test(x[1:10], x[11:20])$p.value
apply(foo, 1, f) / goo

根据评论,上述内容并不恰当。

在该示例的情况下,您可能会观察到返回矩阵的对角线是期望的结果:

f = function (x,y) t.test(x[1:10],x[11:20])$p.value/y
goo = 1:5
diag(apply(foo,1,f,y=goo))

除了在时间或空间上效率低下之外,这还有另一个问题。这是对y进行矢量化操作的结果,这对于示例来说是正确的。在这种情况下,前一种解决方案更好。所以我怀疑在你的实际问题中,你的操作没有矢量化。

有时候for循环确实是最好的答案。 apply函数族不是神奇的;它们仍然是循环。

这是一个sapply解决方案。它的时间不会超过for(可能也不会失败),但它没有很高的空间开销。我们的想法是应用行索引并使用它来提取foo行和goo的元素以传递给f

sapply(seq(nrow(foo)), function(i) f(foo[i,], goo[i]))

答案 1 :(得分:1)

f <- function (x,y) t.test(x[1:10],x[11:20])$p.value/y
f2 <- function(a, b){
    tt <- t.test(x = a[1:10], y = a[11:20])$p.value
    tt/b
}
f3 <- function() {
  res <- numeric(5)
  for (i in 1:5){
      res[i] <- f(foo[i,],i)
  }
  res
}
f4 <- function(x) t.test(x[1:10], x[11:20])$p.value

set.seed(101)
foo <- matrix(rnorm(100),ncol=20)
goo <- 1:5
library(rbenchmark)
benchmark(
     apply(foo, 1, f4) / goo,
     mapply(f,split(foo,row(foo)),goo),
     f2(foo,goo),
     f3(),replications=1000,
     sapply(seq(nrow(foo)), function(i) f(foo[i,], goo[i])),
     columns=c("test","replications","elapsed","relative"))

##                     test replications elapsed  relative
## 1   apply(foo, 1, f4)/goo         1000   1.581     5.528
## 3            f2(foo, goo)         1000   0.286     1.000
## 4                    f3()         1000   1.458     5.098
## 2             mapply(...)         1000   1.599     5.591
## 5             sapply(...)         1000   1.486     5.196

直接划分最好(但实际上并不适用);对于此示例,其他解决方案之间没有太大差异,但for循环优于sapply,优于mapply。你应该在一个更现实的例子中尝试这个,看看它将如何扩展你的问题。