Unir varios data.frames en un sólo paso: 'merge' y 'Reduce'

Para unir data.frames en R existe la función merge que con todas sus opciones hace que esto sea una tarea fácil, sin embargo, se vuelve una tarea aburrida y repetitiva cuando se tienen muchos data.frames para unir, puesto que la función merge solo permite la unión de dos a la vez, véase la definición de los argumentos y la ayuda de dicha función. Pese a este inconveniente, la función merge puede ser utilizada para unir tantos data.frames como queramos si la combinamos apropiadamente con la función Reduce, el único requisito que se ha de tener en cuenta es que el nombre de la variable identificadora sea el mismo en todos los data.frames a ser unidos. Vemos unos ejemplos.

Consideremos los siguientes data.frames:

A <- data.frame(id = c("A", "B", "C", "D"), age = c(24, 25, 17, 19), height = c(1.8, 
    1.9, 1.75, 1.65))
A
##   id age height
## 1  A  24   1.80
## 2  B  25   1.90
## 3  C  17   1.75
## 4  D  19   1.65
B <- data.frame(gender = c("M", "M", "F", "F"), id = c("A", "B", "C", "D"))
B
##   gender id
## 1      M  A
## 2      M  B
## 3      F  C
## 4      F  D
C <- data.frame(id = c("A", "B", "C", "D"), math = c(6.5, 8.9, 7.4, 9.2), science = c(7.2, 
    8.4, 6.5, 8.7))
C
##   id math science
## 1  A  6.5     7.2
## 2  B  8.9     8.4
## 3  C  7.4     6.5
## 4  D  9.2     8.7
D <- data.frame(id = c("A", "B", "C", "D"), eyes = c("blue", "brown", "green", 
    "black"))
C
##   id math science
## 1  A  6.5     7.2
## 2  B  8.9     8.4
## 3  C  7.4     6.5
## 4  D  9.2     8.7
# Nótese que todos ellos tienen como variable identificadora id

## Uniendo los dataframes con merge
AB <- merge(A, B)  # une A con B
ABC <- merge(AB, C)  # a la unión de A y B le agrega C
ABCD <- merge(ABC, D)  # a la unión de A, B y C le agrega D
ABCD  # resultado final
##   id age height gender math science  eyes
## 1  A  24   1.80      M  6.5     7.2  blue
## 2  B  25   1.90      M  8.9     8.4 brown
## 3  C  17   1.75      F  7.4     6.5 green
## 4  D  19   1.65      F  9.2     8.7 black

Como se ha visto, se tiene que ir uniendo dos data.frames en cada paso. ¿Hay alguna manera de hacerlo en un sólo paso? La respuesta es Sí y sólo requiere combinar la función merge con Reduce y tener en cuenta que la variable identificadora en cada data.frame tenga el mismo nombre (id en este ejemplo), no hace falta que esta variable ocupe la misma posición en cada data.frame, sólo se requiere que sea nombrada de la misma manera. El ejemplo anterior se puede hacer tan sólo con una simple linea de comandos:

Reduce(merge, list(A, B, C, D))
##   id age height gender math science  eyes
## 1  A  24   1.80      M  6.5     7.2  blue
## 2  B  25   1.90      M  8.9     8.4 brown
## 3  C  17   1.75      F  7.4     6.5 green
## 4  D  19   1.65      F  9.2     8.7 black

Como se ve en ambos casos se obtiene el mismo resultado, sin embargo en el segundo, se ha hecho todo de una vez.

Otro ejemplo:

authors <- data.frame(surname = I(c("Tukey", "Venables", "Tierney", "Ripley", 
    "McNeil")), nationality = c("US", "Australia", "US", "UK", "Australia"), 
    deceased = c("yes", rep("no", 4)))

books <- data.frame(name = I(c("Tukey", "Venables", "Tierney", "Ripley", "Ripley", 
    "McNeil", "R Core")), title = c("Exploratory Data Analysis", "Modern Applied Statistics ...", 
    "LISP-STAT", "Spatial Statistics", "Stochastic Simulation", "Interactive Data Analysis", 
    "An Introduction to R"), other.author = c(NA, "Ripley", NA, NA, NA, NA, 
    "Venables & Smith"))

colnames(authors)[1] <- "name"  # cambiando el ID de authors para que sea igual para todos

edition <- data.frame(name = authors[, 1], edition = c(4, 2, 3, 1, 2))  # invento
year <- data.frame(name = authors[, 1], year = 2000:2004)  # invento

Los data.frames authors y books son los ejemplos que se encuentran en la ayuda de merge',editionyyear fueron inventos míos para ilustrar el ejemplo.

Uniendo los data.frames usando merge, dos a la vez.

m1 <- merge(authors, books)
m2 <- merge(m1, edition)
(m3 <- merge(m2, year))  # El resultado final es:
##       name nationality deceased                         title other.author
## 1   McNeil   Australia       no     Interactive Data Analysis         <NA>
## 2   Ripley          UK       no            Spatial Statistics         <NA>
## 3   Ripley          UK       no         Stochastic Simulation         <NA>
## 4  Tierney          US       no                     LISP-STAT         <NA>
## 5    Tukey          US      yes     Exploratory Data Analysis         <NA>
## 6 Venables   Australia       no Modern Applied Statistics ...       Ripley
##   edition year
## 1       2 2004
## 2       1 2003
## 3       1 2003
## 4       3 2002
## 5       4 2000
## 6       2 2001

Uniéndolos todos a la vez:

Reduce(merge, list(authors, books, edition, year))
##       name nationality deceased                         title other.author
## 1   McNeil   Australia       no     Interactive Data Analysis         <NA>
## 2   Ripley          UK       no            Spatial Statistics         <NA>
## 3   Ripley          UK       no         Stochastic Simulation         <NA>
## 4  Tierney          US       no                     LISP-STAT         <NA>
## 5    Tukey          US      yes     Exploratory Data Analysis         <NA>
## 6 Venables   Australia       no Modern Applied Statistics ...       Ripley
##   edition year
## 1       2 2004
## 2       1 2003
## 3       1 2003
## 4       3 2002
## 5       4 2000
## 6       2 2001

Como se ha visto, si se combina merge con Reduce se pueden unir tantos data.frames en un sólo paso, sin embargo, con el enfoque tradicional de usar sólo merge se tiene que repetir la operación \( K-1 \) veces donde \( K \) es el número de data.frames.

7 comentarios:

  1. Justo lo que necesitaba, me habían recomendado ya buscar la función merge, pero la combinación con reduce me va a venor fenomenal.
    Muchas gracias

    ResponderEliminar
  2. esta bien. aunque eso mismo se haría en excel muy rápidamente. algo que si me parece interesante y que no puede hacer con excel fácilmente es usar merge para combinar dos tablas con tamaños diferentes.

    Podrias hacer un tutorial usando by.x y by.y para combinar data.frames?

    ResponderEliminar
  3. Y como lo hariamos si... Por ejemplo, tuvimos que particionar una base de datos en excel por su tamaño, pero queremos consolidarla para dejarla como maestra y usarla en R...???

    Esta vez no agregando variables, si no que información (filas)

    Gracias.

    ResponderEliminar
  4. cómo se podrían juntar bases con distinto número de observaciones, pero igual variables?

    ResponderEliminar
  5. como se pueden cruzar varias bases de datos, que conserve el tamaño de la base de datos más grande y que no sume los otros registros. Por ejemplo, que en una base aparezca, id más nombre, id mas color de ojos, id más estatura, pero que cada base no tienen los mismos No. de id, sino diferentes pero que me traiga las variables de estatura, color de ojos ,nombre de la id, que solo aparezca en la base de datos grande, y que si no esta esa id en la base de datos más grande, no me sume las que no estan ahi, pero si en las otras bases de datos. Gracias.

    ResponderEliminar