1 Introdución a la combinación de tablas (joins)

En las bases de datos relacionales y en el análisis estructurado, la información suele encontrarse fragmentada en múltiples tablas. El paquete dplyr proporciona dos familias principales de funciones para combinar datos mediante llaves comunes (keys):

  1. Mutating joinsinner_join(), left_join(), right_join(), full_join()—: combinan columnas de ambas tablas y emparejan las filas según una coincidencia en las llaves comunes.

  2. Filtering joinssemi_join(), anti_join()—: filtran las filas de la primera tabla según la presencia o ausencia de coincidencias en la segunda tabla, sin agregar columnas nuevas.

2 Ejemplo

Para ilustrar de forma clara cada tipo de join, crearemos dos dataset pequeños con algunos estudiantes y los cursos en los cuales están inscritos.

  • Estudiantes: contiene cinco (5) alumnos. La estudiante “Sofia”, tiene un id_curso= 5 – que no existe en la tabla cursos –.

  • Cursos: contiene 4 materias. El curso de “Historia”, con id_curso = 4, no tiene ningún estudiane matriculado.

library(tidyverse)
library(tibble)
estudiantes <- tibble::tibble(
  
  id_alumno = c(101,102,103,104,105),
  nombre    = c("Ana", "Carlos", "Elena", "David", "Sofía"),
  id_curso  = c(1,2,2,3,5)
  
)

estudiantes
cursos <- tibble::tibble(
  
  id_curso     = c(1,2,3,4),
  nombre_curso = c("Matemáticas",  "Programación", "Economía", "Historia"),
  profesor     = c("Dr. García", "Ing. López", "Dra. Martínez", "Lic. Fernández")
  
)

cursos

2.1 Mutating joins – Uniones de modificación

2.1.1 inner_join() – coincidencias exactas

Mantiene unicamente las filas que tienen coincidencia en ambas tablas.

Aplicándolos en casos como el planteado anteriormente, con dos dataset, la función retorna solo las filas donde estudiantes$id_cursocoinciden con cursos$id_estudiantes.

Quedan excluídos: Sofía (curso 5) e Historia (curso 4).

estudiantes %>%
  dplyr::inner_join(cursos, by = "id_curso")

2.1.2 left_join() – prioridad tabla izquierda

Mantiene todas las filas de la tabla izquierda (por orden de declaración). Si no hay coincidencia en la derecha, los campos se rellenan con NA.

Para los dos dataset planteados, está función conserva a todos los estudiantes. Sofía permanece en los resultados, pero muestra NA en el nombre del curso y del profesor.

estudiantes %>%
  dplyr::left_join(cursos, by = "id_curso")

2.1.3 right_join() – prioridad tabla derecha

Mantiene todas las filas de la tabla derecha. Si no hay coincidencias en la tabla izquierda, los campos del estudiante se rellenan con NA.

Con nuestros dataset, la función conserva todos los cursos. El curso “Historia” aparece, pero con los datos del estudiante inscrito como NA.

estudiantes %>%
  dplyr::right_join(cursos, by = "id_curso")

2.1.4 full_join() – unión completa

Mantiene todas las filas de ambas tablas, rellenando con NA en cualquier lugar donde no exista una coincidencia.

Con los dos dataset, no se pierde ningún registro. Incluye tanto Sofía como al curso de historia.

estudiantes %>%
  dplyr::full_join(cursos, by = "id_curso")

2.2 Filtering joins – Uniones de filtrado

A diferencia de los mutating joins, las uniones de filtrado nunca agregan nuevas columnas de la tabla derecha, solo filtran la tabla izquierda.

2.2.1 semi_join() – filtra coincidencias

Conserva solo las filas de estudiantes que tengan coincidencia en cursos.

Devolverá solo los estudiantes asignados a un curso válido, pero sin incluir la columna cursos.

estudiantes %>%
  dplyr::semi_join(cursos, by = "id_curso")

2.2.2 anti_join() – filtra diferencias

Conserva las filas de estudiantes que no tienen coincidencia en cursos. Es ideal para identificar registros huérfanos.

Identificará a los estudiantes que no están inscritos a un curso válido. En este caso, únicamente SOfía no pertenece a un curso válido (5).

estudiantes %>%
  dplyr::anti_join(cursos, by = "id_curso")
LS0tDQp0aXRsZTogIkPDoWxjdWxvIGRlIGluZGljYWRvcmVzIHNpbXBsZXMgeSB0cmFuc2Zvcm1hY2nDs24gZGUgbGEgaW5mb3JtYWNpw7NuIg0Kc3VidGl0bGU6ICJDb21iaW5hY2nDs24gZGUgdGFibGFzIG1lZGlhbnRlICpKT0lOUyoiDQphdXRob3I6ICJIYXJvbGQgUy4gTC4gTW9udGFubyINCmRhdGU6ICIyMDI2LTA3LTI0Ig0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDoNCiAgICAgIGNvbGxhcHNlZDogZmFsc2UNCiAgICAgIHNtb290aF9zY3JvbGw6IHRydWUNCiAgICB0b2NfZGVwdGg6IDMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUNCiAgICB0aGVtZTogZmxhdGx5DQogICAgaGlnaGxpZ2h0OiB0YW5nbw0KICAgIGRmX3ByaW50OiBwYWdlZA0KICAgIGNvZGVfZm9sZGluZzogc2hvdw0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldCgNCiAgZWNobyAgICA9IFRSVUUsDQogIG1lc3NhZ2UgPSBGQUxTRSwNCiAgd2FybmluZyA9IEZBTFNFLA0KICBjb21tZW50ID0gIiM+Ig0KKQ0KDQojIFRBQkxBUyBIVE1MIENPTlNJU1RFTlRFUyBTSU4gREVQRU5ERU5DSUFTIEVYVEVSTkFTDQp0YWJsYSA8LSBmdW5jdGlvbih4LCAuLi4pIHsNCiAga25pdHI6OmthYmxlKHgsIGZvcm1hdCA9ICJodG1sIiwgdGFibGUuYXR0ciA9ICdjbGFzcz0idGFibGEiJywgLi4uKQ0KfQ0KYGBgDQoNCmBgYHtjc3MsIGVjaG89RkFMU0V9DQpib2R5IHsNCiAgZm9udC1zaXplOiAxNXB4Ow0KICBsaW5lLWhlaWdodDogMS42NTsNCn0NCg0KaDEudGl0bGUgeyBmb250LXdlaWdodDogNjAwOyB9DQoNCmgxIHsNCiAgYm9yZGVyLWJvdHRvbTogMnB4IHNvbGlkICMyYzNlNTA7DQogIHBhZGRpbmctYm90dG9tOiAuM2VtOw0KICBtYXJnaW4tdG9wOiAxLjhlbTsNCn0NCg0KLmVudW5jaWFkbyB7DQogIGJhY2tncm91bmQtY29sb3I6ICNmNGY3Zjk7DQogIGJvcmRlci1sZWZ0OiA0cHggc29saWQgIzJjM2U1MDsNCiAgcGFkZGluZzogLjllbSAxLjFlbTsNCiAgbWFyZ2luOiAxLjJlbSAwOw0KICBib3JkZXItcmFkaXVzOiAwIDRweCA0cHggMDsNCn0NCg0KLmVudW5jaWFkbyBwOmxhc3QtY2hpbGQgeyBtYXJnaW4tYm90dG9tOiAwOyB9DQoNCi5ub3RhIHsNCiAgYmFja2dyb3VuZC1jb2xvcjogI2ZkZjZlMzsNCiAgYm9yZGVyLWxlZnQ6IDRweCBzb2xpZCAjYjU4OTAwOw0KICBwYWRkaW5nOiAuOGVtIDEuMWVtOw0KICBtYXJnaW46IDEuMmVtIDA7DQogIGZvbnQtc2l6ZTogLjkzZW07DQogIGJvcmRlci1yYWRpdXM6IDAgNHB4IDRweCAwOw0KfQ0KDQp0YWJsZS50YWJsYSB7DQogIGJvcmRlci1jb2xsYXBzZTogY29sbGFwc2U7DQogIG1hcmdpbjogMWVtIDA7DQogIGZvbnQtc2l6ZTogLjkzZW07DQp9DQoNCnRhYmxlLnRhYmxhIHRoIHsNCiAgYmFja2dyb3VuZC1jb2xvcjogIzJjM2U1MDsNCiAgY29sb3I6ICNmZmY7DQogIHBhZGRpbmc6IC41ZW0gLjllbTsNCiAgdGV4dC1hbGlnbjogcmlnaHQ7DQp9DQoNCnRhYmxlLnRhYmxhIHRoOmZpcnN0LWNoaWxkIHsgdGV4dC1hbGlnbjogbGVmdDsgfQ0KDQp0YWJsZS50YWJsYSB0ZCB7DQogIHBhZGRpbmc6IC40NWVtIC45ZW07DQogIGJvcmRlci1ib3R0b206IDFweCBzb2xpZCAjZTFlNWVhOw0KICB0ZXh0LWFsaWduOiByaWdodDsNCn0NCg0KdGFibGUudGFibGEgdGQ6Zmlyc3QtY2hpbGQgew0KICB0ZXh0LWFsaWduOiBsZWZ0Ow0KICBmb250LXdlaWdodDogNTAwOw0KfQ0KDQp0YWJsZS50YWJsYSB0cjpob3ZlciB0ZCB7IGJhY2tncm91bmQtY29sb3I6ICNmNGY3Zjk7IH0NCg0KcHJlIHsgYm9yZGVyLXJhZGl1czogNHB4OyB9DQpgYGANCg0KIyBJbnRyb2R1Y2nDs24gYSBsYSBjb21iaW5hY2nDs24gZGUgdGFibGFzICgqam9pbnMqKQ0KDQpFbiBsYXMgYmFzZXMgZGUgZGF0b3MgcmVsYWNpb25hbGVzIHkgZW4gZWwgYW7DoWxpc2lzIGVzdHJ1Y3R1cmFkbywgbGENCmluZm9ybWFjacOzbiBzdWVsZSBlbmNvbnRyYXJzZSBmcmFnbWVudGFkYSBlbiBtw7psdGlwbGVzIHRhYmxhcy4gRWwgcGFxdWV0ZQ0KYGRwbHlyYCBwcm9wb3JjaW9uYSBkb3MgZmFtaWxpYXMgcHJpbmNpcGFsZXMgZGUgZnVuY2lvbmVzIHBhcmEgY29tYmluYXIgZGF0b3MNCm1lZGlhbnRlIGxsYXZlcyBjb211bmVzICgqa2V5cyopOg0KDQoxLiAqKipNdXRhdGluZyBqb2lucyoqKiDigJRgaW5uZXJfam9pbigpYCwgYGxlZnRfam9pbigpYCwgYHJpZ2h0X2pvaW4oKWAsDQpgZnVsbF9qb2luKClg4oCUOiBjb21iaW5hbiBjb2x1bW5hcyBkZSBhbWJhcyB0YWJsYXMgeSBlbXBhcmVqYW4gbGFzIGZpbGFzIHNlZ8O6bg0KdW5hIGNvaW5jaWRlbmNpYSBlbiBsYXMgbGxhdmVzIGNvbXVuZXMuDQoNCjIuICoqKkZpbHRlcmluZyBqb2lucyoqKiDigJRgc2VtaV9qb2luKClgLCBgYW50aV9qb2luKClg4oCUOiBmaWx0cmFuIGxhcyBmaWxhcyBkZQ0KbGEgcHJpbWVyYSB0YWJsYSBzZWfDum4gbGEgcHJlc2VuY2lhIG8gYXVzZW5jaWEgZGUgY29pbmNpZGVuY2lhcyBlbiBsYSBzZWd1bmRhDQp0YWJsYSwgc2luIGFncmVnYXIgY29sdW1uYXMgbnVldmFzLg0KDQojIEVqZW1wbG8NCg0KOjo6ey5lbnVuY2lhZG99DQpQYXJhIGlsdXN0cmFyIGRlIGZvcm1hIGNsYXJhIGNhZGEgdGlwbyBkZSAqam9pbiosIGNyZWFyZW1vcyBkb3MgKmRhdGFzZXQqDQpwZXF1ZcOxb3MgY29uIGFsZ3Vub3MgZXN0dWRpYW50ZXMgeSBsb3MgY3Vyc29zIGVuIGxvcyBjdWFsZXMgZXN0w6FuIGluc2NyaXRvcy4NCjo6Og0KDQotICoqRXN0dWRpYW50ZXMqKjogY29udGllbmUgY2luY28gKDUpIGFsdW1ub3MuIExhIGVzdHVkaWFudGUgIlNvZmlhIiwgdGllbmUgdW4NCmBpZF9jdXJzb2A9IDUgLS0gcXVlIG5vIGV4aXN0ZSBlbiBsYSB0YWJsYSBjdXJzb3MgLS0uDQoNCi0gKipDdXJzb3MqKjogY29udGllbmUgNCBtYXRlcmlhcy4gRWwgY3Vyc28gZGUgIkhpc3RvcmlhIiwgY29uIGBpZF9jdXJzb2AgPQ0KNCwgbm8gdGllbmUgbmluZ8O6biBlc3R1ZGlhbmUgbWF0cmljdWxhZG8uDQoNCmBgYHtyIGxpYnJhcmllcywgZWNobz1UUlVFfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHRpYmJsZSkNCmBgYA0KDQpgYGB7ciBsZWZ0LXRhYmxlLXh9DQoNCmVzdHVkaWFudGVzIDwtIHRpYmJsZTo6dGliYmxlKA0KICANCiAgaWRfYWx1bW5vID0gYygxMDEsMTAyLDEwMywxMDQsMTA1KSwNCiAgbm9tYnJlICAgID0gYygiQW5hIiwgIkNhcmxvcyIsICJFbGVuYSIsICJEYXZpZCIsICJTb2bDrWEiKSwNCiAgaWRfY3Vyc28gID0gYygxLDIsMiwzLDUpDQogIA0KKQ0KDQplc3R1ZGlhbnRlcw0KYGBgDQoNCmBgYHtyIHJpZ2h0LXRhYmxlLXl9DQoNCmN1cnNvcyA8LSB0aWJibGU6OnRpYmJsZSgNCiAgDQogIGlkX2N1cnNvICAgICA9IGMoMSwyLDMsNCksDQogIG5vbWJyZV9jdXJzbyA9IGMoIk1hdGVtw6F0aWNhcyIsICAiUHJvZ3JhbWFjacOzbiIsICJFY29ub23DrWEiLCAiSGlzdG9yaWEiKSwNCiAgcHJvZmVzb3IgICAgID0gYygiRHIuIEdhcmPDrWEiLCAiSW5nLiBMw7NwZXoiLCAiRHJhLiBNYXJ0w61uZXoiLCAiTGljLiBGZXJuw6FuZGV6IikNCiAgDQopDQoNCmN1cnNvcw0KYGBgDQoNCiMjICpNdXRhdGluZyBqb2lucyogLS0gVW5pb25lcyBkZSBtb2RpZmljYWNpw7NuIA0KDQojIyMgYGlubmVyX2pvaW4oKWAgLS0gY29pbmNpZGVuY2lhcyBleGFjdGFzDQoNCjo6Onsubm90YX0NCk1hbnRpZW5lIHVuaWNhbWVudGUgbGFzIGZpbGFzIHF1ZSB0aWVuZW4gY29pbmNpZGVuY2lhIGVuIGFtYmFzIHRhYmxhcy4NCjo6Og0KDQpBcGxpY8OhbmRvbG9zIGVuIGNhc29zIGNvbW8gZWwgcGxhbnRlYWRvIGFudGVyaW9ybWVudGUsIGNvbiBkb3MgKmRhdGFzZXQqLCBsYQ0KZnVuY2nDs24gcmV0b3JuYSBzb2xvIGxhcyBmaWxhcyBkb25kZSBgZXN0dWRpYW50ZXMkaWRfY3Vyc29gY29pbmNpZGVuIGNvbg0KYGN1cnNvcyRpZF9lc3R1ZGlhbnRlc2AuIA0KDQpRdWVkYW4gZXhjbHXDrWRvczogKlNvZsOtYSogKGN1cnNvIDUpIGUgKkhpc3RvcmlhKiAoY3Vyc28gNCkuDQoNCmBgYHtyIGlubmVyLWpvaW59DQoNCmVzdHVkaWFudGVzICU+JQ0KICBkcGx5cjo6aW5uZXJfam9pbihjdXJzb3MsIGJ5ID0gImlkX2N1cnNvIikNCg0KYGBgDQoNCiMjIyBgbGVmdF9qb2luKClgIC0tIHByaW9yaWRhZCB0YWJsYSBpenF1aWVyZGENCg0KOjo6ey5ub3RhfQ0KTWFudGllbmUgdG9kYXMgbGFzIGZpbGFzIGRlIGxhIHRhYmxhIGl6cXVpZXJkYSAocG9yIG9yZGVuIGRlIGRlY2xhcmFjacOzbikuIA0KU2kgbm8gaGF5IGNvaW5jaWRlbmNpYSBlbiBsYSBkZXJlY2hhLCBsb3MgY2FtcG9zIHNlIHJlbGxlbmFuIGNvbiBgTkFgLg0KOjo6DQoNClBhcmEgbG9zIGRvcyAqZGF0YXNldCogcGxhbnRlYWRvcywgZXN0w6EgZnVuY2nDs24gY29uc2VydmEgYSB0b2RvcyBsb3MgZXN0dWRpYW50ZXMuDQpTb2bDrWEgcGVybWFuZWNlIGVuIGxvcyByZXN1bHRhZG9zLCBwZXJvIG11ZXN0cmEgYE5BYCBlbiBlbCBub21icmUgZGVsIGN1cnNvIHkgDQpkZWwgcHJvZmVzb3IuDQoNCmBgYHtyIGxlZnQtam9pbn0NCg0KZXN0dWRpYW50ZXMgJT4lDQogIGRwbHlyOjpsZWZ0X2pvaW4oY3Vyc29zLCBieSA9ICJpZF9jdXJzbyIpDQoNCmBgYA0KDQojIyMgYHJpZ2h0X2pvaW4oKWAgLS0gcHJpb3JpZGFkIHRhYmxhIGRlcmVjaGENCg0KOjo6ey5ub3RhfQ0KTWFudGllbmUgdG9kYXMgbGFzIGZpbGFzIGRlIGxhIHRhYmxhIGRlcmVjaGEuIFNpIG5vIGhheSBjb2luY2lkZW5jaWFzIGVuIGxhIHRhYmxhDQppenF1aWVyZGEsIGxvcyBjYW1wb3MgZGVsIGVzdHVkaWFudGUgc2UgcmVsbGVuYW4gY29uIGBOQWAuDQo6OjoNCg0KQ29uIG51ZXN0cm9zICpkYXRhc2V0KiwgbGEgZnVuY2nDs24gY29uc2VydmEgdG9kb3MgbG9zIGN1cnNvcy4gRWwgY3Vyc28gIkhpc3RvcmlhIiBhcGFyZWNlLCBwZXJvIGNvbiBsb3MgZGF0b3MgZGVsIGVzdHVkaWFudGUgaW5zY3JpdG8gY29tbyBgTkFgLg0KDQpgYGB7ciByaWdodC1qb2lufQ0KDQplc3R1ZGlhbnRlcyAlPiUNCiAgZHBseXI6OnJpZ2h0X2pvaW4oY3Vyc29zLCBieSA9ICJpZF9jdXJzbyIpDQogIA0KDQpgYGANCg0KIyMjIGBmdWxsX2pvaW4oKWAgLS0gdW5pw7NuIGNvbXBsZXRhDQoNCjo6Onsubm90YX0NCk1hbnRpZW5lIHRvZGFzIGxhcyBmaWxhcyBkZSBhbWJhcyB0YWJsYXMsIHJlbGxlbmFuZG8gY29uIGBOQWAgZW4gY3VhbHF1aWVyIGx1Z2FyDQpkb25kZSBubyBleGlzdGEgdW5hIGNvaW5jaWRlbmNpYS4NCjo6Og0KDQpDb24gbG9zIGRvcyAqZGF0YXNldCosIG5vIHNlIHBpZXJkZSBuaW5nw7puIHJlZ2lzdHJvLiBJbmNsdXllIHRhbnRvIFNvZsOtYSBjb21vIGFsDQpjdXJzbyBkZSBoaXN0b3JpYS4NCg0KYGBge3IgZnVsbC1qb2lufQ0KDQplc3R1ZGlhbnRlcyAlPiUNCiAgZHBseXI6OmZ1bGxfam9pbihjdXJzb3MsIGJ5ID0gImlkX2N1cnNvIikNCg0KYGBgDQoNCiMjICpGaWx0ZXJpbmcgam9pbnMqIC0tIFVuaW9uZXMgZGUgZmlsdHJhZG8NCg0KOjo6ey5ub3RhfQ0KQSBkaWZlcmVuY2lhIGRlIGxvcyAqbXV0YXRpbmcgam9pbnMqLCBsYXMgdW5pb25lcyBkZSBmaWx0cmFkbyBudW5jYSBhZ3JlZ2FuIG51ZXZhcyBjb2x1bW5hcyBkZSBsYSB0YWJsYSBkZXJlY2hhLCBzb2xvIGZpbHRyYW4gbGEgdGFibGEgaXpxdWllcmRhLg0KOjo6DQoNCiMjIyBgc2VtaV9qb2luKClgIC0tIGZpbHRyYSBjb2luY2lkZW5jaWFzDQoNCjo6Onsubm90YX0NCkNvbnNlcnZhIHNvbG8gbGFzIGZpbGFzIGRlIGBlc3R1ZGlhbnRlc2AgcXVlIHRlbmdhbiBjb2luY2lkZW5jaWEgZW4gYGN1cnNvc2AuDQo6OjoNCg0KRGV2b2x2ZXLDoSBzb2xvIGxvcyBlc3R1ZGlhbnRlcyBhc2lnbmFkb3MgYSB1biBjdXJzbyB2w6FsaWRvLCBwZXJvIHNpbiBpbmNsdWlyIGxhIA0KY29sdW1uYSBgY3Vyc29zYC4NCg0KYGBge3Igc2VtaS1qb2lufQ0KDQplc3R1ZGlhbnRlcyAlPiUNCiAgZHBseXI6OnNlbWlfam9pbihjdXJzb3MsIGJ5ID0gImlkX2N1cnNvIikNCmBgYA0KDQojIyMgYGFudGlfam9pbigpYCAtLSBmaWx0cmEgZGlmZXJlbmNpYXMNCg0KOjo6ey5ub3RhfQ0KQ29uc2VydmEgbGFzIGZpbGFzIGRlIGBlc3R1ZGlhbnRlc2AgcXVlIG5vIHRpZW5lbiBjb2luY2lkZW5jaWEgZW4gYGN1cnNvc2AuIEVzIA0KaWRlYWwgcGFyYSBpZGVudGlmaWNhciByZWdpc3Ryb3MgaHXDqXJmYW5vcy4NCjo6Og0KDQpJZGVudGlmaWNhcsOhIGEgbG9zIGVzdHVkaWFudGVzIHF1ZSBubyBlc3TDoW4gaW5zY3JpdG9zIGEgdW4gY3Vyc28gdsOhbGlkby4gRW4gZXN0ZQ0KY2Fzbywgw7puaWNhbWVudGUgU09mw61hIG5vIHBlcnRlbmVjZSBhIHVuIGN1cnNvIHbDoWxpZG8gKDUpLg0KDQpgYGB7ciBhbnRpLWpvaW59DQoNCmVzdHVkaWFudGVzICU+JQ0KICBkcGx5cjo6YW50aV9qb2luKGN1cnNvcywgYnkgPSAiaWRfY3Vyc28iKQ0KDQpgYGANCg0KDQoNCg0K