binding

Published by onesixx on

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는 같은 위치에 같은 컬럼이 있다고 가정하고 바로 합치다보니, 가장 빠르다. 

Categories: Reshaping

onesixx

Blog Owner

Subscribe
Notify of
guest

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
2
0
Would love your thoughts, please comment.x
()
x