Combina dos marcos de datos por filas (rbind) cuando tienen diferentes conjuntos de columnas

¿Es posible enlazar dos marcos de datos que no tienen el mismo conjunto de columnas? Espero conservar las columnas que no coinciden después del enlace.

rbind.fill del paquete plyr puede ser lo que estás buscando.

Una solución más reciente es usar la función bind_rows , que supongo que es más eficiente que smartbind .

Puede usar smartbind desde el paquete gtools .

Ejemplo:

 library(gtools) df1 <- data.frame(a = c(1:5), b = c(6:10)) df2 <- data.frame(a = c(11:15), b = c(16:20), c = LETTERS[1:5]) smartbind(df1, df2) # result abc 1.1 1 6  1.2 2 7  1.3 3 8  1.4 4 9  1.5 5 10  2.1 11 16 A 2.2 12 17 B 2.3 13 18 C 2.4 14 19 D 2.5 15 20 E 

Si las columnas en df1 son un subconjunto de aquellas en df2 (por nombres de columna):

 df3 <- rbind(df1, df2[, names(df1)]) 

Una alternativa con data.table :

 library(data.table) df1 = data.frame(a = c(1:5), b = c(6:10)) df2 = data.frame(a = c(11:15), b = c(16:20), c = LETTERS[1:5]) rbindlist(list(df1, df2), fill = TRUE) 

rbind también funcionará en data.table siempre que los objetos se conviertan en objetos data.table , por lo que

 rbind(setDT(df1), setDT(df2), fill=TRUE) 

también funcionará en esta situación. Esto puede ser preferible cuando tiene un par de data.tables y no quiere construir una lista.

También podría simplemente sacar los nombres de columna comunes.

 > cols <- intersect(colnames(df1), colnames(df2)) > rbind(df1[,cols], df2[,cols]) 

La mayoría de las respuestas de la base R abordan la situación donde solo un data.frame tiene columnas adicionales o que el data.frame resultante tendría la intersección de las columnas. Dado que el OP escribe , espero retener las columnas que no coinciden después del enlace , probablemente valga la pena publicar una respuesta utilizando los métodos de base R para abordar este problema.

A continuación, presento dos métodos R básicos: uno que altera los marcos de datos originales y otro que no. Además, ofrezco un método que generaliza el método no destructivo a más de dos data.frames.

Primero, obtengamos algunos datos de muestra.

 # sample data, variable c is in df1, variable d is in df2 df1 = data.frame(a=1:5, b=6:10, d=month.name[1:5]) df2 = data.frame(a=6:10, b=16:20, c = letters[8:12]) 

Dos data.frames, alterar originales
Para retener todas las columnas de ambos data.frames en un rbind (y permitir que la función funcione sin rbind un error), agregue columnas NA a cada data.frame con los nombres faltantes correspondientes completados usando setdiff .

 # fill in non-overlapping columns with NAs df1[setdiff(names(df2), names(df1))] <- NA df2[setdiff(names(df1), names(df2))] <- NA 

Ahora, rbind -em

 rbind(df1, df2) abdc 1 1 6 January  2 2 7 February  3 3 8 March  4 4 9 April  5 5 10 May  6 6 16  h 7 7 17  i 8 8 18  j 9 9 19  k 10 10 20  l 

Tenga en cuenta que las primeras dos líneas alteran los data.frames, df1 y df2 originales, y agrega el conjunto completo de columnas a ambos.


Dos data.frames, no alteran los originales
Para dejar intactos los data.frames originales, recorra primero los nombres que difieren, devuelva un vector con nombre de NA que se concatene en una lista con data.frame utilizando c . Luego, data.frame convierte el resultado en un data.frame apropiado para el rbind .

 rbind( data.frame(c(df1, sapply(setdiff(names(df2), names(df1)), function(x) NA))), data.frame(c(df2, sapply(setdiff(names(df1), names(df2)), function(x) NA))) ) 

Muchos marcos de datos, no alteran los originales
En el caso de que tenga más de dos data.frames, podría hacer lo siguiente.

 # put data.frames into list (dfs named df1, df2, df3, etc) mydflist <- mget(ls(pattern="df\\d+") # get all variable names allNms <- unique(unlist(lapply(mydflist, names))) # put em all together do.call(rbind, lapply(mydflist, function(x) data.frame(c(x, sapply(setdiff(allNms, names(x)), function(y) NA))))) 

Tal vez un poco mejor para no ver los nombres de las filas de data.frames originales? Entonces haz esto.

 do.call(rbind, c(lapply(mydflist, function(x) data.frame(c(x, sapply(setdiff(allNms, names(x)), function(y) NA)))), make.row.names=FALSE)) 

Escribí una función para hacer esto porque me gusta que mi código me diga si algo está mal. Esta función le indicará explícitamente qué nombres de columna no coinciden y si tiene un tipo no coincidente. Luego hará todo lo posible para combinar los data.frames de todos modos. La limitación es que solo puede combinar dos data.frames a la vez.

 ### combines data frames (like rbind) but by matching column names # columns without matches in the other data frame are still combined # but with NA in the rows corresponding to the data frame without # the variable # A warning is issued if there is a type mismatch between columns of # the same name and an attempt is made to combine the columns combineByName <- function(A,B) { a.names <- names(A) b.names <- names(B) all.names <- union(a.names,b.names) print(paste("Number of columns:",length(all.names))) a.type <- NULL for (i in 1:ncol(A)) { a.type[i] <- typeof(A[,i]) } b.type <- NULL for (i in 1:ncol(B)) { b.type[i] <- typeof(B[,i]) } a_b.names <- names(A)[!names(A)%in%names(B)] b_a.names <- names(B)[!names(B)%in%names(A)] if (length(a_b.names)>0 | length(b_a.names)>0){ print("Columns in data frame A but not in data frame B:") print(a_b.names) print("Columns in data frame B but not in data frame A:") print(b_a.names) } else if(a.names==b.names & a.type==b.type){ C <- rbind(A,B) return(C) } C <- list() for(i in 1:length(all.names)) { la <- all.names[i]%in%a.names pos.a <- match(all.names[i],a.names) typ.a <- a.type[pos.a] lb <- all.names[i]%in%b.names pos.b <- match(all.names[i],b.names) typ.b <- b.type[pos.b] if(la & lb) { if(typ.a==typ.b) { vec <- c(A[,pos.a],B[,pos.b]) } else { warning(c("Type mismatch in variable named: ",all.names[i],"\n")) vec <- try(c(A[,pos.a],B[,pos.b])) } } else if (la) { vec <- c(A[,pos.a],rep(NA,nrow(B))) } else { vec <- c(rep(NA,nrow(A)),B[,pos.b]) } C[[i]] <- vec } names(C) <- all.names C <- as.data.frame(C) return(C) } 

Solo por la documentación. Puede probar la biblioteca Stack y su función Stack en la siguiente forma:

 Stack(df_1, df_2) 

También tengo la impresión de que es más rápido que otros métodos para grandes conjuntos de datos.

Tal vez malinterpreté completamente su pregunta, pero el mensaje “Espero conservar las columnas que no coinciden después del enlace” me hace pensar que está buscando un left join o un left join right join similar a una consulta SQL. R tiene la función de merge que le permite especificar uniones izquierdas, derechas o internas similares a las tablas de unión en SQL.

Ya hay una gran pregunta y respuesta sobre este tema aquí: ¿Cómo unir (combinar) marcos de datos (interno, externo, izquierdo, derecho)?

gtools / smartbind no le gustó trabajar con Dates, probablemente porque era como un vector. Así que aquí está mi solución …

 sbind = function(x, y, fill=NA) { sbind.fill = function(d, cols){ for(c in cols) d[[c]] = fill d } x = sbind.fill(x, setdiff(names(y),names(x))) y = sbind.fill(y, setdiff(names(x),names(y))) rbind(x, y) } 
 rbind.ordered=function(x,y){ diffCol = setdiff(colnames(x),colnames(y)) if (length(diffCol)>0){ cols=colnames(y) for (i in 1:length(diffCol)) y=cbind(y,NA) colnames(y)=c(cols,diffCol) } diffCol = setdiff(colnames(y),colnames(x)) if (length(diffCol)>0){ cols=colnames(x) for (i in 1:length(diffCol)) x=cbind(x,NA) colnames(x)=c(cols,diffCol) } return(rbind(x, y[, colnames(x)])) }