Code
library(tidyverse)
library(kableExtra)🎯 Objetivo: aprender a combinar, filtrar y auditar tablas mediante diferentes tipos de joins (controlando duplicados y registros huérfanos), así como a reestructurar datos aplicando los principios de Tidy Data.
library(tidyverse)
library(kableExtra)# Tabla 1: 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")
)
# Tabla 2: 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)
)
# Tabla 3: Pagos
pagos <- tibble(
id_factura = c("F001", "F002", "F004", "F006", "F007"),
monto_pagado = c(15000, 22000, 31000, 12000, 9800)
)| id_cliente | nombre | ciudad | tipo |
|---|---|---|---|
| C001 | Ana Torres | Santo Domingo | Premium |
| C002 | Pedro Méndez | Santiago | Regular |
| C003 | María López | Santo Domingo | Premium |
| C004 | Luis García | La Vega | Regular |
| C005 | Carmen Díaz | San Pedro | Regular |
| C006 | Jorge Reyes | Santo Domingo | Premium |
| id_factura | id_cliente | mes | monto |
|---|---|---|---|
| F001 | C001 | Ene | 15000 |
| F002 | C001 | Feb | 22000 |
| F003 | C002 | Ene | 8500 |
| F004 | C003 | Feb | 31000 |
| F005 | C005 | Ene | 4500 |
| F006 | C007 | Feb | 12000 |
| F007 | C002 | Mar | 9800 |
| F008 | C001 | Ene | 15000 |
| id_factura | monto_pagado |
|---|---|
| F001 | 15000 |
| F002 | 22000 |
| F004 | 31000 |
| F006 | 12000 |
| F007 | 9800 |
facturas_sin_cliente <- anti_join(facturas, clientes, by = "id_cliente")| id_factura | id_cliente | mes | monto |
|---|---|---|---|
| F006 | C007 | Feb | 12000 |
La factura F006 pertenece al cliente C007, el cual no existe en la tabla clientes.
sin_factura_registrada <- clientes |>
anti_join(facturas, clientes, by = "id_cliente")| id_cliente | nombre | ciudad | tipo |
|---|---|---|---|
| C004 | Luis García | La Vega | Regular |
| C006 | Jorge Reyes | Santo Domingo | Premium |
Hay 2 clientes sin facturas
sin_pago_registrado <- anti_join(facturas, pagos, by = "id_factura" )| id_factura | id_cliente | mes | monto |
|---|---|---|---|
| F003 | C002 | Ene | 8500 |
| F005 | C005 | Ene | 4500 |
| F008 | C001 | Ene | 15000 |
IDs: F003, F005, F008
duplicados <- facturas |>
count(id_cliente) |>
filter(n > 1)| id_cliente | n |
|---|---|
| C001 | 3 |
| C002 | 2 |
El cliente C001 tiene 3 facturas. El cliente C002 tiene 2 facturas.
Antes de eliminar alguna factura, primero me fijaria si son distintas, ya que un cliente puede tener varias facturas. Es entonces que tomaria una decision
nrow(clientes)[1] 6
clientes_con_facturas <- clientes |> left_join(facturas, by = "id_cliente")
nrow(clientes_con_facturas)[1] 9
| id_cliente | nombre | ciudad | tipo | id_factura | mes | monto |
|---|---|---|---|---|---|---|
| C001 | Ana Torres | Santo Domingo | Premium | F001 | Ene | 15000 |
| C001 | Ana Torres | Santo Domingo | Premium | F002 | Feb | 22000 |
| C001 | Ana Torres | Santo Domingo | Premium | F008 | Ene | 15000 |
| C002 | Pedro Méndez | Santiago | Regular | F003 | Ene | 8500 |
| C002 | Pedro Méndez | Santiago | Regular | F007 | Mar | 9800 |
| C003 | María López | Santo Domingo | Premium | F004 | Feb | 31000 |
| C004 | Luis García | La Vega | Regular | NA | NA | NA |
| C005 | Carmen Díaz | San Pedro | Regular | F005 | Ene | 4500 |
| C006 | Jorge Reyes | Santo Domingo | Premium | NA | NA | NA |
Hay más filas porque algunos clientes tienen más de una factura, entonces se agregan al unir ambas filas.
clientes_con_facturas_inner_join <- clientes |> inner_join(facturas, by = "id_cliente")| id_cliente | nombre | ciudad | tipo | id_factura | mes | monto |
|---|---|---|---|---|---|---|
| C001 | Ana Torres | Santo Domingo | Premium | F001 | Ene | 15000 |
| C001 | Ana Torres | Santo Domingo | Premium | F002 | Feb | 22000 |
| C001 | Ana Torres | Santo Domingo | Premium | F008 | Ene | 15000 |
| C002 | Pedro Méndez | Santiago | Regular | F003 | Ene | 8500 |
| C002 | Pedro Méndez | Santiago | Regular | F007 | Mar | 9800 |
| C003 | María López | Santo Domingo | Premium | F004 | Feb | 31000 |
| C005 | Carmen Díaz | San Pedro | Regular | F005 | Ene | 4500 |
Desaparecen: Luis García (C004) y Jorge Reyes (C006). Porque no tienen facturas registradas.
Une clientes con facturas usando full_join. ¿Qué fila extra aparece que no estaba en ninguna de las dos tablas originales? Explica en un comentario por qué aparece esa fila.
clientes_con_facturas_full_join <- clientes |> full_join(facturas, by = "id_cliente")| id_cliente | nombre | ciudad | tipo | id_factura | mes | monto |
|---|---|---|---|---|---|---|
| C001 | Ana Torres | Santo Domingo | Premium | F001 | Ene | 15000 |
| C001 | Ana Torres | Santo Domingo | Premium | F002 | Feb | 22000 |
| C001 | Ana Torres | Santo Domingo | Premium | F008 | Ene | 15000 |
| C002 | Pedro Méndez | Santiago | Regular | F003 | Ene | 8500 |
| C002 | Pedro Méndez | Santiago | Regular | F007 | Mar | 9800 |
| C003 | María López | Santo Domingo | Premium | F004 | Feb | 31000 |
| C004 | Luis García | La Vega | Regular | NA | NA | NA |
| C005 | Carmen Díaz | San Pedro | Regular | F005 | Ene | 4500 |
| C006 | Jorge Reyes | Santo Domingo | Premium | NA | NA | NA |
| C007 | NA | NA | NA | F006 | Feb | 12000 |
Aparece C007 porque está en facturas pero no en clientes. Lo que hace full_join() es que completa los datos faltantes con NA.
tabla_maestra <- clientes |>
left_join(facturas, by = "id_cliente") |>
left_join(pagos, by = "id_factura")| id_cliente | nombre | ciudad | tipo | id_factura | mes | monto | monto_pagado |
|---|---|---|---|---|---|---|---|
| C001 | Ana Torres | Santo Domingo | Premium | F001 | Ene | 15000 | 15000 |
| C001 | Ana Torres | Santo Domingo | Premium | F002 | Feb | 22000 | 22000 |
| C001 | Ana Torres | Santo Domingo | Premium | F008 | Ene | 15000 | NA |
| C002 | Pedro Méndez | Santiago | Regular | F003 | Ene | 8500 | NA |
| C002 | Pedro Méndez | Santiago | Regular | F007 | Mar | 9800 | 9800 |
| C003 | María López | Santo Domingo | Premium | F004 | Feb | 31000 | 31000 |
| C004 | Luis García | La Vega | Regular | NA | NA | NA | NA |
| C005 | Carmen Díaz | San Pedro | Regular | F005 | Ene | 4500 | NA |
| C006 | Jorge Reyes | Santo Domingo | Premium | NA | NA | NA | NA |
¿Cuántas filas tiene? 9
¿Por qué algunos monto_pagado son NA? Sencillamente porque no hay un pago de forma registrada.
clientes_con_facturas_semi_join <- clientes |> semi_join(facturas, by = "id_cliente")| id_cliente | nombre | ciudad | tipo |
|---|---|---|---|
| C001 | Ana Torres | Santo Domingo | Premium |
| C002 | Pedro Méndez | Santiago | Regular |
| C003 | María López | Santo Domingo | Premium |
| C005 | Carmen Díaz | San Pedro | Regular |
Son: 4
clientes_con_facturas_anti_join <- clientes |> anti_join(facturas, by = "id_cliente") |> select(id_cliente, nombre, ciudad)| id_cliente | nombre | ciudad |
|---|---|---|
| C004 | Luis García | La Vega |
| C006 | Jorge Reyes | Santo Domingo |
clientes_no_registrados <- facturas |> anti_join(clientes, by = "id_cliente") |> select(id_factura, id_cliente, monto)| id_factura | id_cliente | monto |
|---|---|---|
| F006 | C007 | 12000 |
#facturas_sin_dup <- facturas |> filter(!duplicated(.)) <----- no funciona, tuve que usar:
facturas_sin_dup <- facturas |> distinct()| id_factura | id_cliente | mes | monto |
|---|---|---|---|
| F001 | C001 | Ene | 15000 |
| F002 | C001 | Feb | 22000 |
| F003 | C002 | Ene | 8500 |
| F004 | C003 | Feb | 31000 |
| F005 | C005 | Ene | 4500 |
| F006 | C007 | Feb | 12000 |
| F007 | C002 | Mar | 9800 |
| F008 | C001 | Ene | 15000 |
¿Cuántas filas quedan? 8
facturas_max <- facturas |>
group_by(id_cliente) |>
slice_max(order_by = monto, n = 1) |>
ungroup()| id_factura | id_cliente | mes | monto |
|---|---|---|---|
| F002 | C001 | Feb | 22000 |
| F007 | C002 | Mar | 9800 |
| F004 | C003 | Feb | 31000 |
| F005 | C005 | Ene | 4500 |
| F006 | C007 | Feb | 12000 |
tabla_maestra2 <- clientes |>
left_join(facturas_sin_dup, by = "id_cliente") |>
left_join(pagos, by = "id_factura")| id_cliente | nombre | ciudad | tipo | id_factura | mes | monto | monto_pagado |
|---|---|---|---|---|---|---|---|
| C001 | Ana Torres | Santo Domingo | Premium | F001 | Ene | 15000 | 15000 |
| C001 | Ana Torres | Santo Domingo | Premium | F002 | Feb | 22000 | 22000 |
| C001 | Ana Torres | Santo Domingo | Premium | F008 | Ene | 15000 | NA |
| C002 | Pedro Méndez | Santiago | Regular | F003 | Ene | 8500 | NA |
| C002 | Pedro Méndez | Santiago | Regular | F007 | Mar | 9800 | 9800 |
| C003 | María López | Santo Domingo | Premium | F004 | Feb | 31000 | 31000 |
| C004 | Luis García | La Vega | Regular | NA | NA | NA | NA |
| C005 | Carmen Díaz | San Pedro | Regular | F005 | Ene | 4500 | NA |
| C006 | Jorge Reyes | Santo Domingo | Premium | NA | NA | NA | NA |
El numero de filas no cambio, sigue siendo 9. no había duplicados exactos para eliminar
ventas_por_mes <- tabla_maestra |>
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 # Rellenar con 0 donde no hay ventas
)| 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 |
ventas_largo <- ventas_por_mes |>
pivot_longer(
cols = -nombre, # Todo excepto la columna nombre
names_to = "mes",
values_to = "total"
)| 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 |
total_vendido_mes <- ventas_largo |>
group_by(mes) |>
summarise(
total_vendido = sum(total), .groups = "drop"
) |>
arrange(desc(total_vendido))| mes | total_vendido |
|---|---|
| Feb | 53000 |
| Ene | 43000 |
| Mar | 9800 |