Respuestas:
rbind.fill
del paquete plyr
puede ser lo que estás buscando.
Una solución más reciente es usar dplyr
la bind_rows
función que supongo es más eficiente que smartbind
.
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])
dplyr::bind_rows(df1, df2)
a b c
1 1 6 <NA>
2 2 7 <NA>
3 3 8 <NA>
4 4 9 <NA>
5 5 10 <NA>
6 11 16 A
7 12 17 B
8 13 18 C
9 14 19 D
10 15 20 E
ABC
no se puede convertir de carácter a numérico. ¿Hay alguna forma de convertir las columnas primero?
Puedes usar smartbind
desde el gtools
paquete.
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
smartbind
con dos marcos de datos grandes (en total, aproximadamente 3 * 10 ^ 6 filas) y lo aborté después de 10 minutos.
Si las columnas en df1 son un subconjunto de las de 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á data.table
siempre que los objetos se conviertan en data.table
objetos, por lo que
rbind(setDT(df1), setDT(df2), fill=TRUE)
También funcionará en esta situación. Esto puede ser preferible cuando tienes un par de data.tables y no quieres construir una lista.
intersect
enfoque, solo funcionan para 2 marcos de datos y no se generalizan fácilmente.
La mayoría de las respuestas de la base R abordan la situación en la que 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 que utilice métodos de base R para abordar este problema.
A continuación, presento dos métodos base R: uno que altera los data.frames 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 marcos de datos, alterar originales
Para retener todas las columnas de ambos data.frames en un rbind
(y permitir que la función funcione sin provocar un error), agregue columnas NA a cada data.frame con los nombres faltantes correspondientes rellenados utilizando 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 dos primeras líneas alteran los data.frames originales, df1 y df2, agregando el conjunto completo de columnas a ambos.
Dos marcos de datos, no altere los originales
Para dejar intactos los marcos de datos originales, primero recorra los nombres que difieren, devuelva un vector con nombre de NA que se concatenan en una lista con el marco de datos c
. Luego, data.frame
convierte el resultado en un data.frame apropiado para 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, puede 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)))))
¿Quizás sea un poco más agradable 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))
mydflist <- list(as, dr, kr, hyt, ed1, of)
. Esto debería construir un objeto de lista que no aumente el tamaño de su entorno, sino que solo apunte a cada elemento de la lista (siempre que no altere ninguno de los contenidos después). Después de la operación, elimine el objeto de la lista, solo para estar seguro.
También podría simplemente extraer los nombres de columna comunes.
> cols <- intersect(colnames(df1), colnames(df2))
> rbind(df1[,cols], df2[,cols])
Escribí una función para hacer esto porque me gusta que mi código me diga si algo está mal. Esta función le dirá explícitamente qué nombres de columna no coinciden y si tiene un tipo no coincidente. Entonces hará todo lo posible para combinar 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)
}
Tal vez leí completamente su pregunta, pero el "Espero retener las columnas que no coinciden después del enlace" me hace pensar que está buscando una consulta SQL left join
o right join
similar. R tiene elmerge
función que le permite especificar uniones izquierdas, derechas o internas similares a unir tablas en SQL.
Ya hay una gran pregunta y respuesta sobre este tema aquí: ¿Cómo unir (fusionar) marcos de datos (interno, externo, izquierdo, derecho)?
A gtools / smartbind no le gustaba trabajar con Dates, probablemente porque era 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)
}
Solo por la documentación. Puede probar la Stack
biblioteca y su función Stack
de 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.
También podría usar sjmisc::add_rows()
, que usa dplyr::bind_rows()
, pero a diferencia bind_rows()
, add_rows()
conserva los atributos y, por lo tanto, es útil para los datos etiquetados .
Vea el siguiente ejemplo con un conjunto de datos etiquetado. La frq()
función imprime tablas de frecuencia con etiquetas de valor, si los datos están etiquetados.
library(sjmisc)
library(dplyr)
data(efc)
# select two subsets, with some identical and else different columns
x1 <- efc %>% select(1:5) %>% slice(1:10)
x2 <- efc %>% select(3:7) %>% slice(11:20)
str(x1)
#> 'data.frame': 10 obs. of 5 variables:
#> $ c12hour : num 16 148 70 168 168 16 161 110 28 40
#> ..- attr(*, "label")= chr "average number of hours of care per week"
#> $ e15relat: num 2 2 1 1 2 2 1 4 2 2
#> ..- attr(*, "label")= chr "relationship to elder"
#> ..- attr(*, "labels")= Named num 1 2 3 4 5 6 7 8
#> .. ..- attr(*, "names")= chr "spouse/partner" "child" "sibling" "daughter or son -in-law" ...
#> $ e16sex : num 2 2 2 2 2 2 1 2 2 2
#> ..- attr(*, "label")= chr "elder's gender"
#> ..- attr(*, "labels")= Named num 1 2
#> .. ..- attr(*, "names")= chr "male" "female"
#> $ e17age : num 83 88 82 67 84 85 74 87 79 83
#> ..- attr(*, "label")= chr "elder' age"
#> $ e42dep : num 3 3 3 4 4 4 4 4 4 4
#> ..- attr(*, "label")= chr "elder's dependency"
#> ..- attr(*, "labels")= Named num 1 2 3 4
#> .. ..- attr(*, "names")= chr "independent" "slightly dependent" "moderately dependent" "severely dependent"
bind_rows(x1, x1) %>% frq(e42dep)
#>
#> # e42dep <numeric>
#> # total N=20 valid N=20 mean=3.70 sd=0.47
#>
#> val frq raw.prc valid.prc cum.prc
#> 3 6 30 30 30
#> 4 14 70 70 100
#> <NA> 0 0 NA NA
add_rows(x1, x1) %>% frq(e42dep)
#>
#> # elder's dependency (e42dep) <numeric>
#> # total N=20 valid N=20 mean=3.70 sd=0.47
#>
#> val label frq raw.prc valid.prc cum.prc
#> 1 independent 0 0 0 0
#> 2 slightly dependent 0 0 0 0
#> 3 moderately dependent 6 30 30 30
#> 4 severely dependent 14 70 70 100
#> NA NA 0 0 NA NA
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)]))
}
rbind.fill
ybind_rows()
ambos silenciosamente sueltan nombres de fila.