서로 다른 열 집합이있을 때 두 데이터 프레임을 행 (리 바인드)별로 결합


232

동일한 열 집합이없는 두 개의 데이터 프레임을 행 바인딩 할 수 있습니까? 바인드 후 일치하지 않는 열을 유지하려고합니다.

답변:



124

더 최근의 솔루션을 사용하는 것입니다 dplyrbind_rows내가보다 효율적입니다 가정 기능을 smartbind.

df1 <- data.frame(a = c(1:5), b = c(6:10))
df2 <- data.frame(a = c(11:15), b = c(16:20), c = LETTERS[1:5])
dplyr::bind_rows(df1, df2)
    a  b    c
1   1  6 <NA>
2   2  7 <NA>
3   3  8 <NA>
4   4  9 <NA>
5   5 10 <NA>
6  11 16    A
7  12 17    B
8  13 18    C
9  14 19    D
10 15 20    E

다른 열 이름과 많은 수의 데이터 프레임 (16)을 결합하려고하는데 오류가 발생합니다. 오류 : 열 ABC을 문자에서 숫자로 변환 할 수 없습니다. 열을 먼저 변환하는 방법이 있습니까?
sar

46

패키지 smartbind에서 사용할 수 있습니다 gtools.

예:

library(gtools)
df1 <- data.frame(a = c(1:5), b = c(6:10))
df2 <- data.frame(a = c(11:15), b = c(16:20), c = LETTERS[1:5])
smartbind(df1, df2)
# result
     a  b    c
1.1  1  6 <NA>
1.2  2  7 <NA>
1.3  3  8 <NA>
1.4  4  9 <NA>
1.5  5 10 <NA>
2.1 11 16    A
2.2 12 17    B
2.3 13 18    C
2.4 14 19    D
2.5 15 20    E

3
smartbind두 개의 큰 데이터 프레임 (총 약 3 * 10 ^ 6 행)으로 시도 하고 10 분 후에 중단했습니다.
Joe

2
9 년 만에 많은 일이 일어났습니다 :) 나는 오늘 스마트 바인드를 사용하지 않을 수도 있습니다. 또한 원래 질문은 큰 데이터 프레임을 지정하지 않았습니다.
neilfws

42

컬럼의 경우 DF1 에 해당하는 서브 세트 DF2 (열 이름으로는)

df3 <- rbind(df1, df2[, names(df1)])

37

대안 data.table:

library(data.table)
df1 = data.frame(a = c(1:5), b = c(6:10))
df2 = data.frame(a = c(11:15), b = c(16:20), c = LETTERS[1:5])
rbindlist(list(df1, df2), fill = TRUE)

rbind도에서 작동 data.table객체가 변환 될 때만큼 data.table객체 때문에,

rbind(setDT(df1), setDT(df2), fill=TRUE)

이 상황에서도 작동합니다. 이것은 몇 개의 data.tables가 있고 목록을 구성하지 않으려는 경우에 바람직 할 수 있습니다.


이 솔루션은 여러 개의 데이터 프레임으로 쉽게 일반화 할 수있는 가장 단순하고 즉시 사용 가능한 솔루션입니다. 모든 데이터 프레임을 별도의 목록 요소에 저장할 수 있기 때문입니다. intersect접근 방식 과 같은 다른 답변 은 2 개의 데이터 프레임에서만 작동하며 쉽게 일반화되지 않습니다.
Rich Pauloo 2016 년

35

대부분의 기본 R 답변은 하나의 data.frame에만 추가 열이 있거나 결과 data.frame이 열의 교차점을 갖는 상황을 해결합니다. OP가 쓰기 때문에 bind 이후에 일치하지 않는 열을 유지하기를 희망 하므로이 문제를 해결하기 위해 기본 R 방법을 사용하는 답변을 게시하는 것이 좋습니다.

아래에는 두 가지 기본 R 방법이 있습니다. 하나는 원래 data.frames를 변경하고 다른 하나는 변경하지 않습니다. 또한 비파괴 적 방법을 두 개 이상의 data.frames로 일반화하는 방법을 제공합니다.

먼저 샘플 데이터를 얻자.

# sample data, variable c is in df1, variable d is in df2
df1 = data.frame(a=1:5, b=6:10, d=month.name[1:5])
df2 = data.frame(a=6:10, b=16:20, c = letters[8:12])

두 개의 data.frames, 원본 변경 두 data.frames의
모든 열을 유지 rbind하고 오류없이 함수가 작동하게하려면 적절한 누락 된 이름으로 채워진 NA 열을 각 data.frame에 추가합니다. 사용하여 setdiff.

# fill in non-overlapping columns with NAs
df1[setdiff(names(df2), names(df1))] <- NA
df2[setdiff(names(df1), names(df2))] <- NA

자, rbind-em

rbind(df1, df2)
    a  b        d    c
1   1  6  January <NA>
2   2  7 February <NA>
3   3  8    March <NA>
4   4  9    April <NA>
5   5 10      May <NA>
6   6 16     <NA>    h
7   7 17     <NA>    i
8   8 18     <NA>    j
9   9 19     <NA>    k
10 10 20     <NA>    l

처음 두 줄은 원래 data.frames, df1 및 df2를 변경하여 전체 열 집합을 둘 다에 추가합니다.


원본
변경하지 않는 두 개의 data.frame 원본 data.frames를 그대로 유지하려면 먼저 다른 이름을 반복하고를 사용하여 data.frame과 함께 목록에 연결된 명명 된 NA 벡터를 반환하십시오 c. 그런 다음 data.frame결과를에 대한 적절한 data.frame으로 변환합니다 rbind.

rbind(
  data.frame(c(df1, sapply(setdiff(names(df2), names(df1)), function(x) NA))),
  data.frame(c(df2, sapply(setdiff(names(df1), names(df2)), function(x) NA)))
)

원본을 변경하지 않는 많은 data.frames 여러 data.frame
이있는 경우 다음을 수행 할 수 있습니다.

# put data.frames into list (dfs named df1, df2, df3, etc)
mydflist <- mget(ls(pattern="df\\d+"))
# get all variable names
allNms <- unique(unlist(lapply(mydflist, names)))

# put em all together
do.call(rbind,
        lapply(mydflist,
               function(x) data.frame(c(x, sapply(setdiff(allNms, names(x)),
                                                  function(y) NA)))))

원래 data.frames의 행 이름을 보지 못하는 것이 더 좋을까요? 그런 다음이 작업을 수행하십시오.

do.call(rbind,
        c(lapply(mydflist,
                 function(x) data.frame(c(x, sapply(setdiff(allNms, names(x)),
                                                    function(y) NA)))),
          make.row.names=FALSE))

열이 다른 16 개의 데이터 프레임이 있습니다 (각각 약 70-90 개의 총 열). 이것을 시도하면 첫 번째 명령 <-mget (ls (pattern = "df \\ d +"))이 붙어 있습니다. 내 데이터 프레임의 이름이 다릅니다. mydflist <-c (as, dr, kr, hyt, ed1, of)를 사용하여 목록을 만들려고 시도했지만 엄청난 목록이 생겼습니다.
sar

@GKi
sar

1
@sar 사용 mydflist <- list(as, dr, kr, hyt, ed1, of). 이것은 환경의 크기를 늘리지 않고 목록의 각 요소를 가리키는 목록 객체를 구성해야합니다 (나중에 내용을 변경하지 않는 한). 작업 후 안전을 위해 목록 개체를 제거하십시오.
lmo

20

공통 열 이름을 가져올 수도 있습니다.

> cols <- intersect(colnames(df1), colnames(df2))
> rbind(df1[,cols], df2[,cols])

6

나는 무언가가 잘못되었는지 알려주는 코드를 좋아하기 때문에 이것을 수행하는 함수를 작성했다. 이 함수는 일치하지 않는 열 이름과 유형이 일치하지 않는 경우 명시 적으로 알려줍니다. 그런 다음 어쨌든 data.frames을 결합하기 위해 최선을 다할 것입니다. 한 번에 두 개의 data.frame 만 결합 할 수 있다는 제한이 있습니다.

### combines data frames (like rbind) but by matching column names
# columns without matches in the other data frame are still combined
# but with NA in the rows corresponding to the data frame without
# the variable
# A warning is issued if there is a type mismatch between columns of
# the same name and an attempt is made to combine the columns
combineByName <- function(A,B) {
    a.names <- names(A)
    b.names <- names(B)
    all.names <- union(a.names,b.names)
    print(paste("Number of columns:",length(all.names)))
    a.type <- NULL
    for (i in 1:ncol(A)) {
        a.type[i] <- typeof(A[,i])
    }
    b.type <- NULL
    for (i in 1:ncol(B)) {
        b.type[i] <- typeof(B[,i])
    }
    a_b.names <- names(A)[!names(A)%in%names(B)]
    b_a.names <- names(B)[!names(B)%in%names(A)]
    if (length(a_b.names)>0 | length(b_a.names)>0){
        print("Columns in data frame A but not in data frame B:")
        print(a_b.names)
        print("Columns in data frame B but not in data frame A:")
        print(b_a.names)
    } else if(a.names==b.names & a.type==b.type){
        C <- rbind(A,B)
        return(C)
    }
    C <- list()
    for(i in 1:length(all.names)) {
        l.a <- all.names[i]%in%a.names
        pos.a <- match(all.names[i],a.names)
        typ.a <- a.type[pos.a]
        l.b <- all.names[i]%in%b.names
        pos.b <- match(all.names[i],b.names)
        typ.b <- b.type[pos.b]
        if(l.a & l.b) {
            if(typ.a==typ.b) {
                vec <- c(A[,pos.a],B[,pos.b])
            } else {
                warning(c("Type mismatch in variable named: ",all.names[i],"\n"))
                vec <- try(c(A[,pos.a],B[,pos.b]))
            }
        } else if (l.a) {
            vec <- c(A[,pos.a],rep(NA,nrow(B)))
        } else {
            vec <- c(rep(NA,nrow(A)),B[,pos.b])
        }
        C[[i]] <- vec
    }
    names(C) <- all.names
    C <- as.data.frame(C)
    return(C)
}

2

어쩌면 나는 당신의 질문을 완전히 읽지 못했지만 "바인드 후 일치하지 않는 열을 유지하기를 희망합니다"는 SQL 쿼리와 유사 left join하거나 right join비슷한 것을 찾고 있다고 생각합니다 . R에는 mergeSQL의 테이블 조인과 유사한 왼쪽, 오른쪽 또는 내부 조인을 지정할 수 있는 기능이 있습니다.

이 주제에 대해 이미 좋은 질문과 답변이 있습니다 : 데이터 프레임을 결합 (병합)하는 방법 (내부, 외부, 왼쪽, 오른쪽)?


2

gtools / smartbind는 아마도 날짜를 다루는 것을 좋아하지 않았을 것입니다. 여기 내 해결책이 있습니다 ...

sbind = function(x, y, fill=NA) {
    sbind.fill = function(d, cols){ 
        for(c in cols)
            d[[c]] = fill
        d
    }

    x = sbind.fill(x, setdiff(names(y),names(x)))
    y = sbind.fill(y, setdiff(names(x),names(y)))

    rbind(x, y)
}

rbind (x, y) 대신 dplyr :: bind_rows (x, y)를 사용하면 첫 번째 데이터 프레임을 기준으로 열 순서가 유지됩니다.
RanonKahn

2

문서화를 위해서만. 다음 형식으로 Stack라이브러리와 그 기능 Stack을 시도 할 수 있습니다 .

Stack(df_1, df_2)

또한 대용량 데이터 세트의 다른 방법보다 빠르다는 인상을 받았습니다.


1

당신은 또한 사용할 수 sjmisc::add_rows()있는 용도, dplyr::bind_rows()하지만 달리 bind_rows(), add_rows()보존 속성 때문에 유용하다 표시된 데이터 .

레이블이 지정된 데이터 세트가있는 다음 예를 참조하십시오. frq()α- 함수는 값 레이블 빈도 표를 인쇄 하는 경우 , 데이터가 분류된다.

library(sjmisc)
library(dplyr)

data(efc)
# select two subsets, with some identical and else different columns
x1 <- efc %>% select(1:5) %>% slice(1:10)
x2 <- efc %>% select(3:7) %>% slice(11:20)

str(x1)
#> 'data.frame':    10 obs. of  5 variables:
#>  $ c12hour : num  16 148 70 168 168 16 161 110 28 40
#>   ..- attr(*, "label")= chr "average number of hours of care per week"
#>  $ e15relat: num  2 2 1 1 2 2 1 4 2 2
#>   ..- attr(*, "label")= chr "relationship to elder"
#>   ..- attr(*, "labels")= Named num  1 2 3 4 5 6 7 8
#>   .. ..- attr(*, "names")= chr  "spouse/partner" "child" "sibling" "daughter or son -in-law" ...
#>  $ e16sex  : num  2 2 2 2 2 2 1 2 2 2
#>   ..- attr(*, "label")= chr "elder's gender"
#>   ..- attr(*, "labels")= Named num  1 2
#>   .. ..- attr(*, "names")= chr  "male" "female"
#>  $ e17age  : num  83 88 82 67 84 85 74 87 79 83
#>   ..- attr(*, "label")= chr "elder' age"
#>  $ e42dep  : num  3 3 3 4 4 4 4 4 4 4
#>   ..- attr(*, "label")= chr "elder's dependency"
#>   ..- attr(*, "labels")= Named num  1 2 3 4
#>   .. ..- attr(*, "names")= chr  "independent" "slightly dependent" "moderately dependent" "severely dependent"

bind_rows(x1, x1) %>% frq(e42dep)
#> 
#> # e42dep <numeric> 
#> # total N=20  valid N=20  mean=3.70  sd=0.47
#>  
#>   val frq raw.prc valid.prc cum.prc
#>     3   6      30        30      30
#>     4  14      70        70     100
#>  <NA>   0       0        NA      NA

add_rows(x1, x1) %>% frq(e42dep)
#> 
#> # elder's dependency (e42dep) <numeric> 
#> # total N=20  valid N=20  mean=3.70  sd=0.47
#>  
#>  val                label frq raw.prc valid.prc cum.prc
#>    1          independent   0       0         0       0
#>    2   slightly dependent   0       0         0       0
#>    3 moderately dependent   6      30        30      30
#>    4   severely dependent  14      70        70     100
#>   NA                   NA   0       0        NA      NA

-1
rbind.ordered=function(x,y){

  diffCol = setdiff(colnames(x),colnames(y))
  if (length(diffCol)>0){
    cols=colnames(y)
    for (i in 1:length(diffCol)) y=cbind(y,NA)
    colnames(y)=c(cols,diffCol)
  }

  diffCol = setdiff(colnames(y),colnames(x))
  if (length(diffCol)>0){
    cols=colnames(x)
    for (i in 1:length(diffCol)) x=cbind(x,NA)
    colnames(x)=c(cols,diffCol)
  }
  return(rbind(x, y[, colnames(x)]))
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.