说我有这样的数据框:
Df <- data.frame(
V1 = c(1,2,3,NA,5),
V2 = c(1,2,NA,4,5),
V3 = c(NA,2,NA,4,NA)
)
现在我想计算两个变量的每个组合的有效观察数。为此,我写了一个函数sharedcount
:
sharedcount <- function(x,...){
nx <- names(x)
alln <- combn(nx,2)
out <- apply(alln,2,
function(y)sum(complete.cases(x[y]))
)
data.frame(t(alln),out)
}
这给出了输出:
> sharedcount(Df)
X1 X2 out
1 V1 V2 3
2 V1 V3 1
3 V2 V3 2
一切都很好,但功能本身在大数据帧上需要很长时间(600个变量和大约10000个观测值)。我有一种感觉,我正在监督一种更简单的方法,特别是因为cor(...,use ='pairwise')的运行速度要快得多,而且必须做类似的事情:
> require(rbenchmark)
> benchmark(sharedcount(TestDf),cor(TestDf,use='pairwise'),
+ columns=c('test','elapsed','relative'),
+ replications=1
+ )
test elapsed relative
2 cor(TestDf, use = "pairwise") 0.25 1.0
1 sharedcount(TestDf) 1.90 7.6
任何提示都表示赞赏。
注意:使用Vincent的技巧,我编写了一个返回相同数据框的函数。代码在我的答案中。
答案 0 :(得分:8)
以下内容稍快一些:
x <- !is.na(Df)
t(x) %*% x
# test elapsed relative
# cor(Df) 12.345 1.000000
# t(x) %*% x 20.736 1.679708
答案 1 :(得分:3)
我认为Vincent看起来非常优雅,更不用说比我的二年级for循环更快,除了它似乎需要我在下面添加的提取步骤。这只是与数据帧一起使用时apply方法的大量开销的一个例子。
shrcnt <- function(Df) {Comb <- t(combn(1:ncol(Df),2) )
shrd <- 1:nrow(Comb)
for (i in seq_len(shrd)){
shrd[i] <- sum(complete.cases(Df[,Comb[i,1]], Df[,Comb[i,2]]))}
return(shrd)}
benchmark(
shrcnt(Df), sharedcount(Df), {prs <- t(x) %*% x; prs[lower.tri(prs)]},
cor(Df,use='pairwise'),
columns=c('test','elapsed','relative'),
replications=100
)
#--------------
test elapsed relative
3 { 0.008 1.0
4 cor(Df, use = "pairwise") 0.020 2.5
2 sharedcount(Df) 0.092 11.5
1 shrcnt(Df) 0.036 4.5
答案 2 :(得分:2)
基于Vincent的可爱技巧和DWin的额外lower.tri()
建议,我想出了以下函数,它给出了与原始函数相同的输出(即数据框),并运行了很多更快:
sharedcount2 <- function(x,stringsAsFactors=FALSE,...){
counts <- crossprod(!is.na(x))
id <- lower.tri(counts)
count <- counts[id]
X1 <- colnames(counts)[col(counts)[id]]
X2 <- rownames(counts)[row(counts)[id]]
data.frame(X1,X2,count)
}
请注意crossprod()
的使用,因为与%*%
相比,它会有一些小的改进,但它完全相同。
时间安排:
> benchmark(sharedcount(TestDf),sharedcount2(TestDf),
+ replications=5,
+ columns=c('test','replications','elapsed','relative'))
test replications elapsed relative
1 sharedcount(TestDf) 5 10.00 90.90909
2 sharedcount2(TestDf) 5 0.11 1.00000
注意:我在问题中提供了TestDf,因为我注意到时间因数据帧的大小而异。如此处所示,与使用小型数据框进行比较相比,时间的增加要大得多。