Fusionner simultanément plusieurs données.cadres dans une liste
j'ai une liste de nombreuses données.images que je veux fusionner. Le problème ici est que chaque donnée.frame diffère en termes de nombre de lignes et de colonnes, mais ils partagent tous les variables clés (que j'ai appelé "var1"
et "var2"
dans le code ci-dessous). Si les données.les cadres étaient identiques en termes de colonnes, je pouvais simplement rbind
, pour lequel plyr rbind.remplir pour faire le travail, mais ce n'est pas le cas avec ces données.
Parce que la commande merge
ne fonctionne que sur 2 données.frames, je me suis tourné vers Internet pour des idées. J'ai eu celui-ci de ici , qui a fonctionné parfaitement dans R 2.7.2, qui est ce 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 ainsi:
df <- merge.rec(my.list, by.x = c("var1", "var2"),
by.y = c("var1", "var2"), all = T, suffixes=c("", ""))
mais dans toute version R après 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 ça?
5 réponses
une autre question posée spécifiquement comment effectuer plusieurs jonctions à gauche en utilisant dplyr dans R . La question a été marquée comme une copie de celui-ci donc je réponds ici, en utilisant les 3 cadres de données d'échantillon ci-dessous:
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)
Update June 2018 : j'ai divisé la réponse en trois sections représentant trois façons différentes d'effectuer la fusion. Vous voulez probablement utiliser la méthode purrr
si vous utilisez déjà les colis tidyverse . Pour les fins de comparaison ci-dessous, vous trouverez une version de base R utilisant le même ensemble de données d'échantillon.
les joindre avec reduce
du purrr
paquet
le paquet purrr
fournit une fonction reduce
qui a une syntaxe concise:
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
Vous pouvez également effectuer d'autres jointures, comme un full_join
ou 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
dplyr::left_join()
avec base 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
Base R merge()
avec base R Reduce()
et à des fins de comparaison, voici une version de base R de la jointure gauche
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
Réduire le rend assez facile:
merged.data.frame = Reduce(function(...) merge(..., all=T), list.of.data.frames)
voici un exemple complet en utilisant quelques données simulées:
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
et voici un exemple en utilisant ces données pour répliquer 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>
Note: il semble qu'il s'agisse d'un bug dans merge
. Le problème est qu'il n'y a pas de vérification que l'ajout des suffixes (pour gérer le chevauchement les noms qui ne concordent pas) les rend en fait uniques. À un certain point, il utilise [.data.frame
qui fait make.unique
les noms, provoquant le rbind
d'échouer.
# 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.
la façon la plus facile de corriger est de ne pas laisser le champ Renommer pour les champs en double (dont il y a beaucoup ici) jusqu'à merge
. Par exemple:
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))
Le merge
/ Reduce
beau travail.
Vous pouvez le faire en utilisant merge_all
dans le reshape
. Vous pouvez passer les paramètres à merge
en utilisant le ...
argument
reshape::merge_all(list_of_dataframes, ...)
Ici est une excellente ressource sur les différentes méthodes de fusion de données images .
vous pouvez utiliser la récursion pour faire ceci. Je n'ai pas vérifié ce qui suit, mais cela devrait vous donner la bonne idée:
MergeListOfDf = function( data , ... )
{
if ( length( data ) == 2 )
{
return( merge( data[[ 1 ]] , data[[ 2 ]] , ... ) )
}
return( merge( MergeListOfDf( data[ -1 ] , ... ) , data[[ 1 ]] , ... ) )
}
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 douce en utilisant purrr
et tidyr
library(tidyverse)
list(x, y, z) %>%
map_df(gather, key=key, value=value, -i) %>%
spread(key, value)