Unir data frames en R con dplyr: left_join, inner_join, anti_join y más

Unir data frames en R con left_join y otros joins de dplyr

dplyr ofrece una familia de funciones *_join para combinar dos data frames a partir de una o varias columnas clave. Los joins de mutación (inner_join, left_join, right_join y full_join) añaden columnas de una tabla a la otra, mientras que los joins de filtrado (semi_join y anti_join) solo conservan o descartan filas. En este tutorial aprenderás qué join elegir en cada caso, cómo unir tablas cuyas claves tienen nombres distintos y cómo resolver los problemas más habituales: claves duplicadas, el aviso “many-to-many”, filas de más y valores NA después del join.

Datos de ejemplo

En este tutorial utilizaremos dos data frames pequeños: una tabla de clientes y una tabla de pedidos. Ambas comparten la columna id, que identifica al cliente. Fíjate en que no todas las claves coinciden: David (id = 4) no tiene pedidos y el pedido 105 pertenece a un cliente (id = 5) que no está en la tabla de clientes. Además, Ana tiene dos pedidos.

library(dplyr)

clientes <- data.frame(id = c(1, 2, 3, 4),
                       nombre = c("Ana", "Ben", "Carla", "David"),
                       ciudad = c("Madrid", "Londres", "París", "Roma"))

pedidos <- data.frame(pedido_id = c(101, 102, 103, 104, 105),
                      id = c(1, 1, 2, 3, 5),
                      importe = c(50, 20, 35, 80, 15))

clientes
pedidos
  id nombre  ciudad
1  1    Ana  Madrid
2  2    Ben Londres
3  3  Carla   París
4  4  David    Roma

  pedido_id id importe
1       101  1      50
2       102  1      20
3       103  2      35
4       104  3      80
5       105  5      15

Todas las funciones de join comparten la misma sintaxis básica: el primer data frame (x), el segundo (y) y las columnas clave, que se indican en el argumento by. También funcionan con el operador pipe, de modo que clientes %>% left_join(pedidos, by = "id") equivale a left_join(clientes, pedidos, by = "id").

left_join(x, y, by = NULL, suffix = c(".x", ".y"), ..., relationship = NULL)

Indica siempre el argumento by. Si lo omites, dplyr une por todas las columnas que tengan el mismo nombre en ambas tablas y muestra un mensaje como Joining with by = join_by(id). Es cómodo para explorar, pero puede unir sin avisar por columnas que no esperabas.

Joins de mutación

Los joins de mutación combinan las columnas de los dos data frames. La diferencia entre ellos está en qué filas se conservan cuando una clave no tiene pareja en la otra tabla.

inner_join

inner_join conserva solo las filas cuya clave existe en las dos tablas. David (sin pedidos) y el pedido 105 (cliente desconocido) desaparecen.

inner_join(clientes, pedidos, by = "id")
  id nombre  ciudad pedido_id importe
1  1    Ana  Madrid       101      50
2  1    Ana  Madrid       102      20
3  2    Ben Londres       103      35
4  3  Carla   París       104      80

left_join

left_join es el join más utilizado. Conserva todas las filas de la primera tabla (x) y le añade la información correspondiente de la segunda (y). Cuando una fila de x no tiene pareja, las columnas nuevas se rellenan con NA, como ocurre con David.

left_join(clientes, pedidos, by = "id")
  id nombre  ciudad pedido_id importe
1  1    Ana  Madrid       101      50
2  1    Ana  Madrid       102      20
3  2    Ben Londres       103      35
4  3  Carla   París       104      80
5  4  David    Roma        NA      NA

right_join

right_join hace lo contrario: conserva todas las filas de la segunda tabla (y). Aquí se mantiene el pedido 105, pero su nombre y su ciudad son NA porque el cliente 5 no existe en clientes.

right_join(clientes, pedidos, by = "id")
  id nombre  ciudad pedido_id importe
1  1    Ana  Madrid       101      50
2  1    Ana  Madrid       102      20
3  2    Ben Londres       103      35
4  3  Carla   París       104      80
5  5   <NA>    <NA>       105      15

En la práctica, right_join(x, y) devuelve las mismas filas que left_join(y, x), solo que con las columnas en otro orden. Por eso mucha gente usa siempre left_join y simplemente cambia el orden de las tablas.

full_join

full_join conserva todas las filas de ambas tablas. Las filas sin pareja en cualquiera de los dos lados reciben NA en las columnas que proceden de la otra tabla.

full_join(clientes, pedidos, by = "id")
  id nombre  ciudad pedido_id importe
1  1    Ana  Madrid       101      50
2  1    Ana  Madrid       102      20
3  2    Ben Londres       103      35
4  3  Carla   París       104      80
5  4  David    Roma        NA      NA
6  5   <NA>    <NA>       105      15

Joins de filtrado

Los joins de filtrado no añaden ninguna columna. Solo usan la segunda tabla para decidir qué filas de la primera se quedan, como un filtro basado en otro data frame. Además, nunca duplican filas.

semi_join

semi_join conserva las filas de x que tienen pareja en y. El siguiente código devuelve los clientes que han hecho al menos un pedido. Observa que Ana aparece una sola vez, aunque tenga dos pedidos.

semi_join(clientes, pedidos, by = "id")
  id nombre  ciudad
1  1    Ana  Madrid
2  2    Ben Londres
3  3  Carla   París

anti_join

anti_join conserva las filas de x que no tienen pareja en y. Es la forma más sencilla de responder a preguntas como “¿qué clientes no tienen pedidos?”.

# Clientes sin pedidos
anti_join(clientes, pedidos, by = "id")
  id nombre ciudad
1  4  David   Roma

Si intercambias las tablas, anti_join te sirve para revisar la calidad de un join antes de hacerlo. El siguiente código encuentra los pedidos cuyo cliente no existe, que son justo las filas que tendrían NA en un right_join.

# Pedidos de un cliente desconocido
anti_join(pedidos, clientes, by = "id")
  pedido_id id importe
1       105  5      15

¿Qué join tienes que usar?

La siguiente tabla resume las filas que devuelve cada join con los datos de ejemplo (4 clientes y 5 pedidos).

Función Filas que conserva Añade columnas de y Filas en el ejemplo
inner_join(x, y) Filas con pareja en ambas tablas Sí 4
left_join(x, y) Todas las filas de x Sí 5
right_join(x, y) Todas las filas de y Sí 5
full_join(x, y) Todas las filas de ambas tablas Sí 6
semi_join(x, y) Filas de x con pareja en y No 3
anti_join(x, y) Filas de x sin pareja en y No 1

Como regla general, usa left_join cuando quieras añadir información a una tabla principal sin perder filas, inner_join cuando solo te interesen los casos completos y anti_join para encontrar lo que no encaja.

Unir por varias columnas

A veces una sola columna no identifica una fila. En el siguiente ejemplo, cada venta se identifica por la tienda y el año, así que tienes que pasar ambas columnas a by.

ventas <- data.frame(tienda = c("A", "A", "B", "B"),
                     anio = c(2023, 2024, 2023, 2024),
                     ventas = c(100, 120, 90, 95))

objetivos <- data.frame(tienda = c("A", "A", "B"),
                        anio = c(2023, 2024, 2024),
                        objetivo = c(110, 115, 100))

left_join(ventas, objetivos, by = c("tienda", "anio"))
  tienda anio ventas objetivo
1      A 2023    100      110
2      A 2024    120      115
3      B 2023     90       NA
4      B 2024     95      100

Si te olvidas de una de las columnas clave y unes solo por tienda, cada venta se empareja con todos los objetivos de esa tienda. El resultado tiene 6 filas en lugar de 4, dos columnas de año (anio.x y anio.y) y un aviso:

left_join(ventas, objetivos, by = "tienda")
  tienda anio.x ventas anio.y objetivo
1      A   2023    100   2023      110
2      A   2023    100   2024      115
3      A   2024    120   2023      110
4      A   2024    120   2024      115
5      B   2023     90   2024      100
6      B   2024     95   2024      100
Warning message:
In left_join(ventas, objetivos, by = "tienda") :
  Detected an unexpected many-to-many relationship between `x` and `y`.
ℹ Row 1 of `x` matches multiple rows in `y`.
ℹ Row 1 of `y` matches multiple rows in `x`.
ℹ If a many-to-many relationship is expected, set `relationship =
  "many-to-many"` to silence this warning.

Unir por columnas con nombres distintos

Es muy habitual que la clave tenga un nombre diferente en cada tabla. Supón que la columna id de la tabla de pedidos se llama cliente_id. Podrías renombrar la columna antes del join, pero no hace falta.

pedidos2 <- rename(pedidos, cliente_id = id)
pedidos2
  pedido_id cliente_id importe
1       101          1      50
2       102          1      20
3       103          2      35
4       104          3      80
5       105          5      15

Puedes pasar a by un vector con nombre, con la columna de x a la izquierda y la columna de y a la derecha. Desde dplyr 1.1.0 también puedes usar la función auxiliar join_by, que es más legible y utiliza == como si fuera una condición. Las dos líneas siguientes devuelven el mismo resultado.

# Vector con nombre
left_join(clientes, pedidos2, by = c("id" = "cliente_id"))

# join_by (dplyr >= 1.1.0)
left_join(clientes, pedidos2, by = join_by(id == cliente_id))
  id nombre  ciudad pedido_id importe
1  1    Ana  Madrid       101      50
2  1    Ana  Madrid       102      20
3  2    Ben Londres       103      35
4  3  Carla   París       104      80
5  4  David    Roma        NA      NA

El resultado solo conserva la columna clave de x (id). Si necesitas varias claves con nombres distintos, sepáralas con comas: join_by(id == cliente_id, anio == anio_pedido).

Columnas con el mismo nombre: el argumento suffix

Cuando las dos tablas tienen una columna con el mismo nombre que no forma parte de la clave, dplyr conserva ambas y les añade los sufijos .x e .y. En el siguiente ejemplo las dos tablas tienen una columna ciudad.

tiendas <- data.frame(id = c(1, 2, 3),
                      ciudad = c("Madrid", "Berlín", "París"))

left_join(clientes, tiendas, by = "id")
  id nombre ciudad.x ciudad.y
1  1    Ana   Madrid   Madrid
2  2    Ben  Londres   Berlín
3  3  Carla    París    París
4  4  David     Roma     <NA>

Utiliza el argumento suffix para darles nombres con sentido:

left_join(clientes, tiendas,
          by = "id",
          suffix = c("_cliente", "_tienda"))
  id nombre ciudad_cliente ciudad_tienda
1  1    Ana         Madrid        Madrid
2  2    Ben        Londres        Berlín
3  3  Carla          París         París
4  4  David           Roma          <NA>

Si las dos columnas contienen realmente la misma información, normalmente es mejor añadirla a la clave (by = c("id", "ciudad")) o eliminarla de una de las tablas con select antes de unirlas.

¿Por qué un left join devuelve más filas?

Una sorpresa frecuente es que left_join devuelva más filas que la tabla original. La tabla de clientes tiene 4 filas, pero el resultado del left join tiene 5:

nrow(clientes)
nrow(left_join(clientes, pedidos, by = "id"))
[1] 4
[1] 5

left_join garantiza que no se pierde ninguna fila de x, pero no que el número de filas se mantenga. Si una clave de x coincide con varias filas de y, la fila de x se repite una vez por cada coincidencia. Ana tiene dos pedidos, así que aparece dos veces. Esto es correcto en una relación clientes-pedidos (uno a muchos), pero es un error si esperabas una fila por cliente.

Para encontrar las claves repetidas en la segunda tabla puedes contarlas:

pedidos %>%
  count(id) %>%
  filter(n > 1)
  id n
1  1 2

Si quieres una fila por cliente, resume primero los pedidos (por ejemplo, el importe total por cliente con group_by y summarise) y después une la tabla resumida.

El aviso “many-to-many relationship”

Desde dplyr 1.1.0, los joins te avisan cuando una clave está duplicada en las dos tablas, porque cada fila duplicada de x se combina con cada fila duplicada de y y el número de filas puede crecer muy rápido. Considera una tabla de stock y una tabla de precios en la que el precio del boli se introdujo dos veces por error:

precios <- data.frame(producto = c("boli", "boli", "libro"),
                      precio = c(1, 1.2, 10))

stock <- data.frame(producto = c("boli", "boli", "libro"),
                    tienda = c("T1", "T2", "T1"))

left_join(stock, precios, by = "producto")
  producto tienda precio
1     boli     T1    1.0
2     boli     T1    1.2
3     boli     T2    1.0
4     boli     T2    1.2
5    libro     T1   10.0
Warning message:
In left_join(stock, precios, by = "producto") :
  Detected an unexpected many-to-many relationship between `x` and `y`.
ℹ Row 1 of `x` matches multiple rows in `y`.
ℹ Row 1 of `y` matches multiple rows in `x`.
ℹ If a many-to-many relationship is expected, set `relationship =
  "many-to-many"` to silence this warning.

El aviso indica que la fila 1 de x coincide con varias filas de y y viceversa. Cada boli de la tabla de stock se ha emparejado con los dos precios, así que el resultado tiene 5 filas en lugar de 3. Hay dos soluciones posibles, según si los duplicados son un error o no:

  • Los duplicados son un error (lo habitual). Elimínalos de la tabla de referencia, por ejemplo con distinct (tienes más detalles en cómo eliminar duplicados en R), o añade a by la columna clave que falta.
  • La relación muchos a muchos es esperada. Indica relationship = "many-to-many" para silenciar el aviso.
# Conserva el primer precio de cada producto
precios_limpio <- precios %>%
  distinct(producto, .keep_all = TRUE)

# Join con la tabla limpia
left_join(stock, precios_limpio, by = "producto")
  producto tienda precio
1     boli     T1      1
2     boli     T2      1
3    libro     T1     10

No uses relationship = "many-to-many" solo para que desaparezca el aviso. En la mayoría de los casos el aviso significa que la clave no identifica de forma única las filas de la tabla de referencia y el resultado contiene filas duplicadas que distorsionarán cualquier suma o media posterior.

El argumento relationship también es útil para validar tus supuestos. Si cada producto debería tener un único precio, indica relationship = "many-to-one" y dplyr lanzará un error en lugar de devolver filas duplicadas:

left_join(stock, precios, by = "producto", relationship = "many-to-one")
Error in `left_join()`:
! Each row in `x` must match at most 1 row in `y`.
ℹ Row 1 of `x` matches multiple rows in `y`.

Los valores posibles son "one-to-one", "one-to-many", "many-to-one" y "many-to-many".

Valores NA después de un join

Los joins externos (left_join, right_join y full_join) rellenan con NA las columnas de las filas sin pareja. Por eso David tiene NA en pedido_id e importe después del left join. Estos NA no significan que los datos estén mal, pero tienes que tenerlos en cuenta antes de calcular estadísticos. Si un cliente sin pedidos significa “importe cero”, puedes reemplazar los NA con coalesce dentro de mutate:

clientes %>%
  left_join(pedidos, by = "id") %>%
  mutate(importe = coalesce(importe, 0))
  id nombre  ciudad pedido_id importe
1  1    Ana  Madrid       101      50
2  1    Ana  Madrid       102      20
3  2    Ben Londres       103      35
4  3  Carla   París       104      80
5  4  David    Roma        NA       0

Si obtienes muchos más NA de los esperados, probablemente las claves no coinciden por su formato: un número guardado como texto en una tabla y como numérico en la otra, espacios en blanco de más o mayúsculas distintas. Compruébalo con anti_join, como has visto antes. Aprende más sobre los datos faltantes en el tutorial sobre valores NA en R.

merge() frente a los joins de dplyr

R base permite hacer los mismos joins con la función merge. La siguiente tabla muestra las equivalencias:

R base dplyr
merge(x, y, by = "id") inner_join(x, y, by = "id")
merge(x, y, by = "id", all.x = TRUE) left_join(x, y, by = "id")
merge(x, y, by = "id", all.y = TRUE) right_join(x, y, by = "id")
merge(x, y, by = "id", all = TRUE) full_join(x, y, by = "id")
merge(x, y, by.x = "id", by.y = "cliente_id") inner_join(x, y, by = join_by(id == cliente_id))
x[x$id %in% y$id, ] semi_join(x, y, by = "id")
x[!x$id %in% y$id, ] anti_join(x, y, by = "id")

Las principales diferencias prácticas son que merge ordena el resultado por la clave por defecto, mientras que los joins de dplyr mantienen el orden original de las filas de x, y que merge no te avisa de las relaciones muchos a muchos.