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

병합하려는 많은 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

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

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



답변

또 다른 질문 은 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


답변

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

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잘 작동 후 것이다.


답변

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

reshape::merge_all(list_of_dataframes, ...)

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


답변

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

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


답변

@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)


답변

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인수입니다.


답변

공통 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 )