我经常需要将函数应用于数据框/矩阵中的每对列,并以矩阵形式返回结果。现在我总是写一个循环来做这件事。例如,要创建一个包含相关性p值的矩阵,我写道:
df <- data.frame(x=rnorm(100),y=rnorm(100),z=rnorm(100))
n <- ncol(df)
foo <- matrix(0,n,n)
for ( i in 1:n)
{
for (j in i:n)
{
foo[i,j] <- cor.test(df[,i],df[,j])$p.value
}
}
foo[lower.tri(foo)] <- t(foo)[lower.tri(foo)]
foo
[,1] [,2] [,3]
[1,] 0.0000000 0.7215071 0.5651266
[2,] 0.7215071 0.0000000 0.9019746
[3,] 0.5651266 0.9019746 0.0000000
有效,但对于非常大的矩阵来说非常慢。我可以在R中编写一个函数(不要因为假设上述对称结果而减少一半的切割时间):
Papply <- function(x,fun)
{
n <- ncol(x)
foo <- matrix(0,n,n)
for ( i in 1:n)
{
for (j in 1:n)
{
foo[i,j] <- fun(x[,i],x[,j])
}
}
return(foo)
}
或使用Rcpp的函数:
library("Rcpp")
library("inline")
src <-
'
NumericMatrix x(xR);
Function f(fun);
NumericMatrix y(x.ncol(),x.ncol());
for (int i = 0; i < x.ncol(); i++)
{
for (int j = 0; j < x.ncol(); j++)
{
y(i,j) = as<double>(f(wrap(x(_,i)),wrap(x(_,j))));
}
}
return wrap(y);
'
Papply2 <- cxxfunction(signature(xR="numeric",fun="function"),src,plugin="Rcpp")
但即使在100个变量的相当小的数据集上,两者都很慢(我认为Rcpp函数会更快,但我想R和C ++之间的转换一直都会造成损失):
> system.time(Papply(matrix(rnorm(100*300),300,100),function(x,y)cor.test(x,y)$p.value))
user system elapsed
3.73 0.00 3.73
> system.time(Papply2(matrix(rnorm(100*300),300,100),function(x,y)cor.test(x,y)$p.value))
user system elapsed
3.71 0.02 3.75
所以我的问题是:
plyr
函数可以做到这一点?我找了它但却找不到它。答案 0 :(得分:16)
它不会更快,但您可以使用outer
来简化代码。它确实需要一个矢量化函数,所以我在这里使用Vectorize
来制作函数的矢量化版本,以获得两列之间的相关性。
df <- data.frame(x=rnorm(100),y=rnorm(100),z=rnorm(100))
n <- ncol(df)
corpij <- function(i,j,data) {cor.test(data[,i],data[,j])$p.value}
corp <- Vectorize(corpij, vectorize.args=list("i","j"))
outer(1:n,1:n,corp,data=df)
答案 1 :(得分:6)
我不确定这是否能以正确的方式解决您的问题,但请查看William Revelle的psych
包。 corr.test
返回具有相关系数,阻塞数,t检验统计量和p值的矩阵列表。我知道我一直都在使用它(而AFAICS你也是一名心理学家,所以它也可以满足你的需求)。写循环不是最优雅的方法。
library(psych)
corr.test(mtcars)
( k <- corr.test(mtcars[1:5]) )
Call:corr.test(x = mtcars[1:5])
Correlation matrix
mpg cyl disp hp drat
mpg 1.00 -0.85 -0.85 -0.78 0.68
cyl -0.85 1.00 0.90 0.83 -0.70
disp -0.85 0.90 1.00 0.79 -0.71
hp -0.78 0.83 0.79 1.00 -0.45
drat 0.68 -0.70 -0.71 -0.45 1.00
Sample Size
mpg cyl disp hp drat
mpg 32 32 32 32 32
cyl 32 32 32 32 32
disp 32 32 32 32 32
hp 32 32 32 32 32
drat 32 32 32 32 32
Probability value
mpg cyl disp hp drat
mpg 0 0 0 0.00 0.00
cyl 0 0 0 0.00 0.00
disp 0 0 0 0.00 0.00
hp 0 0 0 0.00 0.01
drat 0 0 0 0.01 0.00
str(k)
List of 5
$ r : num [1:5, 1:5] 1 -0.852 -0.848 -0.776 0.681 ...
..- attr(*, "dimnames")=List of 2
.. ..$ : chr [1:5] "mpg" "cyl" "disp" "hp" ...
.. ..$ : chr [1:5] "mpg" "cyl" "disp" "hp" ...
$ n : num [1:5, 1:5] 32 32 32 32 32 32 32 32 32 32 ...
..- attr(*, "dimnames")=List of 2
.. ..$ : chr [1:5] "mpg" "cyl" "disp" "hp" ...
.. ..$ : chr [1:5] "mpg" "cyl" "disp" "hp" ...
$ t : num [1:5, 1:5] Inf -8.92 -8.75 -6.74 5.1 ...
..- attr(*, "dimnames")=List of 2
.. ..$ : chr [1:5] "mpg" "cyl" "disp" "hp" ...
.. ..$ : chr [1:5] "mpg" "cyl" "disp" "hp" ...
$ p : num [1:5, 1:5] 0.00 6.11e-10 9.38e-10 1.79e-07 1.78e-05 ...
..- attr(*, "dimnames")=List of 2
.. ..$ : chr [1:5] "mpg" "cyl" "disp" "hp" ...
.. ..$ : chr [1:5] "mpg" "cyl" "disp" "hp" ...
$ Call: language corr.test(x = mtcars[1:5])
- attr(*, "class")= chr [1:2] "psych" "corr.test"
答案 2 :(得分:6)
92%的时间花在了cor.test.default
和它调用的例程上,所以通过简单地重写Papply
来试图获得更快的结果是绝望的(除了计算只有那些高于或低于对角假设您的函数在x
和y
中是对称的。
> M <- matrix(rnorm(100*300),300,100)
> Rprof(); junk <- Papply(M,function(x,y) cor.test( x, y)$p.value); Rprof(NULL)
> summaryRprof()
$by.self
self.time self.pct total.time total.pct
cor.test.default 4.36 29.54 13.56 91.87
# ... snip ...
答案 3 :(得分:2)
您可以使用mapply
,但正如其他答案所述,由于cor.test
大部分时间都在耗尽,因此它不太可能快得多。
matrix(mapply(function(x,y) cor.test(df[,x],df[,y])$p.value,rep(1:3,3),sort(rep(1:3,3))),nrow=3,ncol=3)
您可以通过使用对称假设并注意零对角线来减少工作量mapply
,例如
v <- mapply(function(x,y) cor.test(df[,x],df[,y])$p.value,rep(1:2,2:1),rev(rep(3:2,2:1)))
m <- matrix(0,nrow=3,ncol=3)
m[lower.tri(m)] <- v
m[upper.tri(m)] <- v