superponer - Simultáneamente fusionar múltiples cuadros de datos en una lista
superponer graficas en r (6)
Otra pregunta formulada específicamente sobre cómo realizar múltiples uniones a la izquierda usando dplyr en R. La pregunta se marcó como un duplicado de ésta, así que respondo aquí, utilizando los 3 marcos de datos de muestra a continuación:
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)
Actualización de junio de 2018 : dividí la respuesta en tres secciones que representan tres formas diferentes de realizar la fusión. Probablemente desee utilizar el método purrr
si ya está utilizando los paquetes tidyverse . Para fines de comparación a continuación, encontrará una versión base de R que usa el mismo conjunto de datos de muestra.
Únete a ellos con reduce
desde el paquete purrr
El paquete purrr
proporciona una función de reduce
que tiene una sintaxis concisa:
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
También puede realizar otras combinaciones, como full_join
o 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()
con 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()
con base R Reduce()
Y para propósitos de comparación, aquí hay una versión base R de la combinación izquierda.
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
Tengo una lista de muchos data.frames que quiero combinar. El problema aquí es que cada data.frame difiere en términos del número de filas y columnas, pero todas comparten las variables clave (que he llamado "var1"
y "var2"
en el código a continuación). Si los cuadros de datos fueran idénticos en términos de columnas, podría simplemente rbind
, para lo cual rbind.fill de rbind.fill haría el trabajo, pero ese no es el caso con estos datos.
Debido a que el comando de merge
solo funciona en 2 cuadros de datos, me dirigí a Internet para obtener ideas. Conseguí este de here , que funcionó perfectamente en R 2.7.2, que es lo que tenía en ese momento:
merge.rec <- function(.list, ...){
if(length(.list)==1) return(.list[[1]])
Recall(c(list(merge(.list[[1]], .list[[2]], ...)), .list[-(1:2)]), ...)
}
Y llamaría a la función así:
df <- merge.rec(my.list, by.x = c("var1", "var2"),
by.y = c("var1", "var2"), all = T, suffixes=c("", ""))
Pero en cualquier versión R después de 2.7.2, incluyendo 2.11 y 2.12, este código falla con el siguiente error:
Error in match.names(clabs, names(xi)) :
names do not match previous names
(Incidentalmente, veo otras referencias a este error en elsewhere sin resolución).
¿Hay alguna forma de resolver esto?
Puedes hacerlo usando merge_all
en el paquete de reshape
. Puedes pasar parámetros para merge
usando el argumento ...
reshape::merge_all(list_of_dataframes, ...)
Aquí hay un excelente recurso sobre diferentes métodos para combinar marcos de datos .
Puedes usar la recursividad para hacer esto. No he verificado lo siguiente, pero debería darte la idea correcta:
MergeListOfDf = function( data , ... )
{
if ( length( data ) == 2 )
{
return( merge( data[[ 1 ]] , data[[ 2 ]] , ... ) )
}
return( merge( MergeListOfDf( data[ -1 ] , ... ) , data[[ 1 ]] , ... ) )
}
Reducir hace esto bastante fácil:
merged.data.frame = Reduce(function(...) merge(..., all=T), list.of.data.frames)
Aquí hay un ejemplo completo usando algunos datos 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)
# 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
Y aquí hay un ejemplo que usa estos datos 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 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>
Nota: Parece que esto es posiblemente un error en la merge
. El problema es que no hay comprobación de que agregar los sufijos (para manejar la superposición de nombres no coincidentes) en realidad los hace únicos. En un cierto punto, utiliza [.data.frame
que hace que los nombres de make.unique
, causando que rbind
falle.
# 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 forma más fácil de corregir es no dejar el campo renombrado para campos duplicados (de los cuales hay muchos aquí) hasta que se merge
. P.ej:
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))
La merge
/ Reduce
funcionará bien.
Reutilizaré el ejemplo de datos 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)
Aquí hay una solución corta y dulce usando purrr
y tidyr
library(tidyverse)
list(x, y, z) %>%
map_df(gather, key=key, value=value, -i) %>%
spread(key, value)
Tenía una lista de marcos de datos sin una columna de identificación común.
Me faltaban datos en muchos dfs. Había valores nulos. Los marcos de datos se produjeron utilizando la función de tabla. La Reduce, Combina, Rbind, rbind.fill, y sus similares no pudieron ayudarme a mi objetivo. Mi objetivo era producir un marco de datos fusionado comprensible, irrelevante de los datos faltantes y la columna de identificación común.
Por lo tanto, hice la siguiente función. Tal vez esta función pueda ayudar a alguien.
##########################################################
#### 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) )
está siguiendo la función
##########################################################
#### 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
}
Ejecutando el ejemplo
##########################################################
#### Running the example #####
##########################################################
rbind_null_df_lists ( list_of_df )