Unir datos – un repaso de las diferencias entre merge, inner join, left join, right join, full join, cbind y rbind cuando se usa objetos tipo data.table en R

Esta semana alguien me preguntó como realizar unos joins utilizando objetos tipo data.table, esta persona dudaba si lo correcto era usar merge, rbind o cbind así que hago esta entrada al blog aprovechando el script que se usó para explicarle las diferencias.

Cuando queremos «unir» dos conjuntos de datos en general se puede hacer lo siguiente:

  • Agregar Filas: Aumentar las filas de un conjunto de datos debajo del otro.
  • Agregar Columnas: Aumentar las columnas de un conjunto de datos a lado del otro.
  • Join, buscarv o vlookup: En este caso se tienen unas columnas o variables que hacen las veces de «key» o «id» y al primer conjunto se le agrega el contenido del segundo conjunto de datos si el «key» o «id» del segundo conjunto corresponde con el «key» o «id» del primer conjunto de datos. Buscarv y Vlookup son términos los usuarios de excel, estos son un caso particular de «Join» que es el término que conocemos quienes «picamos código» en R. Hay diversos tipos de Join, en resumen:
    • Inner Join: Devuelve sólo los datos de los id que se repiten en ambos conjuntos de datos.
    • Left Join: Devuelve todo el conjunto de dato de la tabla de la izquierda mientras que de la tabla derecha sólo se devuelve los datos de los id que coinciden con el primer conjunto de datos (esto es como el buscarv o vlookup de excel).
    • Right Join: Devuelve todo el conjunto de dato de la tabla de la derecha mientras que de la tabla izquierda sólo se devuelve los datos de los id que coinciden con el segundo conjunto de datos.
    • Full Join: Devolver todas los datos sin importar si hay coincidencia o no entre los ids de ambos conjuntos de datos.

El paquete data.table es una excelente opción para realizar tareas más eficientemente en R, pero para aprender a usarlo se requiere un poco de lectura y paciencia, pueden revisar su vignettes que es una buena introducción.

Para realizar Joins se puede usar la sintaxis Dt[X] del paquete data.table o usar el comando merge como si se tratase con objetos de tipo data.frame.  Dt[X] es más eficiente que merge, por contraparte merge es más intuitivo (al menos para el usuario promedio de R). Para entender un poco Dt[X] se debe resaltar que al escribir Dt[X] el software va a buscar los elementos del objeto Dt basándose en las variables «key» o «id» de X, es decir, se toma como base al objeto X.

En el siguiente script se muestra como realizar Inner Join, Left Join (Buscarv), Right Join, Full Join, agregar columnas y filas utilizando 3 objetos de tipo data.table (nótese que los output inician con ###, como cuandos se usa knitr)

# Cargar el paquete
library(data.table)

# Se crea 3 data.frames con dos columnas 'id' de nombre idéntico pero con
# columnas diferentes entre ellos y además en los 'id' de cada uno de
# ellos van a haber elementos coincidentes y diferentes
df1 <- data.frame(id1 = c(11, 12, 13, 14), id2 = c(21, 22, 23, 24), c11 = c(1, 
    2, 3, 4), c12 = c("a", "b", "c", "d"))
df2 <- data.frame(id1 = c(99, 12, 13, 14), id2 = c(98, 22, 23, 24), c21 = c(5, 
    6, 7, 8), c22 = c("e", "f", "g", "h"))
df3 <- data.frame(id1 = c(999, 9999, 13, 14), id2 = c(998, 9998, 23, 24), c31 = c(9, 
    10, 11, 12), c32 = c("i", "j", "k", "l"))

# Mostrar los 3 data.frames
df1
##   id1 id2 c11 c12
## 1  11  21   1   a
## 2  12  22   2   b
## 3  13  23   3   c
## 4  14  24   4   d
df2
##   id1 id2 c21 c22
## 1  99  98   5   e
## 2  12  22   6   f
## 3  13  23   7   g
## 4  14  24   8   h
df3
##    id1  id2 c31 c32
## 1  999  998   9   i
## 2 9999 9998  10   j
## 3   13   23  11   k
## 4   14   24  12   l

# Transformar los data.frame a data.table indicando que la 'key' son las
# columnas id1 e id2
Dt1 <- data.table(df1, key = c("id1", "id2"))
Dt2 <- data.table(df2, key = c("id1", "id2"))
Dt3 <- data.table(df3, key = c("id1", "id2"))

# *** PARA HACER INNER JOIN *** 
# En el Inner Join se pretende devolver sólo los id 
# que se repiten en ambos conjuntos de datos 

# Inner join ente Dt1 y Dt2 con comando de data.table
Dt1[Dt2, nomatch = 0]
##    id1 id2 c11 c12 c21 c22
## 1:  12  22   2   b   6   f
## 2:  13  23   3   c   7   g
## 3:  14  24   4   d   8   h
# Inner join ente Dt1 y Dt2, usando merge
merge(x = Dt1, y = Dt2)
##    id1 id2 c11 c12 c21 c22
## 1:  12  22   2   b   6   f
## 2:  13  23   3   c   7   g
## 3:  14  24   4   d   8   h
# Inner join ente Dt1, Dt2 y Dt3 con comando de data.table
Dt1[Dt2[Dt3, nomatch = 0], nomatch = 0]
##    id1 id2 c11 c12 c21 c22 c31 c32
## 1:  13  23   3   c   7   g  11   k
## 2:  14  24   4   d   8   h  12   l
# Inner join usando Reduce y merge
Reduce(merge, list(Dt1, Dt2, Dt3))
##    id1 id2 c11 c12 c21 c22 c31 c32
## 1:  13  23   3   c   7   g  11   k
## 2:  14  24   4   d   8   h  12   l

# *** PARA HACER RIGHT JOIN *** 
# En el Right Join se pretende devolver todos los id 
# de la tabla de la derecha, y sólo los id con match 
# de la tabla izquierda 

# Right join ente Dt1 y Dt2 con comando de data.table
Dt1[Dt2, nomatch = NA]
##    id1 id2 c11 c12 c21 c22
## 1:  12  22   2   b   6   f
## 2:  13  23   3   c   7   g
## 3:  14  24   4   d   8   h
## 4:  99  98  NA  NA   5   e
# Right join ente Dt1 y Dt2, usando merge
merge(x = Dt1, y = Dt2, all.y = TRUE)
##    id1 id2 c11 c12 c21 c22
## 1:  12  22   2   b   6   f
## 2:  13  23   3   c   7   g
## 3:  14  24   4   d   8   h
## 4:  99  98  NA  NA   5   e
# Right join ente Dt1, Dt2 y Dt3 con comando de data.table
Dt1[Dt2[Dt3, nomatch = NA], nomatch = NA]
##     id1  id2 c11 c12 c21 c22 c31 c32
## 1:   13   23   3   c   7   g  11   k
## 2:   14   24   4   d   8   h  12   l
## 3:  999  998  NA  NA  NA  NA   9   i
## 4: 9999 9998  NA  NA  NA  NA  10   j
# Para hacer un Right join entre varios data.table se va a crear la
# funcion mergeRight que no es mas que el merge normal con el
# parámetro all.y= TRUE, esto forzará a mostrar todos los datos del
# segundo conjunto y sólo los datos con match del primer conjunto
mergeRight <- function(x, y) merge(x, y, all.y = TRUE)
# Usando Reduce y mergeRight para hacer un Right join entre Dt1 y Dt2 
# se puede ver como se aumentan NAs donde no hay match
Reduce(mergeRight, list(Dt1, Dt2, Dt3))
##     id1  id2 c11 c12 c21 c22 c31 c32
## 1:   13   23   3   c   7   g  11   k
## 2:   14   24   4   d   8   h  12   l
## 3:  999  998  NA  NA  NA  NA   9   i
## 4: 9999 9998  NA  NA  NA  NA  10   j

# *** PARA HACER LEFT JOIN *** 
# En el Left Join se pretende devolver todos los id de 
# la tabla de la izquierda, y sólo los id con match de 
# la tabla derecha 

# Left join ente Dt1 y Dt2 con comando de data.table 
# Para hacer un Left join en data.table simplemente se hace 
# un righ Join cambiando el orden de los data.table. 
# Se puede ver que el único problema con este método 
# es el orden de las columnas pues se muestra primero 
# las columnas del Dt2
Dt2[Dt1, nomatch = NA]
##    id1 id2 c21 c22 c11 c12
## 1:  11  21  NA  NA   1   a
## 2:  12  22   6   f   2   b
## 3:  13  23   7   g   3   c
## 4:  14  24   8   h   4   d
# Left join ente Dt1 y Dt2, usando merge
merge(x = Dt1, y = Dt2, all.x = TRUE)
##    id1 id2 c11 c12 c21 c22
## 1:  11  21   1   a  NA  NA
## 2:  12  22   2   b   6   f
## 3:  13  23   3   c   7   g
## 4:  14  24   4   d   8   h
# Left join ente Dt1, Dt2 y Dt3 con comando de data.table idem: notar el
# orden de las columnas
Dt3[Dt2[Dt1, nomatch = NA], nomatch = NA]
##    id1 id2 c31 c32 c21 c22 c11 c12
## 1:  11  21  NA  NA  NA  NA   1   a
## 2:  12  22  NA  NA   6   f   2   b
## 3:  13  23  11   k   7   g   3   c
## 4:  14  24  12   l   8   h   4   d
# Para hacer un Left join entre varios data.table se va a crear la
# funcion mergeLeft que no es mas que el merge normal con el
# parámetro all.x= TRUE, esto forzará a mostrar todos los datos del
# primer conjunto y sólo los datos con match del segundo conjunto
mergeLeft <- function(x, y) merge(x, y, all.x = TRUE)
# Usando Reduce y mergeLeft para hacer un Full join entre Dt1, Dt2 y Dt3
# se puede ver como se aumentan NAs donde no hay match
Reduce(mergeLeft, list(Dt1, Dt2, Dt3))
##    id1 id2 c11 c12 c21 c22 c31 c32
## 1:  11  21   1   a  NA  NA  NA  NA
## 2:  12  22   2   b   6   f  NA  NA
## 3:  13  23   3   c   7   g  11   k
## 4:  14  24   4   d   8   h  12   l

# *** PARA HACER FULL JOIN *** 
# En el Full Join se pretende devolver todos los id de las 
# tablas sin importar si hay match o no 

# Full join ente Dt1 y Dt2 con comando de data.table 
# Para hacer un Full join en data.table se puede crear un objeto 
# que contenga todos los 'ids' que queremos y utilizar este 
# objeto como el índice de nusetro join, asi: 

# Crear una variable con la unión de los 'id' de las data.table
ids <- unique(rbind(Dt1[, list(id1, id2)], Dt2[, list(id1, id2)]))
ids
##    id1 id2
## 1:  11  21
## 2:  12  22
## 3:  13  23
## 4:  14  24
## 5:  99  98
# Full join ente Dt1 y Dt2 con comando de data.table 
# (usando el objeto ids)
Dt1[Dt2[ids]]
##    id1 id2 c11 c12 c21 c22
## 1:  11  21   1   a  NA  NA
## 2:  12  22   2   b   6   f
## 3:  13  23   3   c   7   g
## 4:  14  24   4   d   8   h
## 5:  99  98  NA  NA   5   e
# Full join ente Dt1 y Dt2, usando merge
merge(x = Dt1, y = Dt2, all = TRUE)
##    id1 id2 c11 c12 c21 c22
## 1:  11  21   1   a  NA  NA
## 2:  12  22   2   b   6   f
## 3:  13  23   3   c   7   g
## 4:  14  24   4   d   8   h
## 5:  99  98  NA  NA   5   e
# Full join ente Dt1, Dt2 y Dt3 con comando de data.table
# Crear una variable con la unión de los 'id' de las data.table
ids <- unique(rbind(Dt1[, list(id1, id2)], Dt2[, list(id1, id2)], Dt3[, list(id1, id2)]))
ids
##     id1  id2
## 1:   11   21
## 2:   12   22
## 3:   13   23
## 4:   14   24
## 5:   99   98
## 6:  999  998
## 7: 9999 9998
# Full join ente Dt1, Dt2 y Dt3 con comando de data.table 
# (usango el objeto ids)
Dt1[Dt2[Dt3[ids]]]
##     id1  id2 c11 c12 c21 c22 c31 c32
## 1:   11   21   1   a  NA  NA  NA  NA
## 2:   12   22   2   b   6   f  NA  NA
## 3:   13   23   3   c   7   g  11   k
## 4:   14   24   4   d   8   h  12   l
## 5:   99   98  NA  NA   5   e  NA  NA
## 6:  999  998  NA  NA  NA  NA   9   i
## 7: 9999 9998  NA  NA  NA  NA  10   j
# Para hacer un Full join entre varios data.table se va a crear la
# funcion mergeFull que no es mas que el merge normal con el
# parámetro all = TRUE, esto forzará a mostrar todos los id del
# primer y segundo conjunto 
mergeFull <- function(x, y) merge(x, y, all = TRUE)
# Usando Reduce y mergeFull para hacer un Full join entre Dt1, Dt2 y Dt3
# se puede ver como se aumentan NAs donde no hay match
Reduce(mergeFull, list(Dt1, Dt2, Dt3))
##     id1  id2 c11 c12 c21 c22 c31 c32
## 1:   11   21   1   a  NA  NA  NA  NA
## 2:   12   22   2   b   6   f  NA  NA
## 3:   13   23   3   c   7   g  11   k
## 4:   14   24   4   d   8   h  12   l
## 5:   99   98  NA  NA   5   e  NA  NA
## 6:  999  998  NA  NA  NA  NA   9   i
## 7: 9999 9998  NA  NA  NA  NA  10   j

# *** PARA AUMENTAR FILAS - RBIND ***
# El comando rbind se usa para aumentar filas a un 
# conjunto de datos, lo siguiente va a producir un
# error pues las columnas no son las mismas
rbind(Dt1, Dt2)
## Error: Some colnames of argument 2 (c21,c22) are not present in colnames
## of item 1. If an argument has colnames they can be in a different order,
## but they must all be present. Alternatively, you can drop names (by using
## an unnamed list) and the columns will then be joined by position. Or, set
## use.names=FALSE.

# *** PARA AUMENTAR COLUMNAS - CBIND *** 
# El comando cbind se usa para aumentar columnas a 
# un conjunto de datos.  
# Aquí se puede ver como el cbind (sin opciones activas) 
# no respeta los indices simplemente pone un
# conjunto a lado de otro (ver el orden de los id)
cbind(Dt1, Dt2)
##    id1 id2 c11 c12 id1 id2 c21 c22
## 1:  11  21   1   a  12  22   6   f
## 2:  12  22   2   b  13  23   7   g
## 3:  13  23   3   c  14  24   8   h
## 4:  14  24   4   d  99  98   5   e
cbind(Dt1, Dt2, Dt3)
##    id1 id2 c11 c12 id1 id2 c21 c22  id1  id2 c31 c32
## 1:  11  21   1   a  12  22   6   f   13   23  11   k
## 2:  12  22   2   b  13  23   7   g   14   24  12   l
## 3:  13  23   3   c  14  24   8   h  999  998   9   i
## 4:  14  24   4   d  99  98   5   e 9999 9998  10   j

Created by Pretty R at inside-R.org

 

El lector más perspicaz se habrá dado cuenta de como realizar un Left Join sin tener el problema con el orden de las columnas, o no? Les dejo la interrogante.

2 comentarios

    • Raul en 13 septiembre, 2017 a las 2:35
    • Responder

    Justo lo que necesitaba, excelentemente explicado. Muchas gracias

    • Elsa Yolanda Nunez en 11 julio, 2017 a las 23:15
    • Responder

    Excelente ayuda.

Deja un comentario

Tu email nunca se publicará.


Ir a la barra de herramientas