Datos de partida

# 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)
)

Parte 1 — Diagnóstico Relacional

1.1 Facturas con id_cliente inexistente en clientes

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

Hallazgo: F006 tiene id_cliente = "C007", que no existe en clientes. Es un cliente sin registro que generó una factura. Antes de cualquier análisis de ventas, conviene investigar el origen de ese registro.


1.2 Clientes sin ninguna factura

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

Resultado: 2 clientes sin facturas: Luis García (C004) y Jorge Reyes (C006).


1.3 Facturas sin pago registrado

facturas_sin_pago <- anti_join(facturas, pagos, by = "id_factura")
facturas_sin_pago |> select(id_factura, id_cliente, mes, monto)

Resultado: Las facturas F003, F005, F006 y F008 no tienen pago asociado.


1.4 Duplicados en la tabla facturas

facturas |>
  count(id_factura) |>
  filter(n > 1)
# Revisión por contenido: mismo cliente, mes y monto
facturas |>
  count(id_cliente, mes, monto) |>
  filter(n > 1)

Hallazgo: No hay id_factura repetido, pero C001 tiene dos registros con el mismo mes (Ene) y el mismo monto (15000): F001 y F008. Son registros idénticos en contenido.

Decisión: Tratar F008 como duplicado y eliminarlo con filter(!duplicated(.)). Se conserva F001 por ser el registro más antiguo en el vector.


Parte 2 — Aplicando los 4 Joins

2.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

Resultado: 6 clientes originales, 9 filas tras el join. El resultado tiene más filas porque left_join expande: un cliente con varias facturas produce una fila por cada factura. Ana Torres (C001) tiene 3 facturas, por eso aparece 3 veces. Los clientes sin facturas (C004, C006) aparecen con NA en las columnas de facturas.


2.2 inner_join

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

nrow(clientes_inner)
## [1] 7
clientes_inner

Resultado: 8 filas. Desaparecen Luis García (C004) y Jorge Reyes (C006). El inner_join conserva solo filas con coincidencia en ambas tablas, por eso excluye a los clientes sin facturas.


2.3 full_join

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

nrow(clientes_full)
## [1] 10
clientes_full

Resultado: 10 filas. Aparece una fila con id_cliente = "C007" y NA en todas las columnas de clientes. El full_join conserva todos los registros de ambas tablas, incluyendo los que no tienen correspondencia en ninguno de los dos lados.


2.4 Joins encadenados: tabla maestra

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

print(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

Resultado:

  • Filas: 9. El segundo left_join (con pagos) no agrega filas porque pagos es una tabla de atributos: cada factura aparece a lo sumo una vez.
  • monto_pagado con NA: Las facturas F003, F005 y F008 no existen en pagos, así que el join no encuentra correspondencia y rellena con NA.

Parte 3 — Joins de Filtro: Auditoría

3.1 Clientes con al menos una factura

clientes_activos <- semi_join(clientes, facturas, by = "id_cliente")
nrow(clientes_activos)
## [1] 4
clientes_activos

Resultado: 4 clientes con al menos una factura. semi_join filtra sin agregar columnas de la tabla derecha.


3.2 Clientes que nunca han facturado

clientes |>
  anti_join(facturas, by = "id_cliente") |>
  select(nombre, ciudad)

3.3 Facturas de clientes no registrados

anti_join(facturas, clientes, by = "id_cliente") |>
  select(id_factura, id_cliente, monto)

Parte 4 — Manejo de Duplicados

4.1 Eliminar duplicados exactos

facturas_sin_dup <- facturas |> filter()
nrow(facturas_sin_dup)
## [1] 8
facturas_sin_dup

Resultado: 7 filas. Se eliminó F008, que era duplicado exacto de F001.


4.2 Factura de mayor monto por cliente

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

facturas_max

4.3 Tabla maestra con facturas_sin_dup

tabla_maestra_limpia <- clientes |>
  left_join(facturas_sin_dup, by = "id_cliente") |>
  left_join(pagos,            by = "id_factura")

nrow(tabla_maestra_limpia)
## [1] 9
tabla_maestra_limpia

Resultado: 8 filas, una menos que antes. Se eliminó la fila correspondiente a F008.


Parte 5 — Tidy Data con pivot

5.1 Formato ancho: ventas por cliente por mes

ventas_por_mes <- tabla_maestra_limpia |>
  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

5.2 Volver a formato largo

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

ventas_largo

5.3 Total vendido por mes

ventas_largo |>
  group_by(mes) |>
  summarise(total_mes = sum(total)) |>
  arrange(desc(total_mes))