tablas - Combina dos marcos de datos por filas(rbind) cuando tienen diferentes conjuntos de columnas
insertar columna en data frame r (12)
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)) {
l.a <- all.names[i]%in%a.names
pos.a <- match(all.names[i],a.names)
typ.a <- a.type[pos.a]
l.b <- all.names[i]%in%b.names
pos.b <- match(all.names[i],b.names)
typ.b <- b.type[pos.b]
if(l.a & l.b) {
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 (l.a) {
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)
}
¿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.
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)
a b d c
1 1 6 January <NA>
2 2 7 February <NA>
3 3 8 March <NA>
4 4 9 April <NA>
5 5 10 May <NA>
6 6 16 <NA> h
7 7 17 <NA> i
8 8 18 <NA> j
9 9 19 <NA> k
10 10 20 <NA> 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))
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
a b c
1.1 1 6 <NA>
1.2 2 7 <NA>
1.3 3 8 <NA>
1.4 4 9 <NA>
1.5 5 10 <NA>
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)])
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)?
También podría simplemente sacar los nombres de columna comunes.
> cols <- intersect(colnames(df1), colnames(df2))
> rbind(df1[,cols], df2[,cols])
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.
Una solución más reciente es usar la función bind_rows
, que supongo que es más eficiente que smartbind
.
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.fill
del paquete plyr
puede ser lo que estás buscando.
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)]))
}