6

次のようなデータ フレームがあるとします。

Df <- data.frame(
    V1 = c(1,2,3,NA,5),
    V2 = c(1,2,NA,4,5),
    V3 = c(NA,2,NA,4,NA)
)

ここで、2 つの変数の組み合わせごとに有効な観測値の数を数えたいと思います。そのために、関数を書きました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 のトリックを使用して、同じデータ フレームを返す関数を作成しました。以下の私の答えのコード。

4

3 に答える 3

9

以下はわずかに高速です。

x <- !is.na(Df)
t(x) %*% x

#       test elapsed relative
#    cor(Df)  12.345 1.000000
# t(x) %*% x  20.736 1.679708
于 2012-02-23T12:58:08.247 に答える
3

Vincent のものは本当にエレガントに見えると思いました。私の 2 年生の 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
于 2012-02-23T15:46:38.203 に答える
2

Vincentの素敵なトリックとlower.tri()DWinの追加の提案に基づいて、元の出力と同じ出力(つまり、データフレーム)を提供し、はるかに高速に実行される次の関数を思いつきました。

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を指定しました。ここに示すように、時間の増加は、小さなデータフレームを使用した場合よりもはるかに劇的です。

于 2012-02-23T16:19:17.540 に答える