binding
rbind(), bind_row(), do.call(), rbindlist(),
https://onesixx.com/lapply/
https://onesixx.com/lapply-vs-docall/
예제데이터
DD <- data.table(iris) names(DD) <- c("SL","SW","PL","PW","Species") # SL SW PL PW Species # 1: 5.1 3.5 1.4 0.2 setosa # 2: 4.9 3.0 1.4 0.2 setosa # 3: 4.7 3.2 1.3 0.2 setosa # 4: 4.6 3.1 1.5 0.2 setosa # 5: 5.0 3.6 1.4 0.2 setosa # --- # 146: 6.7 3.0 5.2 2.3 virginica # 147: 6.3 2.5 5.0 1.9 virginica # 148: 6.5 3.0 5.2 2.0 virginica # 149: 6.2 3.4 5.4 2.3 virginica # 150: 5.9 3.0 5.1 1.8 virginica
ll <- list(a=c(1:6), b=c(1,6)) # $a # [1] 1 2 3 4 5 6 # $b # [1] 1 6 ld <- list(a=DD[1:3,], b=DD[4:6,]) # $a # SL SW PL PW Species # 1: 5.1 3.5 1.4 0.2 setosa # 2: 4.9 3.0 1.4 0.2 setosa # 3: 4.7 3.2 1.3 0.2 setosa # # $b # SL SW PL PW Species # 1: 4.6 3.1 1.5 0.2 setosa # 2: 5.0 3.6 1.4 0.2 setosa # 3: 5.4 3.9 1.7 0.4 setosa
1. rbind() cbind()
- Input: vector 또는 data.frame
- Output: vector/matrix 또는 data.frame
> rbind(a=c(1:6), b=c(1,6)) # rbind for vector # [,1] [,2] [,3] [,4] [,5] [,6] # a 1 2 3 4 5 6 # b 1 6 1 6 1 6 > rbind(a=DD[1:3,], b=DD[4:6,]) # rbind for data.table # SL SW PL PW Species # 1: 5.1 3.5 1.4 0.2 setosa # 2: 4.9 3.0 1.4 0.2 setosa # 3: 4.7 3.2 1.3 0.2 setosa # 4: 4.6 3.1 1.5 0.2 setosa # 5: 5.0 3.6 1.4 0.2 setosa # 6: 5.4 3.9 1.7 0.4 setosa
rbind와 cbind는 list형식을 받으면 결과는 나오지만, 원하는 값이 아니다.
> rbind(ll) # a b # ll Integer,6 Numeric,2 > rbind(ld) # a b # ld List,5 List,5 > rbind(ll, ll) # a b # ll Integer,6 Numeric,2 # ll Integer,6 Numeric,2 > rbind(ld, ld) # a b # ld List,5 List,5 # ld List,5 List,5
bind_rows(), bind_cols()
dplyr 제공함수로써, 여러개의 data.frame을 row/column기준으로 merge할때 사용한다.
기본적으로 rbind, cbind와 결과는 같지만 빠르고, vector에는 적용이 안됨
bind_rows()는 column갯수가 다른 경우에도 NA처리를 통해 가능하고,
bind_cols()는 합치려는 data.frame의 컬럼명이 같은 경우, 번호를 붙여 구분해 준다.
plyr::rbind.fill()
2. do.call(f, X)
- Input: list
- Output: vector/matrix
do.call("rbind", ll) do.call("rbind", ld)
- do.call의 args로 list만을 받는다(rbindlist도 list만을 받지만, 그 내용이 data.frame이어야한다.)
- do.call내의 함수는 list 인덱스 전체에 적용합니다.
- 결과는 vector/ matrix 형태
> ll <- list(a=c(1:6), b=c(1,6)) # $a # [1] 1 2 3 4 5 6 # $b # [1] 1 6 > do.call("rbind", ll) # matrix # [,1] [,2] [,3] [,4] [,5] [,6] # a 1 2 3 4 5 6 # b 1 6 1 6 1 6 > rbindlist(ll) # Error in rbindlist(ll) : # Item 1 of input is not a data.frame, data.table or list > bind_rows(ll) # Error: Tibble columns must have compatible sizes. # * Size 2: Column `b`. # * Size 6: Column `a`. # ℹ Only values of size one are recycled. # Run `rlang::last_error()` to see where the error occurred. > do.call("cbind",transpose(ll)) # [,1] [,2] [,3] [,4] [,5] [,6] # [1,] 1 2 3 4 5 6 # [2,] 1 6 NA NA NA NA
> ld <- list(a=DD[1:3,], b=DD[51:53,]) # $a # SL SW PL PW Species # 1: 5.1 3.5 1.4 0.2 setosa # 2: 4.9 3.0 1.4 0.2 setosa # 3: 4.7 3.2 1.3 0.2 setosa # $b # SL SW PL PW Species # 1: 7.0 3.2 4.7 1.4 versicolor # 2: 6.4 3.2 4.5 1.5 versicolor # 3: 6.9 3.1 4.9 1.5 versicolor > do.call("rbind", ld) #data.table # SL SW PL PW Species # 1: 5.1 3.5 1.4 0.2 setosa # 2: 4.9 3.0 1.4 0.2 setosa # 3: 4.7 3.2 1.3 0.2 setosa # 4: 7.0 3.2 4.7 1.4 versicolor # 5: 6.4 3.2 4.5 1.5 versicolor # 6: 6.9 3.1 4.9 1.5 versicolor > rbindlist(ld) # same > bind_rows(ld) # same for dplyr
참고> do.call() vs. lapply()
do.call은 Input으로 받은 list에 대해 전체적으로 한번만 mean 함수를 적용, output은 vector
lapply는 Input으로 받은 vector를 각각 list화하여, 각각 list갯수만큼 mean함수 적용, output은 list
> mean(c(1, 2, 4, 1, 6)) > do.call(mean, list(c(1, 2, 4, 1, 6))) #same # [1] 2.8 > mean(c(1, 2, 4, 1, NA), na.rm=T) > do.call(mean, list(c(1, 2, 4, 1, NA), na.rm=T)) #same # [1] 2
plyr::ldply() 팩키지
plyr::ldply(mergeData, rbind)
3. rbindlist()
- Input: list (그 list의 element는 dataframe 형식 이어야한다.)
- Output: data.frame
rbindlist()는 input인수로 list형식만 받을 수 있는데,
> rbindlist(ll) # Error in rbindlist(ll) : # Item 1 of input is not a data.frame, data.table or list > do.call("rbind", ll) # [,1] [,2] [,3] [,4] [,5] [,6] # a 1 2 3 4 5 6 # b 1 6 1 6 1 6 > rbind(ll$a, ll$b) #same > rbind(ll[[1]], ll[[2]]) #same
rbindlist(ld)는 do.call("rbind", ld) 와 같다. 하지만, 빠르다.
> rbindlist(ld) > do.call("rbind", ld) #same # SL SW PL PW Species # 1: 5.1 3.5 1.4 0.2 setosa # 2: 4.9 3.0 1.4 0.2 setosa # 3: 4.7 3.2 1.3 0.2 setosa # 4: 4.6 3.1 1.5 0.2 setosa # 5: 5.0 3.6 1.4 0.2 setosa # 6: 5.4 3.9 1.7 0.4 setosa
rbindlist(l, use.names="check", fill=FALSE, idcol=NULL)
# rbind(\dots, use.names=TRUE, fill=FALSE, idcol=NULL)
rbindlist 의 idcol
https://itsalocke.com/blog/r-quick-tip-collapse-a-lists-of-data.frames-with-data.table/
각 list의 이름으로 각 행의 이름을 만든다.
myList <- list(p1=iris[1:3, -1] , p2=iris[51:53,] , p3=iris[101:103,]) # $p1 # Sepal.Width Petal.Length Petal.Width Species # 1 3.5 1.4 0.2 setosa # 2 3.0 1.4 0.2 setosa # 3 3.2 1.3 0.2 setosa # # $p2 # Sepal.Length Sepal.Width Petal.Length Petal.Width Species # 51 7.0 3.2 4.7 1.4 versicolor # 52 6.4 3.2 4.5 1.5 versicolor # 53 6.9 3.1 4.9 1.5 versicolor # # $p3 # Sepal.Length Sepal.Width Petal.Length Petal.Width Species # 101 6.3 3.3 6.0 2.5 virginica # 102 5.8 2.7 5.1 1.9 virginica # 103 7.1 3.0 5.9 2.1 virginica dt <- rbindlist(myList, fill=T) # Sepal.Width Petal.Length Petal.Width Species Sepal.Length # 1: 3.5 1.4 0.2 setosa NA # 2: 3.0 1.4 0.2 setosa NA # 3: 3.2 1.3 0.2 setosa NA # 4: 3.2 4.7 1.4 versicolor 7.0 # 5: 3.2 4.5 1.5 versicolor 6.4 # 6: 3.1 4.9 1.5 versicolor 6.9 # 7: 3.3 6.0 2.5 virginica 6.3 # 8: 2.7 5.1 1.9 virginica 5.8 # 9: 3.0 5.9 2.1 virginica 7.1 dt <- rbindlist(myList, fill=TRUE, idcol="myList") # myList Sepal.Width Petal.Length Petal.Width Species Sepal.Length # 1: p1 3.5 1.4 0.2 setosa NA # 2: p1 3.0 1.4 0.2 setosa NA # 3: p1 3.2 1.3 0.2 setosa NA # 4: p2 3.2 4.7 1.4 versicolor 7.0 # 5: p2 3.2 4.5 1.5 versicolor 6.4 # 6: p2 3.1 4.9 1.5 versicolor 6.9 # 7: p3 3.3 6.0 2.5 virginica 6.3 # 8: p3 2.7 5.1 1.9 virginica 5.8 # 9: p3 3.0 5.9 2.1 virginica 7.1
bind 성능비교(concatenating a list of dataFrames)
https://www.r-bloggers.com/concatenating-a-list-of-data-frames/ – 번역
N <- 10000 data <- list() for (i in 1:N) { data[[i]] = data.frame(index=i, char=sample(letters, 1), z=runif(1)) } data %>% head(3) # [[1]] # index char z # 1 1 p 0.3968009 # # [[2]] # index char z # 1 2 i 0.03525775 # # [[3]] # index char z # 1 3 x 0.5030154
elapsed를 보면, rbindlist()가 가장 빠름. (relative를 보면 가장 빠른 것을 1로 놓고 비교)
pacman::p_load(rbenchmark) benchmark( \tplyr::rbind.fill(data), do.call(rbind, data), \trbindlist(data), plyr::ldply(data, rbind) ) # test replications elapsed relative user.self sys.self user.child sys.child # 2 do.call(rbind, data) 100 45.147 16.796 44.249 0.466 0 0 # 4 plyr::ldply(data, rbind) 100 112.884 41.996 111.810 0.576 0 0 # 1 plyr::rbind.fill(data) 100 40.519 15.074 39.956 0.275 0 0 # 3 rbindlist(data) 100 2.688 1.000 2.643 0.033 0 0
rbind.data.frame() 은 row를 추가할때마다 Column명을 일일이 확인하고 rearange하다보니, 가장 느리다.
rbindlist는 같은 위치에 같은 컬럼이 있다고 가정하고 바로 합치다보니, 가장 빠르다.