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):
Mutating joins
—inner_join(), left_join(),
right_join(), full_join()—: combinan columnas
de ambas tablas y emparejan las filas según una coincidencia en las
llaves comunes.
Filtering joins
—semi_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.
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
Mutating
joins – Uniones de modificación
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")
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")
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")
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")
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.
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")
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