Semana 07 - Joins, Tidy Data y Relaciones entre Tablas

Author

Johamnely Mateo

Introducción

En esta práctica se trabaja con relaciones entre tablas utilizando diferentes tipos de joins del paquete dplyr.

El objetivo principal es analizar la relación entre clientes, facturas y pagos, identificar inconsistencias en los datos, manejar duplicados y transformar información utilizando herramientas de tidy data como pivot_wider() y pivot_longer().

Librerías

library(tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.2.1     ✔ readr     2.2.0
✔ forcats   1.0.1     ✔ stringr   1.6.0
✔ ggplot2   4.0.3     ✔ tibble    3.3.1
✔ lubridate 1.9.5     ✔ tidyr     1.3.2
✔ purrr     1.2.2     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(dplyr)
library(tidyr)
library(knitr)

Parte 1 - Creación de los Datasets

Tabla de clientes

clientes <- tibble(
  id_cliente = c("C001", "C002", "C003", "C004", "C005", "C006"),

  nombre = c(
    "Ana Torres",
    "Pedro Méndez",
    "María López",
    "Luis García",
    "Carmen Díaz",
    "Jorge Reyes"
  ),

  ciudad = c(
    "Santo Domingo",
    "Santiago",
    "Santo Domingo",
    "La Vega",
    "San Pedro",
    "Santo Domingo"
  ),

  tipo = c(
    "Premium",
    "Regular",
    "Premium",
    "Regular",
    "Regular",
    "Premium"
  )
)

clientes
# A tibble: 6 × 4
  id_cliente nombre       ciudad        tipo   
  <chr>      <chr>        <chr>         <chr>  
1 C001       Ana Torres   Santo Domingo Premium
2 C002       Pedro Méndez Santiago      Regular
3 C003       María López  Santo Domingo Premium
4 C004       Luis García  La Vega       Regular
5 C005       Carmen Díaz  San Pedro     Regular
6 C006       Jorge Reyes  Santo Domingo Premium

Tabla de facturas

facturas <- tibble(
  id_factura = c(
    "F001", "F002", "F003", "F004",
    "F005", "F006", "F007", "F008"
  ),

  id_cliente = c(
    "C001", "C001", "C002", "C003",
    "C005", "C007", "C002", "C001"
  ),

  mes = c(
    "Ene", "Feb", "Ene", "Feb",
    "Ene", "Feb", "Mar", "Ene"
  ),

  monto = c(
    15000, 22000, 8500, 31000,
    4500, 12000, 9800, 15000
  )
)

facturas
# A tibble: 8 × 4
  id_factura id_cliente mes   monto
  <chr>      <chr>      <chr> <dbl>
1 F001       C001       Ene   15000
2 F002       C001       Feb   22000
3 F003       C002       Ene    8500
4 F004       C003       Feb   31000
5 F005       C005       Ene    4500
6 F006       C007       Feb   12000
7 F007       C002       Mar    9800
8 F008       C001       Ene   15000

Tabla de pagos

pagos <- tibble(
  id_factura = c(
    "F001", "F002", "F004",
    "F006", "F007"
  ),

  monto_pagado = c(
    15000, 22000, 31000,
    12000, 9800
  )
)

pagos
# A tibble: 5 × 2
  id_factura monto_pagado
  <chr>             <dbl>
1 F001              15000
2 F002              22000
3 F004              31000
4 F006              12000
5 F007               9800

Parte 2 - Diagnóstico Relacional

2.1 Facturas con clientes inexistentes

facturas_sin_cliente <- anti_join(
  facturas,
  clientes,
  by = "id_cliente"
)

facturas_sin_cliente
# A tibble: 1 × 4
  id_factura id_cliente mes   monto
  <chr>      <chr>      <chr> <dbl>
1 F006       C007       Feb   12000

Observación

La factura F006 pertenece al cliente C007, pero dicho cliente no existe en la tabla clientes.

2.2 Clientes sin facturas

clientes_sin_factura <- anti_join(
  clientes,
  facturas,
  by = "id_cliente"
)

clientes_sin_factura
# A tibble: 2 × 4
  id_cliente nombre      ciudad        tipo   
  <chr>      <chr>       <chr>         <chr>  
1 C004       Luis García La Vega       Regular
2 C006       Jorge Reyes Santo Domingo Premium

Observación

Existen clientes que no poseen facturas registradas.

nrow(clientes_sin_factura)
[1] 2

2.3 Facturas sin pago registrado

facturas_sin_pago <- anti_join(
  facturas,
  pagos,
  by = "id_factura"
)

facturas_sin_pago
# A tibble: 3 × 4
  id_factura id_cliente mes   monto
  <chr>      <chr>      <chr> <dbl>
1 F003       C002       Ene    8500
2 F005       C005       Ene    4500
3 F008       C001       Ene   15000

IDs de facturas sin pago

facturas_sin_pago$id_factura
[1] "F003" "F005" "F008"

2.4 Verificación de duplicados

duplicados <- facturas %>%
  count(id_factura) %>%
  filter(n > 1)

duplicados
# A tibble: 0 × 2
# ℹ 2 variables: id_factura <chr>, n <int>

Observación

No existen facturas duplicadas exactas por id_factura.

Sin embargo, un mismo cliente puede tener múltiples facturas, lo cual es completamente válido en un sistema de facturación.

Parte 3 - Aplicando los 4 Joins

3.1 Left Join

nrow(clientes)
[1] 6
clientes_con_facturas <- clientes %>%
  left_join(facturas, by = "id_cliente")

nrow(clientes_con_facturas)
[1] 9
clientes_con_facturas
# A tibble: 9 × 7
  id_cliente nombre       ciudad        tipo    id_factura mes   monto
  <chr>      <chr>        <chr>         <chr>   <chr>      <chr> <dbl>
1 C001       Ana Torres   Santo Domingo Premium F001       Ene   15000
2 C001       Ana Torres   Santo Domingo Premium F002       Feb   22000
3 C001       Ana Torres   Santo Domingo Premium F008       Ene   15000
4 C002       Pedro Méndez Santiago      Regular F003       Ene    8500
5 C002       Pedro Méndez Santiago      Regular F007       Mar    9800
6 C003       María López  Santo Domingo Premium F004       Feb   31000
7 C004       Luis García  La Vega       Regular <NA>       <NA>     NA
8 C005       Carmen Díaz  San Pedro     Regular F005       Ene    4500
9 C006       Jorge Reyes  Santo Domingo Premium <NA>       <NA>     NA

Observación

El resultado contiene más filas porque algunos clientes tienen múltiples facturas, por lo tanto el join replica la información del cliente para cada factura relacionada.

3.2 Inner Join

clientes_inner <- clientes %>%
  inner_join(facturas, by = "id_cliente")

clientes_inner
# A tibble: 7 × 7
  id_cliente nombre       ciudad        tipo    id_factura mes   monto
  <chr>      <chr>        <chr>         <chr>   <chr>      <chr> <dbl>
1 C001       Ana Torres   Santo Domingo Premium F001       Ene   15000
2 C001       Ana Torres   Santo Domingo Premium F002       Feb   22000
3 C001       Ana Torres   Santo Domingo Premium F008       Ene   15000
4 C002       Pedro Méndez Santiago      Regular F003       Ene    8500
5 C002       Pedro Méndez Santiago      Regular F007       Mar    9800
6 C003       María López  Santo Domingo Premium F004       Feb   31000
7 C005       Carmen Díaz  San Pedro     Regular F005       Ene    4500

Observación

Los clientes sin facturas desaparecen del resultado porque inner_join() solo conserva coincidencias presentes en ambas tablas.

3.3 Full Join

clientes_full <- clientes %>%
  full_join(facturas, by = "id_cliente")

clientes_full
# A tibble: 10 × 7
   id_cliente nombre       ciudad        tipo    id_factura mes   monto
   <chr>      <chr>        <chr>         <chr>   <chr>      <chr> <dbl>
 1 C001       Ana Torres   Santo Domingo Premium F001       Ene   15000
 2 C001       Ana Torres   Santo Domingo Premium F002       Feb   22000
 3 C001       Ana Torres   Santo Domingo Premium F008       Ene   15000
 4 C002       Pedro Méndez Santiago      Regular F003       Ene    8500
 5 C002       Pedro Méndez Santiago      Regular F007       Mar    9800
 6 C003       María López  Santo Domingo Premium F004       Feb   31000
 7 C004       Luis García  La Vega       Regular <NA>       <NA>     NA
 8 C005       Carmen Díaz  San Pedro     Regular F005       Ene    4500
 9 C006       Jorge Reyes  Santo Domingo Premium <NA>       <NA>     NA
10 C007       <NA>         <NA>          <NA>    F006       Feb   12000

Observación

Aparece una fila adicional correspondiente al cliente C007 proveniente de la tabla facturas, aunque dicho cliente no existe en la tabla clientes.

3.4 Joins encadenados

tabla_maestra <- clientes %>%
  left_join(facturas, by = "id_cliente") %>%
  left_join(pagos, by = "id_factura")

tabla_maestra
# A tibble: 9 × 8
  id_cliente nombre       ciudad       tipo  id_factura mes   monto monto_pagado
  <chr>      <chr>        <chr>        <chr> <chr>      <chr> <dbl>        <dbl>
1 C001       Ana Torres   Santo Domin… Prem… F001       Ene   15000        15000
2 C001       Ana Torres   Santo Domin… Prem… F002       Feb   22000        22000
3 C001       Ana Torres   Santo Domin… Prem… F008       Ene   15000           NA
4 C002       Pedro Méndez Santiago     Regu… F003       Ene    8500           NA
5 C002       Pedro Méndez Santiago     Regu… F007       Mar    9800         9800
6 C003       María López  Santo Domin… Prem… F004       Feb   31000        31000
7 C004       Luis García  La Vega      Regu… <NA>       <NA>     NA           NA
8 C005       Carmen Díaz  San Pedro    Regu… F005       Ene    4500           NA
9 C006       Jorge Reyes  Santo Domin… Prem… <NA>       <NA>     NA           NA

Cantidad de filas

nrow(tabla_maestra)
[1] 9

Observación

Algunos valores de monto_pagado son NA porque existen facturas que todavía no tienen pagos registrados en la tabla pagos.

Parte 4 - Joins de Filtro

4.1 Clientes con al menos una factura

clientes_con_factura <- semi_join(
  clientes,
  facturas,
  by = "id_cliente"
)

clientes_con_factura
# A tibble: 4 × 4
  id_cliente nombre       ciudad        tipo   
  <chr>      <chr>        <chr>         <chr>  
1 C001       Ana Torres   Santo Domingo Premium
2 C002       Pedro Méndez Santiago      Regular
3 C003       María López  Santo Domingo Premium
4 C005       Carmen Díaz  San Pedro     Regular

Cantidad de clientes con factura

nrow(clientes_con_factura)
[1] 4

4.2 Clientes que nunca han facturado

clientes_sin_factura <- anti_join(
  clientes,
  facturas,
  by = "id_cliente"
)

clientes_sin_factura %>%
  select(nombre, ciudad)
# A tibble: 2 × 2
  nombre      ciudad       
  <chr>       <chr>        
1 Luis García La Vega      
2 Jorge Reyes Santo Domingo

4.3 Facturas de clientes no registrados

facturas_no_registradas <- anti_join(
  facturas,
  clientes,
  by = "id_cliente"
)

facturas_no_registradas %>%
  select(id_factura, id_cliente, monto)
# A tibble: 1 × 3
  id_factura id_cliente monto
  <chr>      <chr>      <dbl>
1 F006       C007       12000

Parte 5 - Manejo de Duplicados

5.1 Eliminación de duplicados exactos

facturas_sin_dup <- facturas %>%
  filter(!duplicated(.))

facturas_sin_dup
# A tibble: 8 × 4
  id_factura id_cliente mes   monto
  <chr>      <chr>      <chr> <dbl>
1 F001       C001       Ene   15000
2 F002       C001       Feb   22000
3 F003       C002       Ene    8500
4 F004       C003       Feb   31000
5 F005       C005       Ene    4500
6 F006       C007       Feb   12000
7 F007       C002       Mar    9800
8 F008       C001       Ene   15000

Cantidad de filas luego de eliminar duplicados

nrow(facturas_sin_dup)
[1] 8

5.2 Factura de mayor monto por cliente

facturas_max <- facturas %>%
  group_by(id_cliente) %>%
  slice_max(order_by = monto, n = 1) %>%
  ungroup()

facturas_max
# A tibble: 5 × 4
  id_factura id_cliente mes   monto
  <chr>      <chr>      <chr> <dbl>
1 F002       C001       Feb   22000
2 F007       C002       Mar    9800
3 F004       C003       Feb   31000
4 F005       C005       Ene    4500
5 F006       C007       Feb   12000

5.3 Nueva tabla maestra usando facturas_sin_dup

tabla_maestra_2 <- clientes %>%
  left_join(facturas_sin_dup, by = "id_cliente") %>%
  left_join(pagos, by = "id_factura")

tabla_maestra_2
# A tibble: 9 × 8
  id_cliente nombre       ciudad       tipo  id_factura mes   monto monto_pagado
  <chr>      <chr>        <chr>        <chr> <chr>      <chr> <dbl>        <dbl>
1 C001       Ana Torres   Santo Domin… Prem… F001       Ene   15000        15000
2 C001       Ana Torres   Santo Domin… Prem… F002       Feb   22000        22000
3 C001       Ana Torres   Santo Domin… Prem… F008       Ene   15000           NA
4 C002       Pedro Méndez Santiago     Regu… F003       Ene    8500           NA
5 C002       Pedro Méndez Santiago     Regu… F007       Mar    9800         9800
6 C003       María López  Santo Domin… Prem… F004       Feb   31000        31000
7 C004       Luis García  La Vega      Regu… <NA>       <NA>     NA           NA
8 C005       Carmen Díaz  San Pedro    Regu… F005       Ene    4500           NA
9 C006       Jorge Reyes  Santo Domin… Prem… <NA>       <NA>     NA           NA

Número de filas

nrow(tabla_maestra_2)
[1] 9

Observación

El número de filas no cambia porque originalmente no existían duplicados exactos en la tabla facturas.

Parte 6 - Tidy Data con Pivot

6.1 Ventas por mes en formato ancho

ventas_por_mes <- tabla_maestra_2 %>%
  filter(!is.na(mes)) %>%
  group_by(nombre, mes) %>%
  summarise(
    total = sum(monto, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  pivot_wider(
    names_from = mes,
    values_from = total,
    values_fill = 0
  )

ventas_por_mes
# A tibble: 4 × 4
  nombre         Ene   Feb   Mar
  <chr>        <dbl> <dbl> <dbl>
1 Ana Torres   30000 22000     0
2 Carmen Díaz   4500     0     0
3 María López      0 31000     0
4 Pedro Méndez  8500     0  9800

Tabla de ventas por mes

knitr::kable(
  ventas_por_mes,
  caption = "Ventas por Mes por Cliente"
)
Ventas por Mes por Cliente
nombre Ene Feb Mar
Ana Torres 30000 22000 0
Carmen Díaz 4500 0 0
María López 0 31000 0
Pedro Méndez 8500 0 9800

6.2 Conversión nuevamente a formato largo

ventas_largo <- ventas_por_mes %>%
  pivot_longer(
    cols = -nombre,
    names_to = "mes",
    values_to = "total"
  )

ventas_largo
# A tibble: 12 × 3
   nombre       mes   total
   <chr>        <chr> <dbl>
 1 Ana Torres   Ene   30000
 2 Ana Torres   Feb   22000
 3 Ana Torres   Mar       0
 4 Carmen Díaz  Ene    4500
 5 Carmen Díaz  Feb       0
 6 Carmen Díaz  Mar       0
 7 María López  Ene       0
 8 María López  Feb   31000
 9 María López  Mar       0
10 Pedro Méndez Ene    8500
11 Pedro Méndez Feb       0
12 Pedro Méndez Mar    9800

Tabla en formato largo

knitr::kable(
  ventas_largo,
  caption = "Ventas en Formato Largo"
)
Ventas en Formato Largo
nombre mes total
Ana Torres Ene 30000
Ana Torres Feb 22000
Ana Torres Mar 0
Carmen Díaz Ene 4500
Carmen Díaz Feb 0
Carmen Díaz Mar 0
María López Ene 0
María López Feb 31000
María López Mar 0
Pedro Méndez Ene 8500
Pedro Méndez Feb 0
Pedro Méndez Mar 9800

6.3 Total vendido por mes

total_por_mes <- ventas_largo %>%
  group_by(mes) %>%
  summarise(
    total_vendido = sum(total)
  ) %>%
  arrange(desc(total_vendido))

total_por_mes
# A tibble: 3 × 2
  mes   total_vendido
  <chr>         <dbl>
1 Feb           53000
2 Ene           43000
3 Mar            9800

Tabla total vendido por mes

knitr::kable(
  total_por_mes,
  caption = "Total Vendido por Mes"
)
Total Vendido por Mes
mes total_vendido
Feb 53000
Ene 43000
Mar 9800

Conclusión

Durante esta práctica se trabajó con diferentes tipos de joins utilizando dplyr, permitiendo comprender cómo se relacionan distintas tablas dentro de un sistema de información.

Además, se aplicaron técnicas de auditoría de datos mediante anti_join() y semi_join(), manejo de duplicados y transformación de datos utilizando pivot_wider() y pivot_longer().

Finalmente, se construyó una tabla maestra consolidada y diferentes resúmenes analíticos para validar la correcta integración de los datos.