Combinar dos tramas de datos por filas (rbind) cuando tienen diferentes conjuntos de columnas

StackOverflow https://stackoverflow.com/questions/3402371

  •  25-09-2019
  •  | 
  •  

Pregunta

¿Es posible la fila se unen dos tramas de datos que no tienen el mismo conjunto de columnas? Tengo la esperanza de conservar las columnas que no coinciden después del aprieto.

¿Fue útil?

Solución

rbind.fill del plyr paquete podría ser lo que buscas.

Otros consejos

Una solución más reciente es el uso de la función de dplyr bind_rows que yo supongo que es más eficiente que smartbind.

Se puede utilizar smartbind del 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 de gl1 es un subconjunto de los de gl2 (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 el trabajo en data.table siempre que los objetos se convierten en objetos data.table, por lo

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

También se trabajará en esta situación. Esto puede ser preferible cuando se tiene un par de data.tables y no desea construir una lista.

La mayor parte de la base R respuestas frente a la situación donde sólo una hoja.de.datos tiene columnas adicionales o que la hoja.de.datos resultante tendría la intersección de las columnas. Desde las escrituras OP Tengo la esperanza de retener las columnas que no coinciden después de la bind , una respuesta utilizando los métodos de base R para tratar este tema es probablemente vale la publicación.

A continuación, presento dos métodos de base R: Uno que altera la data.frames originales, y uno que no lo hace. Además, ofrezco un método que generaliza el método no destructivo para más de dos data.frames.

En primer lugar, vamos a obtener algunos datos de la 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, originales alter
Con el fin de conservar todas las columnas de ambos data.frames en un rbind (y permitir que la función de trabajo sin que se produzca un error), se agrega columnas de NA a cada hoja.de.datos con la apropiada nombres se llenaron utilizando setdiff falta.

# 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 las data.frames originales, DF1 y DF2, la adición de todo el conjunto de columnas a ambos.


Dos data.frames, hacerlo no los originales alter
Para dejar los originales intactos data.frames primer bucle, a través de los nombres que se diferencian, devolver un vector llamado de AN que se concatenan en una lista con el hoja.de.datos usando c. Entonces, 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 data.frames, hacerlo no los originales alter
En el caso de que usted tiene más de dos data.frames, se 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)))))

Tal vez un poco más agradable para no ver los nombres de las filas de data.frames originales? Luego de hacer 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))

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

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

Me escribió una función para hacer esto porque me gusta mi código para decirme si algo está mal. Esta función le dirá explícitamente qué nombres de columna no coinciden y si usted tiene una coincidencia de tipos. A continuación, se hará todo lo posible para combinar los data.frames de todos modos. La limitación es que sólo se 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)
}

Sólo por la documentación. Puede buscar en la biblioteca Stack y su Stack función 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.

Tal vez leí mal por completo su pregunta, pero el "yo soy la esperanza de retener las columnas que no coinciden después del aprieto" me hace pensar que busca un left join o right join similar a una consulta SQL. R tiene la función merge que le permite especificar la izquierda, derecha, o combinaciones internas similares a unir tablas en SQL.

Ya existe una gran pregunta y respuesta en este tema aquí: Cómo unirse (merge) tramas de datos (interior, exterior, izquierda, derecha)?

gtools / smartbind no me gustó trabajar con fechas, probablemente debido a que se as.vectoring. 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)
}

También podría usar sjmisc::add_rows() , que utiliza dplyr::bind_rows(), pero a diferencia de bind_rows() , atributos conservas add_rows() y por lo tanto es útil para datos etiquetados .

Ver siguiente ejemplo con un conjunto de datos etiquetado. Las tablas de frecuencias impresiones frq() funciones con etiquetas de valor, si los datos se etiqueta.

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)]))
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top