Simultaneamente mesclar vários data.frames em uma lista

Eu tenho uma lista de muitos data.frames que eu quero mesclar. A questão aqui é que cada data.frame difere em termos do número de linhas e colunas, mas todos compartilham as variables-chave (que chamei de "var1" e "var2" no código abaixo). Se os data.frames fossem idênticos em termos de colunas, eu poderia simplesmente rbind , para o qual o rbind.fill do plyr faria o trabalho, mas esse não é o caso com esses dados.

Como o comando de merge funciona apenas em 2 data.frames, eu procurei idéias na Internet. Eu consegui um aqui , que funcionou perfeitamente em R 2.7.2, que é o que eu tinha na época:

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

E eu chamaria a function assim:

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

Mas em qualquer versão R após o 2.7.2, incluindo 2.11 e 2.12, este código falha com o seguinte erro:

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

(Incidentalmente, vejo outras referências a esse erro em outro lugar sem resolução).

Existe alguma maneira de resolver isto?

Outra questão perguntou especificamente como executar várias junções esquerdas usando dplyr em R. A questão foi marcada como uma duplicata desta, então eu respondo aqui, usando os 3 frameworks de dados de amostra abaixo:

 library(dplyr) 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) 

Atualização de junho de 2018 : dividi a resposta em três seções representando três maneiras diferentes de realizar a mesclagem. Você provavelmente vai querer usar o modo purrr se você já estiver usando os pacotes tidyverse . Para fins de comparação abaixo, você encontrará uma versão base do R usando o mesmo dataset de amostra.

Junte-se a eles com reduce do pacote purrr

O pacote purrr fornece uma function de reduce que possui uma syntax concisa:

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

Você também pode executar outras junções, como full_join ou inner_join :

 list(x, y, z) %>% reduce(full_join, by = "i") # A tibble: 4 x 4 # ijkl #     # 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 # ijkl #     # 1 c 3 5 7 

dplyr::left_join() com base R Reduce()

 list(x,y,z) %>% Reduce(function(dtf1,dtf2) left_join(dtf1,dtf2,by="i"), .) # ijkl # 1 a 1 NA 9 # 2 b 2 4 NA # 3 c 3 5 7 

Base R merge() com base R Reduce()

E para fins de comparação, aqui está uma versão R base da junit esquerda

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

Reduzir torna isso bastante fácil:

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

Aqui está um exemplo completo usando alguns dados simulados:

 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) # xaby #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 

E aqui está um exemplo usando esses dados para replicar 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  #2 ALVES 100 RI 019 S NA  NA NA NA NA  #3 BADEAU 100 RI 032 S NA  NA NA NA NA  

Nota: parece que este é possivelmente um bug na merge . O problema é que não há verificação de que adicionar os sufixos (para manipular nomes não correspondentes sobrepostos) os torna únicos. Em um certo ponto, ele usa [.data.frame que faz a make.unique dos nomes, fazendo com que o rbind falhe.

 # 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. 

A maneira mais fácil de corrigir é não deixar o campo renomear campos duplicados (dos quais existem muitos aqui) para merge . Por exemplo:

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

A merge / Reduce funcionará bem.

Você pode fazer isso usando merge_all no pacote merge_all . Você pode passar parâmetros para merge usando o argumento ...

 reshape::merge_all(list_of_dataframes, ...) 

Aqui está um excelente recurso em diferentes methods para mesclar frameworks de dados .

Você pode usar recursion para fazer isso. Não verifiquei o seguinte, mas ele deve fornecer a ideia correta:

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

Vou reutilizar o exemplo de dados do @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) 

Aqui está uma solução curta e doce usando purrr e tidyr

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