목록에서 여러 데이터 프레임을 동시에 병합


258

병합하려는 많은 data.frames 목록이 있습니다. 여기서 문제는 각 data.frame이 행과 열 수의 관점에서 다르지만 모두 핵심 변수 ( 아래에서 호출 "var1"하고 "var2"코드)를 공유한다는 것 입니다. data.frames 가 열 측면에서 동일 rbind하다면 plyr의 rbind.fill 이 작업을 수행 할 수는 있지만이 데이터의 경우는 아닙니다.

merge명령은 2 data.frame에서만 작동 하기 때문에 아이디어를 얻기 위해 인터넷을 사용했습니다. 나는 여기 에서 이것을 얻었고 , 이것은 R 2.7.2에서 완벽하게 작동했습니다.

merge.rec <- function(.list, ...){
    if(length(.list)==1) return(.list[[1]])
    Recall(c(list(merge(.list[[1]], .list[[2]], ...)), .list[-(1:2)]), ...)
}

그리고 함수를 다음과 같이 호출합니다.

df <- merge.rec(my.list, by.x = c("var1", "var2"), 
                by.y = c("var1", "var2"), all = T, suffixes=c("", ""))

그러나 2.11 및 2.12를 포함하여 2.7.2 이후의 모든 R 버전에서이 코드는 다음 오류와 함께 실패합니다.

Error in match.names(clabs, names(xi)) : 
  names do not match previous names

(우연히도 다른 곳에서는 이 오류 에 대한 해결책이 없습니다.)

이 문제를 해결할 방법이 있습니까?

답변:


182

또 다른 질문 은 R에서 dplyr을 사용하여 다중 왼쪽 조인을 수행하는 방법을 구체적으로 물었습니다 . 질문은이 질문과 중복으로 표시되어 있으므로 아래 3 개의 샘플 데이터 프레임을 사용하여 여기에 대답하십시오.

x <- data.frame(i = c("a","b","c"), j = 1:3, stringsAsFactors=FALSE)
y <- data.frame(i = c("b","c","d"), k = 4:6, stringsAsFactors=FALSE)
z <- data.frame(i = c("c","d","a"), l = 7:9, stringsAsFactors=FALSE)

2018 년 6 월 업데이트 : 병합을 수행하는 세 가지 방법을 나타내는 세 가지 섹션으로 답변을 나누었습니다. purrr이미 tidyverse 패키지 를 사용하고 있다면 그 방법 을 사용하고 싶을 것입니다 . 아래의 비교를 위해 동일한 샘플 데이터 세트를 사용하는 기본 R 버전을 찾을 수 있습니다.


1) 패키지 reduce에서 함께 참여하십시오 purrr.

purrr패키지가 제공 reduce간결한 구문을 보유 기능 :

library(tidyverse)
list(x, y, z) %>% reduce(left_join, by = "i")
#  A tibble: 3 x 4
#  i       j     k     l
#  <chr> <int> <int> <int>
# 1 a      1    NA     9
# 2 b      2     4    NA
# 3 c      3     5     7

당신은 또한 같은 같은 다른 조인을 수행 할 수 있습니다 full_join또는 inner_join:

list(x, y, z) %>% reduce(full_join, by = "i")
# A tibble: 4 x 4
# i       j     k     l
# <chr> <int> <int> <int>
# 1 a     1     NA     9
# 2 b     2     4      NA
# 3 c     3     5      7
# 4 d     NA    6      8

list(x, y, z) %>% reduce(inner_join, by = "i")
# A tibble: 1 x 4
# i       j     k     l
# <chr> <int> <int> <int>
# 1 c     3     5     7

2) dplyr::left_join()기본 R Reduce():

list(x,y,z) %>%
    Reduce(function(dtf1,dtf2) left_join(dtf1,dtf2,by="i"), .)

#   i j  k  l
# 1 a 1 NA  9
# 2 b 2  4 NA
# 3 c 3  5  7

3)베이스 R이 merge()있는베이스 R Reduce():

그리고 비교를 위해 왼쪽 조인의 기본 R 버전이 있습니다.

 Reduce(function(dtf1, dtf2) merge(dtf1, dtf2, by = "i", all.x = TRUE),
        list(x,y,z))
#   i j  k  l
# 1 a 1 NA  9
# 2 b 2  4 NA
# 3 c 3  5  7

1
full_join 변형은 완벽하게 작동하며 허용되는 답변보다 훨씬 덜 무섭게 보입니다. 그러나 속도 차이는별로 없습니다.
bshor

1
@Axeman 바로,하지만 당신은 사용하여 모든에서 데이터 프레임의 목록을 반환 (시각적으로) 피할 수 있습니다 map_dfr()또는map_dfc()
DaveRGP

´ls (pattern = "DF_name_contains_this") ´를 사용하여 패턴을 기반으로 여러 DF를 조인 할 수 있었지만 아니요. 중고 'noquote이 (붙여 넣기 (()) ',하지만 난 여전히 대신 불쾌한 내가 이름을 입력 결국의 DF 목록의 문자 벡터 생산하고 있습니다.를.
조지 윌리엄 러셀의 펜

또 다른 질문은 제공 파이썬 구현 : 팬더 데이터 프레임의 목록을 dfs = [df1, df2, df3]다음 reduce(pandas.merge, dfs).
Paul Rougieux

222

감소는 이것을 매우 쉽게 만듭니다.

merged.data.frame = Reduce(function(...) merge(..., all=T), list.of.data.frames)

다음은 일부 모의 데이터를 사용하는 완전한 예입니다.

set.seed(1)
list.of.data.frames = list(data.frame(x=1:10, a=1:10), data.frame(x=5:14, b=11:20), data.frame(x=sample(20, 10), y=runif(10)))
merged.data.frame = Reduce(function(...) merge(..., all=T), list.of.data.frames)
tail(merged.data.frame)
#    x  a  b         y
#12 12 NA 18        NA
#13 13 NA 19        NA
#14 14 NA 20 0.4976992
#15 15 NA NA 0.7176185
#16 16 NA NA 0.3841037
#17 19 NA NA 0.3800352

다음은 이러한 데이터 를 사용 하여 복제 하는 예입니다 my.list.

merged.data.frame = Reduce(function(...) merge(..., by=match.by, all=T), my.list)
merged.data.frame[, 1:12]

#  matchname party st district chamber senate1993 name.x v2.x v3.x v4.x senate1994 name.y
#1   ALGIERE   200 RI      026       S         NA   <NA>   NA   NA   NA         NA   <NA>
#2     ALVES   100 RI      019       S         NA   <NA>   NA   NA   NA         NA   <NA>
#3    BADEAU   100 RI      032       S         NA   <NA>   NA   NA   NA         NA   <NA>

참고 : 이것은 아마도 버그 인 것 같습니다 merge. 문제는 일치하지 않는 겹치는 이름을 처리하기 위해 접미사를 추가하면 실제로 접미사를 고유하게 만든다는 확인이 없다는 것입니다. 특정 시점에서 사용 [.data.frame하는 하지 make.unique (가) 원인이 이름을 rbind실패합니다.

# first merge will end up with 'name.x' & 'name.y'
merge(my.list[[1]], my.list[[2]], by=match.by, all=T)
# [1] matchname    party        st           district     chamber      senate1993   name.x      
# [8] votes.year.x senate1994   name.y       votes.year.y
#<0 rows> (or 0-length row.names)
# as there is no clash, we retain 'name.x' & 'name.y' and get 'name' again
merge(merge(my.list[[1]], my.list[[2]], by=match.by, all=T), my.list[[3]], by=match.by, all=T)
# [1] matchname    party        st           district     chamber      senate1993   name.x      
# [8] votes.year.x senate1994   name.y       votes.year.y senate1995   name         votes.year  
#<0 rows> (or 0-length row.names)
# the next merge will fail as 'name' will get renamed to a pre-existing field.

수정하는 가장 쉬운 방법은 중복 필드 (여기서 많은 항목)의 필드 이름을 변경하지 않는 것 merge입니다. 예 :

my.list2 = Map(function(x, i) setNames(x, ifelse(names(x) %in% match.by,
      names(x), sprintf('%s.%d', names(x), i))), my.list, seq_along(my.list))

merge/는 Reduce잘 작동 후 것이다.


감사! Ramnath의 링크 에서도이 솔루션을 보았습니다. 충분히 쉽게 보입니다. 그러나 다음과 같은 오류가 발생합니다. "match.names (clabs, names (xi)) 오류 : 이름이 이전 이름과 일치하지 않습니다." 내가 일치시키는 변수는 모두 목록의 모든 데이터 프레임에 존재 하므로이 오류가 무엇을 말하는 지 알 수 없습니다.
bshor

1
R2.7.2 에서이 솔루션을 테스트했으며 동일한 match.names 오류가 발생합니다. 따라서이 솔루션과 내 데이터에는 더 근본적인 문제가 있습니다. 코드를 사용했습니다 : Reduce (function (x, y) merge (x, y, all = T, by.x = match.by, by.y = match.by), my.list, 축적 = F)
bshor

1
이상하게도 테스트 한 코드를 추가하여 정상적으로 실행되었습니다. 사용중인 병합 인수에 따라 필드 이름 바꾸기가 발생한다고 생각하십니까? 후속 데이터 프레임과 병합하려면 병합 된 결과에 여전히 관련 키가 있어야합니다.
찰스

빈 데이터 프레임에 문제가 있다고 생각합니다. 나는 이와 같은 몇 가지 예를 시도했지만 empty <- data.frame(x=numeric(0),a=numeric(0); L3 <- c(empty,empty,list.of.data.frames,empty,empty,empty)아직 알아 내지 못했던 이상한 일이 발생했습니다.
벤 볼커

@ 찰스 당신은 뭔가에있어. 귀하의 코드는 위와 같이 잘 작동합니다. 그리고 내가 그것을 내 것에 적응시킬 때, 그것은 내가 원하는 주요 변수를 무시하고 병합하는 것을 제외하고는 잘 작동합니다. 키 변수를 제외시키지 않고 추가하려고하면 "Error in is.null (x) : 'x'is missing"오류가 발생합니다. 코드 줄은 "test.reduce <-Reduce (function (...) merge (by = match.by, all = T), my.list)"입니다. 여기서 match.by는 병합하려는 주요 변수 이름의 벡터입니다. 으로.
bshor

52

패키지 merge_all에서 사용할 수 있습니다 reshape. 인수 를 merge사용하여 매개 변수를 전달할 수 있습니다...

reshape::merge_all(list_of_dataframes, ...)

다음은 데이터 프레임을 병합하는 다양한 방법에 대한 훌륭한 리소스입니다 .


이 함수가 이미 존재한다는 것을 알기 위해 merge_recurse =)를 복제 한 것처럼 보입니다.
SFun28

16
예. 내가 아이디어가있을 때마다, 나는 항상 @hadley가 이미 그것을했는지 확인하고, 그가 가지고있는 대부분의 시간 :-)
Ramnath

1
조금 혼란 스러워요. merge_all 또는 merge_recurse를 수행해야합니까? 어쨌든, 추가 인수를 추가하려고 할 때 "공식 인수"all "이 여러 개의 실제 인수와 일치합니다"라는 오류가 발생합니다.
bshor

2
나는 이것을 reshape2에서 떨어 뜨렸다 고 생각합니다. 축소 + 병합은 간단합니다.
hadley

2
@ Ramnath, 링크가 죽었습니다, 거울이 있습니까?
Eduardo Eduardo

4

재귀를 사용하여이 작업을 수행 할 수 있습니다. 다음을 확인하지는 않았지만 올바른 아이디어를 제공해야합니다.

MergeListOfDf = function( data , ... )
{
    if ( length( data ) == 2 ) 
    {
        return( merge( data[[ 1 ]] , data[[ 2 ]] , ... ) )
    }    
    return( merge( MergeListOfDf( data[ -1 ] , ... ) , data[[ 1 ]] , ... ) )
}

2

@PaulRougieux의 데이터 예제를 재사용하겠습니다.

x <- data_frame(i = c("a","b","c"), j = 1:3)
y <- data_frame(i = c("b","c","d"), k = 4:6)
z <- data_frame(i = c("c","d","a"), l = 7:9)

여기에 사용 짧고 달콤한 해결책 purrrtidyr

library(tidyverse)

 list(x, y, z) %>% 
  map_df(gather, key=key, value=value, -i) %>% 
  spread(key, value)

1

eat내 패키지 safejoin 의 기능 에는 그러한 기능이 있습니다 .data.frames 목록을 두 번째 입력으로 제공하면 첫 번째 입력에 재귀 적으로 결합됩니다.

허용 된 답변 데이터를 차용 및 확장 :

x <- data_frame(i = c("a","b","c"), j = 1:3)
y <- data_frame(i = c("b","c","d"), k = 4:6)
z <- data_frame(i = c("c","d","a"), l = 7:9)
z2 <- data_frame(i = c("a","b","c"), l = rep(100L,3),l2 = rep(100L,3)) # for later

# devtools::install_github("moodymudskipper/safejoin")
library(safejoin)
eat(x, list(y,z), .by = "i")
# # A tibble: 3 x 4
#   i         j     k     l
#   <chr> <int> <int> <int>
# 1 a         1    NA     9
# 2 b         2     4    NA
# 3 c         3     5     7

모든 열을 사용할 필요는 없습니다. tidyselect 에서 select helper를 사용하여 선택할 수 있습니다 ( .x모든 .x열 에서 시작하여 유지됨).

eat(x, list(y,z), starts_with("l") ,.by = "i")
# # A tibble: 3 x 3
#   i         j     l
#   <chr> <int> <int>
# 1 a         1     9
# 2 b         2    NA
# 3 c         3     7

또는 특정 것을 제거하십시오.

eat(x, list(y,z), -starts_with("l") ,.by = "i")
# # A tibble: 3 x 3
#   i         j     k
#   <chr> <int> <int>
# 1 a         1    NA
# 2 b         2     4
# 3 c         3     5

목록의 이름이 지정되면 이름이 접두사로 사용됩니다.

eat(x, dplyr::lst(y,z), .by = "i")
# # A tibble: 3 x 4
#   i         j   y_k   z_l
#   <chr> <int> <int> <int>
# 1 a         1    NA     9
# 2 b         2     4    NA
# 3 c         3     5     7

열이 충돌하면 .conflict인수를 사용하여 예를 들어 첫 번째 / 두 번째 열을 가져 와서 추가, 통합 또는 중첩하여 해결할 수 있습니다.

먼저 유지 :

eat(x, list(y, z, z2), .by = "i", .conflict = ~.x)
# # A tibble: 3 x 4
#   i         j     k     l
#   <chr> <int> <int> <int>
# 1 a         1    NA     9
# 2 b         2     4    NA
# 3 c         3     5     7

마지막으로 유지 :

eat(x, list(y, z, z2), .by = "i", .conflict = ~.y)
# # A tibble: 3 x 4
#   i         j     k     l
#   <chr> <int> <int> <dbl>
# 1 a         1    NA   100
# 2 b         2     4   100
# 3 c         3     5   100

더하다:

eat(x, list(y, z, z2), .by = "i", .conflict = `+`)
# # A tibble: 3 x 4
#   i         j     k     l
#   <chr> <int> <int> <dbl>
# 1 a         1    NA   109
# 2 b         2     4    NA
# 3 c         3     5   107

합병 :

eat(x, list(y, z, z2), .by = "i", .conflict = dplyr::coalesce)
# # A tibble: 3 x 4
#   i         j     k     l
#   <chr> <int> <int> <dbl>
# 1 a         1    NA     9
# 2 b         2     4   100
# 3 c         3     5     7

둥지:

eat(x, list(y, z, z2), .by = "i", .conflict = ~tibble(first=.x, second=.y))
# # A tibble: 3 x 4
#   i         j     k l$first $second
#   <chr> <int> <int>   <int>   <int>
# 1 a         1    NA       9     100
# 2 b         2     4      NA     100
# 3 c         3     5       7     100

NA.fill인수 를 사용하여 값을 대체 할 수 있습니다 .

eat(x, list(y, z), .by = "i", .fill = 0)
# # A tibble: 3 x 4
#   i         j     k     l
#   <chr> <int> <dbl> <dbl>
# 1 a         1     0     9
# 2 b         2     4     0
# 3 c         3     5     7

기본적으로는 향상된 것 left_join하지만 모든 dplyr이 관통 지원 조인 .mode인수, 퍼지도를 통해 지원됩니다 조인 match_fun 인수 (이 패키지 주위에 싸여 fuzzyjoin) 또는 같은 수식을주고 ~ X("var1") > Y("var2") & X("var3") < Y("var4")받는 by인수입니다.


0

공통 ID 열이없는 데이터 프레임 목록이 있습니다.
많은 df에 대한 데이터가 누락되었습니다. 널값이있었습니다. 데이터 프레임은 테이블 함수를 사용하여 생성되었습니다. Reduce, Merging, rbind, rbind.fill 등은 저의 목표에 도움이되지 못했습니다. 내 목표는 누락 된 데이터 및 공통 ID 열과 관련이없는 이해할 수있는 병합 된 데이터 프레임을 생성하는 것이 었습니다.

따라서 다음과 같은 기능을 수행했습니다. 이 기능은 누군가에게 도움이 될 수 있습니다.

##########################################################
####             Dependencies                        #####
##########################################################

# Depends on Base R only

##########################################################
####             Example DF                          #####
##########################################################

# Example df
ex_df           <- cbind(c( seq(1, 10, 1), rep("NA", 0), seq(1,10, 1) ), 
                         c( seq(1, 7, 1),  rep("NA", 3), seq(1, 12, 1) ), 
                         c( seq(1, 3, 1),  rep("NA", 7), seq(1, 5, 1), rep("NA", 5) ))

# Making colnames and rownames
colnames(ex_df) <- 1:dim(ex_df)[2]
rownames(ex_df) <- 1:dim(ex_df)[1]

# Making an unequal list of dfs, 
# without a common id column
list_of_df      <- apply(ex_df=="NA", 2, ( table) )

그것은 기능을 따르고있다

##########################################################
####             The function                        #####
##########################################################


# The function to rbind it
rbind_null_df_lists <- function ( list_of_dfs ) {
  length_df     <- do.call(rbind, (lapply( list_of_dfs, function(x) length(x))))
  max_no        <- max(length_df[,1])
  max_df        <- length_df[max(length_df),]
  name_df       <- names(length_df[length_df== max_no,][1])
  names_list    <- names(list_of_dfs[ name_df][[1]])

  df_dfs <- list()
  for (i in 1:max_no ) {

    df_dfs[[i]]            <- do.call(rbind, lapply(1:length(list_of_dfs), function(x) list_of_dfs[[x]][i]))

  }

  df_cbind               <- do.call( cbind, df_dfs )
  rownames( df_cbind )   <- rownames (length_df)
  colnames( df_cbind )   <- names_list

  df_cbind

}

예제 실행

##########################################################
####             Running the example                 #####
##########################################################

rbind_null_df_lists ( list_of_df )

0

df 목록이 있고 열에 "ID"가 포함되어 있지만 일부 목록에서 일부 ID가 누락 된 경우 누락 된 행 ID 또는 레이블의 여러 Dfs를 결합하기 위해이 버전의 Reduce / Merge를 사용할 수 있습니다.

Reduce(function(x, y) merge(x=x, y=y, by="V1", all.x=T, all.y=T), list_of_dfs)

0

다음은 이진 함수를 다중 매개 변수 함수로 변환하는 데 사용할 수있는 일반 래퍼입니다. 이 솔루션의 장점은 매우 일반적이며 모든 이진 함수에 적용 할 수 있다는 것입니다. 한 번만 수행하면 어디서나 적용 할 수 있습니다.

아이디어를 시연하기 위해 간단한 재귀를 사용하여 구현합니다. R의 기능적 패러다임에 대한 훌륭한 지원을 통해보다 우아한 방식으로 구현할 수 있습니다.

fold_left <- function(f) {
return(function(...) {
    args <- list(...)
    return(function(...){
    iter <- function(result,rest) {
        if (length(rest) == 0) {
            return(result)
        } else {
            return(iter(f(result, rest[[1]], ...), rest[-1]))
        }
    }
    return(iter(args[[1]], args[-1]))
    })
})}

그런 다음 이진 함수를 간단히 감싸고 첫 번째 괄호에 위치 매개 변수 (일반적으로 data.frames)를 호출하고 두 번째 괄호에 이름이 지정된 매개 변수 (예 : by =또는 suffix =)를 호출 할 수 있습니다 . 명명 된 매개 변수가 없으면 두 번째 괄호를 비워 두십시오.

merge_all <- fold_left(merge)
merge_all(df1, df2, df3, df4, df5)(by.x = c("var1", "var2"), by.y = c("var1", "var2"))

left_join_all <- fold_left(left_join)
left_join_all(df1, df2, df3, df4, df5)(c("var1", "var2"))
left_join_all(df1, df2, df3, df4, df5)()
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.