349 votes

Fusionner simultanément plusieurs data.frames dans une liste

Je dispose d'une liste de nombreux data.frames que je souhaite fusionner. Le problème est que chaque data.frame diffère en termes de nombre de lignes et de colonnes, mais qu'ils partagent tous les mêmes variables clés (que j'ai appelées "var1" y "var2" dans le code ci-dessous). Si les data.frames étaient identiques en termes de colonnes, je pourrais simplement rbind pour lequel le plyr's rbind.fill ferait l'affaire, mais ce n'est pas le cas avec ces données.

Parce que le merge ne fonctionne que sur 2 data.frames, je me suis tourné vers l'Internet pour trouver des idées. J'ai obtenu celle-ci de aquí qui fonctionnait parfaitement dans R 2.7.2, qui est la version que j'avais à l'époque :

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

Et j'appellerais la fonction comme ça :

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

Mais dans toute version de R postérieure à 2.7.2, y compris 2.11 et 2.12, ce code échoue avec l'erreur suivante :

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

(Incidemment, je vois d'autres références à cette erreur ailleurs sans résolution).

Y a-t-il un moyen de résoudre ce problème ?

5voto

dmi3kno Points 1711

Je vais réutiliser l'exemple de données de @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)

Voici une solution courte et efficace utilisant purrr y tidyr

library(tidyverse)

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

1voto

J'avais une liste de dataframes sans colonne d'identification commune.
J'avais des données manquantes sur de nombreux DFS. Il y avait des valeurs nulles. Les cadres de données ont été produits à l'aide de la fonction table. Les fonctions Reduce, Merging, rbind, rbind.fill et autres n'ont pas pu m'aider à atteindre mon objectif. Mon objectif était de produire un cadre de données fusionné compréhensible, sans tenir compte des données manquantes et de la colonne d'identification commune.

J'ai donc créé la fonction suivante. Peut-être que cette fonction peut aider quelqu'un.

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

il suit la fonction

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

}

Exécution de l'exemple

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

rbind_null_df_lists ( list_of_df )

1voto

englealuze Points 751

Voici un wrapper générique qui peut être utilisé pour convertir une fonction binaire en fonction multi-paramètres. L'avantage de cette solution est qu'elle est très générique et peut être appliquée à n'importe quelle fonction binaire. Vous n'avez qu'à le faire une fois et vous pouvez l'appliquer partout.

Pour démontrer l'idée, j'utilise une récursion simple à mettre en œuvre. Il est bien sûr possible de l'implémenter de manière plus élégante en profitant du bon support de R pour le paradigme fonctionnel.

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

Ensuite, vous pouvez simplement envelopper n'importe quelle fonction binaire avec elle et l'appeler avec des paramètres positionnels (généralement des data.frames) dans les premières parenthèses et des paramètres nommés dans les secondes parenthèses (tels que by = o suffix = ). Si aucun paramètre n'est nommé, laissez les secondes parenthèses vides.

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

0voto

Lorsque vous avez une liste de dfs, et qu'une colonne contient l'"ID", mais que dans certaines listes, certains ID sont manquants, alors vous pouvez utiliser cette version de Reduce / Merge afin de joindre plusieurs dfs dont les Row Ids ou les labels sont manquants :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X