2017-11-29 22 views
3

내가 병합 할 tibbles 목록 (길이 = 5000)이 있습니다. 그들은 모두 동일한 열을 가지고 있으므로 dplyr::bind_rows을 사용하여 병합을 고려했습니다. 그것의 얼굴에 추가 된 tibble마다 행을 바인딩하는 것은 매우 빠릅니다. 그러나 더 많은 tibble이 추가됨에 따라 실행 시간은 선형 적으로 증가하는 대신 기하 급수적으로 증가합니다. 인터넷 검색을 수행 한 결과, 여기에있는 버그와 매우 비슷합니다 : https://github.com/tidyverse/dplyr/issues/1396. 버그가 bind_rows 내부에서 고쳐 졌다고해도, 나는 아직도 tibble 당 경과 시간이 기하 급수적으로 증가하는 것을보고있다.dplyr bind_rows 실행 시간 지수가

library(foreach) 
library(tidyverse) 
set.seed(123456) 
tibbles <- foreach(i = 1:200) %do% { 
       tibble(a = rnorm(10000), 
        b = rep(letters[1:25], 400), 
        c = rnorm(10000)) 
} 
times <- foreach(i = 1:200) %do% { 
      system.time(tibbles[1:i] %>% 
          purrr::reduce(bind_rows)) 
} 

times %>% 
    map_dbl(.f = ~.x[3]) %>% 
    plot(ylab = "time [s] per added tibble") 

왜 그런지, 어떻게 해결할 수 있습니까?

감사합니다. abhiieor의 의견을 확장하려면

elapsed_time

+1

행 단위로 증가하는 데이터 프레임이 비효율적입니다. 'data.table'는 당신을 여기서 더 잘 도울 수 있습니다. – abhiieor

+2

필자는 오해의 소지가 있습니다 만,'purrr : reduce' 대신 직접 tibble에'bind_rows'를 사용할 수 없습니까? 그 변화에 따라 상황이 나에게 선형으로 보입니다. – aosmith

+0

아니, 나는 오해 한 사람이었습니다. 당신이 그것을리스트에서 사용할 수 있는지 몰랐습니다. 고마워. 일이 전혀 끝나지 않았다. – biomiha

답변

1

내 생각 엔 당신이 rbind를 호출 할 때마다, R은 컬럼의 새로운 세트를 할당을 통해 데이터를 복사하는 것입니다. 이것은 시간의 2 차적인 증가로 이어질 것입니다. 상황에 그냥 bind_rows를 사용하는 것이 좋습니다, aosmith 노트로, 비록

system.time(tibbles[1:200] %>% purrr::reduce(bind_rows)) 
#> user system elapsed 
#> 4.888 2.013 6.928 

:

system.time({ 
n <- vapply(tibbles, nrow, 0) 
ntot <- sum(n) 
cols <- list(a = numeric(ntot), b = character(ntot), c = numeric(ntot)) 

off <- 0 
for (i in seq_along(tibbles)) { 
    ix <- off + seq_len(n[[i]]) 
    for (j in seq_along(cols)) { 
     cols[[j]][ix] <- tibbles[[i]][[j]] 
    } 
    off <- off + n[[i]] 
} 

result <- as_tibble(cols) 
}) 
#> user system elapsed 
#> 0.073 0.012 0.085 

purrr::reduce 접근 방식과 비교 :

대신 열을 미리 할당 시도

system.time(result <- bind_rows(tibbles)) 
#> user system elapsed 
#> 0.039 0.005 0.044 
0

, 나는 data.table에서 rbindlist 또는 rbind 도움이 될 수 있다고 생각합니다. 일련의 tibbles (또는 data.tables) 행을 바인딩하려고한다고 가정하면이 코드는 거의 즉시 적용됩니다.

time <- proc.time() 

data_tables <- foreach(i = 1:200) %do% { 
    data.table(a = rnorm(10000), 
     b = rep(letters[1:25], 400), 
     c = rnorm(10000)) 
} 

all_tables <- rbindlist(data_tables) 

end_time <- proc.time() - time