2013-01-05 13 views
10

Coś takiego mnie zaskoczyło: porównajmy dwa sposoby uzyskiwania zmiennych w dużej ramce danych z wieloma kolumnami: rozwiązaniem sapply i rozwiązaniem pętli for.Dlaczego przesyłanie zapytań o zmienne w data.frame jest względnie powolne?

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) 
) 

daje mi na moim komputerze,

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 

Co ciekawe, jeśli przekształcimy bigDF do listy sapply znów jest miły i szybki.

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)) 

daje mi

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 

Dlaczego są te operacje, zwłaszcza sapply, biorąc tyle już z data.frame w porównaniu do list? I czy istnieje bardziej idiomatyczne rozwiązanie?

+0

pamiętać, że jest wielkość zależny. Na moim komputerze, jeśli zmienię liczbę wierszy na 1000, to 'sapply (bigDF, class)' zajmuje mniej czasu niż 'for_soln (bigDF)'. –

Odpowiedz

13

edit: Stare proponowane rozwiązanie t3 <- sapply(1:ncol(bigDF), function(idx) class(bigDF[,idx])) zostało teraz zmienione na t3 <- sapply(1:ncol(bigDF), function(idx) class(bigDF[[idx]])). Jest jeszcze szybszy. Dzięki komentarzowi @Wojciech

Powodem, dla którego mogłem myśleć jest to, że konwertujesz dane.frame na listę niepotrzebnie. Ponadto, wyniki również nie są identyczne

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 

robi Rprof na sapply mówi, że wszystko jest czas spędzony na 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  

Można przyspieszyć operację, nie pytając o as.list.data.frame. Zamiast tego możemy po prostu zapytać o klasę każdej kolumny data.frame bezpośrednio, jak pokazano poniżej. Jest to dokładnie to samo, co w przypadku 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 

Różnica t3 jest, aby utworzyć listę długości 1000 każdy o długości 1. Natomiast w T1, swoją listę długości 1000, każdy o długości 10000.

+1

Ah, widzę - 'lapply' (który jest wywoływany przez' sapply') sprawdza element 'is.vector' na obiekcie przekazanym, które to obiekty klasy' data.frame' zawodzą. Z tego powodu obiekt jest konwertowany na listę z 'as.list' przed faktycznym wywołaniem' .Internal (lapply) ', co jest zrozumiałe powolne. Dzięki! –

+1

@Arunku powinieneś zmienić klasę (bigDF [, idx]) na klasę (bigDF [[idx]]) w "nowym-sapply" rozwiązanie –

+0

Domyślam się, że ostateczna odpowiedź na to: faworyzuję '* apply' wywołania formularza , np. 'lapply (1: length (df), function (i) { (df [[i]])})' podczas pracy z 'data.frame'. Chociaż (imo) czuje się bardziej przygięty. –

Powiązane problemy