10

そのような驚きがありました。class多くの列を持つビッグデータフレームで変数のesを取得する2つの方法、sapplyソリューションとforループソリューションを比較してみましょう。

bigDF <- as.data.frame( matrix( 0, nrow=1E5, ncol=1E3 ) )
library( microbenchmark )

for_soln <- function(x) {
  out <- character( ncol(x) )
  for( i in 1:ncol(x) ) {
    out[i] <- class(x[,i])
  }
  return( out )
}

microbenchmark( times=20,
  sapply( bigDF, class ),
  for_soln( bigDF )
)

私のマシンで、

Unit: milliseconds
                  expr       min        lq    median       uq      max
1      for_soln(bigDF)  21.26563  21.58688  26.03969 163.6544 300.6819
2 sapply(bigDF, class) 385.90406 405.04047 444.69212 471.8829 889.6217

興味深いことに、bigDFリストに変換するsapplyと、もう一度素晴らしくスピーディーになります。

bigList <- as.list( bigDF )
for_soln2 <- function(x) {
  out <- character( length(x) )
  for( i in 1:length(x) ) {
    out[i] <- class( x[[i]] )
  }
  return( out )
}

microbenchmark( sapply( bigList, class ), for_soln2( bigList ) )

私にくれます

Unit: milliseconds
                    expr      min       lq   median       uq      max
1     for_soln2(bigList) 1.887353 1.959856 2.010270 2.058968 4.497837
2 sapply(bigList, class) 1.348461 1.386648 1.401706 1.428025 3.825547

なぜこれらの操作は、特に、と比較してsapply非常に長い時間がかかるのですか?そして、もっと慣用的な解決策はありますか?data.framelist

4

1 に答える 1

13

edit:以前に提案されたソリューションt3 <- sapply(1:ncol(bigDF), function(idx) class(bigDF[,idx]))は、に変更されましたt3 <- sapply(1:ncol(bigDF), function(idx) class(bigDF[[idx]]))。そのさらに高速です。@Wojciechのコメントに感謝します

私が考えることができる理由は、data.frameを不必要にリストに変換しているためです。さらに、あなたの結果も同じではありません

bigDF <- as.data.frame(matrix(0, nrow=1E5, ncol=1E3))
t1 <- sapply(bigDF, class)
t2 <- for_soln(bigDF)

> head(t1)
    V1        V2        V3        V4        V5        V6 
"numeric" "numeric" "numeric" "numeric" "numeric" "numeric" 
> head(t2)
[1] "numeric" "numeric" "numeric" "numeric" "numeric" "numeric"

> identical(t1, t2)
[1] FALSE

Rprofオンにすることsapplyは、費やされたすべての時間がオンであることを示しますas.list.data.fraame

Rprof()
t1 <- sapply(bigDF, class)
Rprof(NULL)
summaryRprof()

$by.self
                     self.time self.pct total.time total.pct
"as.list.data.frame"      1.16      100       1.16       100    

を要求しないことで、操作を高速化できますas.list.data.frame。代わりに、data.frame以下に示すように、の各列のクラスを直接クエリすることができます。これはfor-loop、実際に達成することとまったく同じです。

t3 <- sapply(1:ncol(bigDF), function(idx) class(bigDF[[idx]]))
> identical(t2, t3)
[1] TRUE

microbenchmark(times=20, 
    sapply(bigDF, class),
    for_soln(bigDF),
    sapply(1:ncol(bigDF), function(idx) 
        class(bigDF[[idx]]))
)

Unit: milliseconds
        expr             min        lq       median       uq       max
1   for-soln (t2)     38.31545   39.45940   40.48152   43.05400  313.9484
2   sapply-new (t3)   18.51510   18.82293   19.87947   26.10541  261.5233
3   sapply-orig (t1) 952.94612 1075.38915 1159.49464 1204.52747 1484.1522

の違いt3は、それぞれ長さ1の長さ1000のリストを作成することです。一方、t1では、それぞれ長さ10000の長さ1000のリストを作成します。

于 2013-01-05T09:43:13.037 に答える