R

R

Tutorial R: joins en r para combinación y relaciones de tablas

Aprende a usar inner, outer, semi y anti joins en R con dplyr para combinar y filtrar tablas eficientemente en análisis de datos.

Aprende R y certifícate

Inner y outer joins en dplyr

Cuando trabajamos con datos en R, es común necesitar combinar información de diferentes tablas o dataframes. El paquete dplyr nos ofrece funciones específicas para realizar estas operaciones de forma sencilla y eficiente, permitiéndonos relacionar datos a través de columnas comunes.

Los joins o uniones son operaciones que nos permiten conectar tablas basándonos en valores coincidentes en columnas específicas. Estas operaciones son fundamentales cuando trabajamos con datos que están distribuidos en múltiples tablas, como suele ocurrir en bases de datos relacionales.

Inner join: intersección de datos

El inner join es la operación más básica y devuelve únicamente las filas que tienen coincidencias en ambas tablas. Usando la función inner_join() de dplyr, podemos combinar dos dataframes incluyendo solo los registros que existen en ambas tablas.

Veamos un ejemplo sencillo con dos dataframes:

# Cargamos la librería dplyr
library(dplyr)

# Creamos dos dataframes de ejemplo
clientes <- tibble(
  id_cliente = c(1, 2, 3, 4, 5),
  nombre = c("Ana", "Benito", "Carmen", "David", "Elena"),
  ciudad = c("Madrid", "Barcelona", "Sevilla", "Valencia", "Bilbao")
)

pedidos <- tibble(
  id_pedido = c(101, 102, 103, 104, 105),
  id_cliente = c(1, 3, 3, 6, 7),
  producto = c("Laptop", "Móvil", "Tablet", "Monitor", "Teclado"),
  precio = c(850, 300, 200, 150, 25)
)

# Realizamos un inner join
clientes_con_pedidos <- inner_join(clientes, pedidos, by = "id_cliente")

# Visualizamos el resultado
clientes_con_pedidos

El resultado mostrará solo los clientes que han realizado pedidos (ids 1 y 3), excluyendo a los clientes sin pedidos y los pedidos de clientes que no están en nuestra tabla de clientes:

# A tibble: 3 × 6
  id_cliente nombre ciudad  id_pedido producto precio
       <dbl> <chr>  <chr>       <dbl> <chr>     <dbl>
1          1 Ana    Madrid        101 Laptop      850
2          3 Carmen Sevilla       102 Móvil       300
3          3 Carmen Sevilla       103 Tablet      200

Outer joins: incluyendo datos no coincidentes

A diferencia del inner join, los outer joins nos permiten conservar filas que no tienen coincidencias en la otra tabla. Dplyr ofrece tres tipos de outer joins:

Left join: mantiene todas las filas de la tabla izquierda

El left_join() conserva todas las filas de la primera tabla (izquierda) y añade la información de la segunda tabla cuando hay coincidencias:

# Realizamos un left join
todos_clientes_con_pedidos <- left_join(clientes, pedidos, by = "id_cliente")

# Visualizamos el resultado
todos_clientes_con_pedidos

El resultado incluirá a todos los clientes, incluso aquellos sin pedidos (con valores NA en las columnas de pedidos):

# A tibble: 7 × 6
  id_cliente nombre ciudad    id_pedido producto precio
       <dbl> <chr>  <chr>         <dbl> <chr>     <dbl>
1          1 Ana    Madrid          101 Laptop      850
2          2 Benito Barcelona        NA NA           NA
3          3 Carmen Sevilla         102 Móvil       300
4          3 Carmen Sevilla         103 Tablet      200
5          4 David  Valencia         NA NA           NA
6          5 Elena  Bilbao           NA NA           NA

Right join: mantiene todas las filas de la tabla derecha

El right_join() funciona de manera similar al left join, pero conserva todas las filas de la segunda tabla (derecha):

# Realizamos un right join
pedidos_con_clientes <- right_join(clientes, pedidos, by = "id_cliente")

# Visualizamos el resultado
pedidos_con_clientes

Este resultado incluirá todos los pedidos, incluso aquellos de clientes que no están en nuestra tabla de clientes:

# A tibble: 5 × 6
  id_cliente nombre ciudad  id_pedido producto precio
       <dbl> <chr>  <chr>       <dbl> <chr>     <dbl>
1          1 Ana    Madrid        101 Laptop      850
2          3 Carmen Sevilla       102 Móvil       300
3          3 Carmen Sevilla       103 Tablet      200
4          6 NA     NA            104 Monitor     150
5          7 NA     NA            105 Teclado      25

Full join: mantiene todas las filas de ambas tablas

El full_join() es la unión más inclusiva, ya que conserva todas las filas de ambas tablas, independientemente de si tienen coincidencias o no:

# Realizamos un full join
todos_datos_combinados <- full_join(clientes, pedidos, by = "id_cliente")

# Visualizamos el resultado
todos_datos_combinados

El resultado incluirá todos los clientes y todos los pedidos:

# A tibble: 9 × 6
  id_cliente nombre ciudad    id_pedido producto precio
       <dbl> <chr>  <chr>         <dbl> <chr>     <dbl>
1          1 Ana    Madrid          101 Laptop      850
2          2 Benito Barcelona        NA NA           NA
3          3 Carmen Sevilla         102 Móvil       300
4          3 Carmen Sevilla         103 Tablet      200
5          4 David  Valencia         NA NA           NA
6          5 Elena  Bilbao           NA NA           NA
7          6 NA     NA              104 Monitor     150
8          7 NA     NA              105 Teclado      25

Especificando columnas de unión

Cuando las columnas que queremos usar para unir las tablas tienen nombres diferentes, podemos especificarlas usando un vector con nombres:

# Creamos un dataframe con nombre de columna diferente
ventas <- tibble(
  id_venta = c(201, 202, 203),
  cliente_id = c(1, 3, 5),  # Nombre diferente para la misma información
  fecha = c("2023-01-15", "2023-01-20", "2023-01-25")
)

# Unimos especificando la relación entre columnas
clientes_ventas <- inner_join(clientes, ventas, by = c("id_cliente" = "cliente_id"))

# Visualizamos el resultado
clientes_ventas

El resultado mostrará los clientes que tienen ventas asociadas:

# A tibble: 3 × 5
  id_cliente nombre ciudad  id_venta fecha      
       <dbl> <chr>  <chr>      <dbl> <chr>      
1          1 Ana    Madrid       201 2023-01-15
2          3 Carmen Sevilla      202 2023-01-20
3          5 Elena  Bilbao       203 2023-01-25

Uniendo por múltiples columnas

También podemos unir tablas basándonos en coincidencias en múltiples columnas:

# Creamos dataframes con múltiples columnas para unir
empleados <- tibble(
  departamento = c("Ventas", "Ventas", "IT", "Marketing", "IT"),
  id_empleado = c(101, 102, 103, 104, 105),
  nombre = c("Laura", "Carlos", "Marta", "Javier", "Sofía")
)

proyectos <- tibble(
  departamento = c("Ventas", "IT", "IT", "RRHH", "Marketing"),
  id_empleado = c(101, 103, 106, 107, 104),
  proyecto = c("CRM", "Web", "App", "Selección", "Campaña")
)

# Unimos por departamento e id_empleado
asignaciones <- inner_join(empleados, proyectos, 
                          by = c("departamento", "id_empleado"))

# Visualizamos el resultado
asignaciones

El resultado mostrará solo los empleados que están asignados a proyectos en su mismo departamento:

# A tibble: 3 × 4
  departamento id_empleado nombre proyecto
  <chr>             <dbl> <chr>  <chr>   
1 Ventas              101 Laura  CRM     
2 IT                  103 Marta  Web     
3 Marketing           104 Javier Campaña 

Casos de uso prácticos

Los joins son especialmente útiles en análisis de datos reales. Por ejemplo:

  • Combinar información demográfica de clientes con su historial de compras
  • Relacionar datos de ventas con información de productos
  • Unir registros de empleados con datos de rendimiento
# Ejemplo: Análisis de ventas por región
regiones <- tibble(
  id_region = c(1, 2, 3),
  nombre_region = c("Norte", "Centro", "Sur"),
  responsable = c("María", "Pedro", "Lucía")
)

tiendas <- tibble(
  id_tienda = c(10, 11, 12, 13, 14),
  nombre_tienda = c("Tienda A", "Tienda B", "Tienda C", "Tienda D", "Tienda E"),
  id_region = c(1, 1, 2, 3, 4)
)

ventas_mensuales <- tibble(
  id_tienda = c(10, 11, 12, 13, 15),
  mes = c("Enero", "Enero", "Enero", "Enero", "Enero"),
  ventas = c(15000, 12000, 18000, 9000, 7500)
)

# Análisis completo con múltiples joins
analisis_ventas <- tiendas %>%
  left_join(regiones, by = "id_region") %>%
  inner_join(ventas_mensuales, by = "id_tienda")

# Visualizamos el resultado
analisis_ventas

Este análisis nos permitirá ver las ventas por tienda junto con la información de la región a la que pertenece:

# A tibble: 4 × 7
  id_tienda nombre_tienda id_region nombre_region responsable mes   ventas
      <dbl> <chr>             <dbl> <chr>         <chr>       <chr>  <dbl>
1        10 Tienda A              1 Norte         María       Enero  15000
2        11 Tienda B              1 Norte         María       Enero  12000
3        12 Tienda C              2 Centro        Pedro       Enero  18000
4        13 Tienda D              3 Sur           Lucía       Enero   9000

Los joins en dplyr son herramientas fundamentales para el análisis de datos relacionales en R, permitiéndonos combinar información de diferentes fuentes de manera eficiente y flexible según nuestras necesidades específicas.

Semi-joins y anti-joins para filtrado

Además de los inner y outer joins que vimos anteriormente, dplyr ofrece dos tipos especiales de joins que son extremadamente útiles para filtrar datos: los semi-joins y los anti-joins. A diferencia de los joins tradicionales, estos no combinan columnas de ambas tablas, sino que se utilizan principalmente para filtrar filas basándose en la existencia (o ausencia) de coincidencias en otra tabla.

Semi-join: filtrar filas que tienen coincidencias

Un semi-join (semi_join()) mantiene las filas de la primera tabla que tienen al menos una coincidencia en la segunda tabla, pero a diferencia del inner join, no añade ninguna columna de la segunda tabla. Es como decir "dame todas las filas de la tabla A que tienen alguna coincidencia en la tabla B".

Veamos un ejemplo práctico:

# Cargamos la librería necesaria
library(dplyr)

# Creamos dos dataframes de ejemplo
productos <- tibble(
  id_producto = c(101, 102, 103, 104, 105),
  nombre = c("Laptop", "Tablet", "Smartphone", "Monitor", "Teclado"),
  categoria = c("Informática", "Móviles", "Móviles", "Periféricos", "Periféricos")
)

ventas_2023 <- tibble(
  id_venta = c(1, 2, 3, 4),
  id_producto = c(101, 103, 103, 106),
  cantidad = c(2, 1, 3, 1)
)

# Aplicamos un semi-join para encontrar productos que se han vendido
productos_vendidos <- semi_join(productos, ventas_2023, by = "id_producto")

# Visualizamos el resultado
productos_vendidos

El resultado mostrará solo los productos que aparecen en la tabla de ventas:

# A tibble: 2 × 3
  id_producto nombre     categoria  
        <dbl> <chr>      <chr>      
1         101 Laptop     Informática
2         103 Smartphone Móviles    

Observa que el resultado mantiene solo las columnas de la tabla productos, pero filtra las filas para mostrar únicamente aquellos productos que tienen al menos una venta registrada.

Anti-join: filtrar filas que no tienen coincidencias

Por otro lado, un anti-join (anti_join()) hace exactamente lo contrario: mantiene las filas de la primera tabla que no tienen coincidencias en la segunda tabla. Es como preguntar "dame todas las filas de la tabla A que no aparecen en la tabla B".

Continuando con nuestro ejemplo:

# Aplicamos un anti-join para encontrar productos sin ventas
productos_sin_ventas <- anti_join(productos, ventas_2023, by = "id_producto")

# Visualizamos el resultado
productos_sin_ventas

El resultado mostrará los productos que no aparecen en la tabla de ventas:

# A tibble: 3 × 3
  id_producto nombre  categoria  
        <dbl> <chr>   <chr>      
1         102 Tablet  Móviles    
2         104 Monitor Periféricos
3         105 Teclado Periféricos

Este resultado nos muestra los productos que no han tenido ninguna venta registrada, lo cual podría ser útil para identificar inventario estancado o productos que necesitan promoción.

Casos de uso prácticos

Los semi-joins y anti-joins son herramientas poderosas para análisis de datos en situaciones específicas:

Identificación de registros faltantes o inconsistentes

# Creamos dataframes de ejemplo
estudiantes <- tibble(
  id_estudiante = c(1, 2, 3, 4, 5),
  nombre = c("Ana", "Bruno", "Carmen", "Daniel", "Elena"),
  carrera = c("Informática", "Matemáticas", "Física", "Química", "Biología")
)

examenes <- tibble(
  id_examen = c(101, 102, 103, 104),
  id_estudiante = c(1, 2, 5, 6),
  asignatura = c("Programación", "Álgebra", "Genética", "Estadística"),
  calificacion = c(8.5, 7.2, 9.0, 6.8)
)

# Estudiantes que no han realizado ningún examen
estudiantes_sin_examenes <- anti_join(estudiantes, examenes, by = "id_estudiante")

# Exámenes de estudiantes que no están en nuestra base de datos
examenes_sin_estudiante <- anti_join(examenes, estudiantes, by = "id_estudiante")

# Visualizamos los resultados
estudiantes_sin_examenes
examenes_sin_estudiante

Estos resultados nos permitirían identificar:

  • Estudiantes que no han realizado exámenes (Carmen y Daniel)
  • Exámenes registrados para estudiantes que no están en nuestra base de datos (id_estudiante = 6)

Filtrado eficiente de datos

Los semi-joins y anti-joins son más eficientes en memoria que los joins completos cuando solo necesitamos filtrar datos, ya que no duplican información:

# Creamos dataframes de ejemplo
clientes <- tibble(
  id_cliente = c(1, 2, 3, 4, 5),
  nombre = c("María", "Pedro", "Lucía", "Jorge", "Sara"),
  ciudad = c("Madrid", "Barcelona", "Valencia", "Sevilla", "Bilbao")
)

clientes_vip <- tibble(
  id_cliente = c(1, 3, 6),
  fecha_alta_vip = c("2022-01-15", "2022-03-20", "2022-06-10")
)

# Filtramos para obtener solo los clientes VIP
solo_clientes_vip <- semi_join(clientes, clientes_vip, by = "id_cliente")

# Filtramos para obtener los clientes que no son VIP
clientes_no_vip <- anti_join(clientes, clientes_vip, by = "id_cliente")

# Visualizamos los resultados
solo_clientes_vip
clientes_no_vip

Este enfoque es más limpio que usar un left_join seguido de un filtro, especialmente cuando trabajamos con grandes volúmenes de datos.

Combinando semi-joins y anti-joins con otras operaciones

Estos joins se pueden combinar con otras operaciones de dplyr para crear flujos de análisis más complejos:

# Creamos dataframes de ejemplo
productos <- tibble(
  id_producto = c(101, 102, 103, 104, 105),
  nombre = c("Laptop", "Tablet", "Smartphone", "Monitor", "Teclado"),
  precio = c(1200, 400, 600, 250, 80),
  categoria = c("Informática", "Móviles", "Móviles", "Periféricos", "Periféricos")
)

ventas <- tibble(
  id_venta = c(1, 2, 3, 4, 5),
  id_producto = c(101, 103, 103, 102, 105),
  fecha = c("2023-01-15", "2023-01-20", "2023-02-05", "2023-02-10", "2023-03-01"),
  cantidad = c(1, 2, 1, 3, 2)
)

# Productos vendidos más de una vez
productos_populares <- ventas %>%
  group_by(id_producto) %>%
  summarise(total_vendido = sum(cantidad)) %>%
  filter(total_vendido > 1) %>%
  semi_join(productos, by = "id_producto")

# Productos caros que no se han vendido
productos_caros_sin_ventas <- productos %>%
  filter(precio > 300) %>%
  anti_join(ventas, by = "id_producto")

# Visualizamos los resultados
productos_populares
productos_caros_sin_ventas

Diferencias clave con otros tipos de joins

Es importante entender las diferencias fundamentales entre estos joins y los que vimos anteriormente:

  • Semi-join vs Inner join: El semi-join solo devuelve columnas de la primera tabla, mientras que el inner join combina columnas de ambas tablas.

  • Anti-join vs Left join con filtro: El anti-join es más eficiente y claro que hacer un left join y luego filtrar los NA.

  • Propósito principal: Los semi-joins y anti-joins están diseñados específicamente para filtrar datos, no para combinarlos.

Veamos una comparación visual con un ejemplo sencillo:

# Comparación entre diferentes tipos de joins
empleados <- tibble(
  id = c(1, 2, 3, 4),
  nombre = c("Ana", "Berto", "Carmen", "David")
)

proyectos <- tibble(
  id_proyecto = c(101, 102, 103),
  id_empleado = c(1, 2, 5),
  proyecto = c("Web", "App", "Base de datos")
)

# Diferentes tipos de joins
inner <- inner_join(empleados, proyectos, by = c("id" = "id_empleado"))
left <- left_join(empleados, proyectos, by = c("id" = "id_empleado"))
semi <- semi_join(empleados, proyectos, by = c("id" = "id_empleado"))
anti <- anti_join(empleados, proyectos, by = c("id" = "id_empleado"))

# Visualizamos los resultados
inner  # Ana y Berto con sus proyectos
left   # Todos los empleados, Carmen y David con NA en proyectos
semi   # Solo Ana y Berto, sin información de proyectos
anti   # Solo Carmen y David

Consejos prácticos para usar semi-joins y anti-joins

  • Usa semi-joins cuando necesites filtrar registros que existen en otra tabla sin necesitar los datos adicionales.

  • Utiliza anti-joins para identificar excepciones o registros que faltan entre conjuntos de datos.

  • Estos joins son más eficientes que sus equivalentes con inner_join o left_join seguidos de filtros, especialmente con grandes conjuntos de datos.

  • Recuerda que estos joins no añaden columnas de la segunda tabla, solo filtran filas de la primera.

# Ejemplo de eficiencia: Encontrar clientes con compras recientes

# Enfoque con semi-join (más eficiente)
clientes_activos <- semi_join(clientes, 
                             filter(compras, fecha > "2023-01-01"), 
                             by = "id_cliente")

# Enfoque alternativo menos eficiente
clientes_activos_alt <- clientes %>%
  left_join(compras, by = "id_cliente") %>%
  filter(fecha > "2023-01-01") %>%
  select(id_cliente, nombre, ciudad) %>%
  distinct()

Los semi-joins y anti-joins son herramientas de filtrado especializado que complementan perfectamente a los inner y outer joins, permitiéndote realizar operaciones de filtrado basadas en relaciones entre tablas de manera clara y eficiente.

Aprende R online

Otros ejercicios de programación de R

Evalúa tus conocimientos de esta lección joins en r para combinación y relaciones de tablas con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.

Todas las lecciones de R

Accede a todas las lecciones de R y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Instalación De R Y Rstudio

R

Introducción Y Entorno

Introducción A R

R

Introducción Y Entorno

Operadores

R

Sintaxis

Estructuras De Datos

R

Sintaxis

Funciones

R

Sintaxis

Estructuras De Control Iterativo

R

Sintaxis

Scopes Y Closures

R

Sintaxis

Estructuras De Control Condicional

R

Sintaxis

Funciones Anónimas

R

Sintaxis

Tipos De Datos Y Variables

R

Sintaxis

Sistema R6: Clases Referenciales Y Encapsulamiento

R

Programación Orientada A Objetos

Sistema S4: Clases Formales Y Validación

R

Programación Orientada A Objetos

Herencia Y Polimorfismo En R

R

Programación Orientada A Objetos

Sistemas De Oop En R

R

Programación Orientada A Objetos

Sistema S3: Clases Implícitas Y Métodos Genéricos

R

Programación Orientada A Objetos

Tidyverse Para Transformación De Datos

R

Manipulación De Datos

Lubridate Para Fechas Y Tiempo

R

Manipulación De Datos

Group_by Y Summarize Para Agrupación Y Resumen

R

Manipulación De Datos

Stringr Para Expresiones Regulares

R

Manipulación De Datos

Tidyr Para Limpieza De Valores Faltantes

R

Manipulación De Datos

Joins En R Para Combinación Y Relaciones De Tablas

R

Manipulación De Datos

Pivot_longer Y Pivot_wider Para Reestructuración

R

Manipulación De Datos

Mutate Y Transmute Para Transformación

R

Manipulación De Datos

Dplyr Para Filtrado Y Selección

R

Manipulación De Datos

Readr Y Read.csv Para Importar Datos

R

Manipulación De Datos

Gráficos Bivariantes En R

R

Visualización De Datos

Gráficos Univariantes En R

R

Visualización De Datos

Facetas En Ggplot2

R

Visualización De Datos

Personalización Y Temas

R

Visualización De Datos

Ggplot2 Para Visualización De Datos

R

Visualización De Datos

Gráficos Multivariantes En R

R

Visualización De Datos

Correlación En R

R

Estadística

Regresión Lineal En R

R

Estadística

Pruebas De Hipótesis En R

R

Estadística

Anova En R

R

Estadística

Estadística Descriptiva En R

R

Estadística

Accede GRATIS a R y certifícate

En esta lección

Objetivos de aprendizaje de esta lección

  • Comprender qué son los joins y su utilidad para combinar tablas en R.
  • Aprender a usar inner_join, left_join, right_join y full_join para diferentes tipos de combinaciones.
  • Saber especificar columnas de unión cuando tienen nombres diferentes y unir por múltiples columnas.
  • Entender el uso de semi_join y anti_join para filtrar filas basándose en coincidencias o ausencia de ellas.
  • Aplicar joins en casos prácticos de análisis de datos para combinar y filtrar información eficientemente.