library(tidyverse)Semana 06 — Limpieza de Datos y Resumen Analítico con dplyr
Introducción al Lenguaje de Programación y Estadística R — ITLA
1 Dataset Original
ventas_crudas <- tibble(
id_venta = c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 3),
vendedor = c("ana", "PEDRO", "María ", NA, "carmen", "ana",
"PEDRO", "luis", "carmen", "luis", "María "),
region = c("norte", "SUR", "Norte", "sur", "NORTE", "norte",
"sur", "Norte", "sur", "norte", "Norte"),
monto = c(15000, 22000, 18500, NA, 31000, 16000,
19500, 9500000, 21000, 17500, 18500),
mes = c(1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 1),
completada = c("SI", "SI", "NO", "SI", NA, "SI", "NO", "SI", "SI", "NO", "NO")
)
print(ventas_crudas)# A tibble: 11 × 6
id_venta vendedor region monto mes completada
<dbl> <chr> <chr> <dbl> <dbl> <chr>
1 1 "ana" norte 15000 1 SI
2 2 "PEDRO" SUR 22000 1 SI
3 3 "María " Norte 18500 1 NO
4 4 <NA> sur NA 2 SI
5 5 "carmen" NORTE 31000 2 <NA>
6 6 "ana" norte 16000 2 SI
7 7 "PEDRO" sur 19500 3 NO
8 8 "luis" Norte 9500000 3 SI
9 9 "carmen" sur 21000 3 SI
10 10 "luis" norte 17500 3 NO
11 3 "María " Norte 18500 1 NO
2 Parte 1 — Diagnóstico Inicial
2.1 glimpse() — Estructura del dataset
glimpse(ventas_crudas)Rows: 11
Columns: 6
$ id_venta <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 3
$ vendedor <chr> "ana", "PEDRO", "María ", NA, "carmen", "ana", "PEDRO", "lu…
$ region <chr> "norte", "SUR", "Norte", "sur", "NORTE", "norte", "sur", "N…
$ monto <dbl> 15000, 22000, 18500, NA, 31000, 16000, 19500, 9500000, 2100…
$ mes <dbl> 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 1
$ completada <chr> "SI", "SI", "NO", "SI", NA, "SI", "NO", "SI", "SI", "NO", "…
2.2 summary() — Resumen estadístico
summary(ventas_crudas) id_venta vendedor region monto mes
Min. : 1.000 Length :11 Length :11 Min. : 15000 Min. :1
1st Qu.: 3.000 N.unique : 5 N.unique : 5 1st Qu.: 17750 1st Qu.:1
Median : 5.000 N.blank : 0 N.blank : 0 Median : 19000 Median :2
Mean : 5.273 Min.nchar: 3 Min.nchar: 3 Mean : 967900 Mean :2
3rd Qu.: 7.500 Max.nchar: 6 Max.nchar: 5 3rd Qu.: 21750 3rd Qu.:3
Max. :10.000 NAs : 1 Max. :9500000 Max. :3
NAs :1
completada
Length :11
N.unique : 2
N.blank : 0
Min.nchar: 2
Max.nchar: 2
NAs : 1
2.3 Conteo de NAs por columna
cat("=== NAs por columna ===\n")=== NAs por columna ===
colSums(is.na(ventas_crudas)) id_venta vendedor region monto mes completada
0 1 0 1 0 1
2.4 Diagnóstico completo
# ── Duplicados ──────────────────────────────────────────────────────────────
n_filas_total <- nrow(ventas_crudas)
n_ids_unicos <- n_distinct(ventas_crudas$id_venta)
n_duplicados <- n_filas_total - n_ids_unicos
cat("=== DIAGNÓSTICO DEL DATASET ===\n\n")=== DIAGNÓSTICO DEL DATASET ===
cat("Filas totales: ", n_filas_total, "\n")Filas totales: 11
cat("IDs únicos: ", n_ids_unicos, "\n")IDs únicos: 10
cat("Filas duplicadas: ", n_duplicados, "\n")Filas duplicadas: 1
# ── NAs ─────────────────────────────────────────────────────────────────────
cat("\nNAs por columna:\n")
NAs por columna:
na_conteo <- colSums(is.na(ventas_crudas))
for (col in names(na_conteo)) {
if (na_conteo[col] > 0)
cat(" •", col, "→", na_conteo[col], "NA(s)\n")
} • vendedor → 1 NA(s)
• monto → 1 NA(s)
• completada → 1 NA(s)
# ── Inconsistencias de texto ─────────────────────────────────────────────────
cat("\nValores únicos en 'vendedor' (antes de limpiar):\n")
Valores únicos en 'vendedor' (antes de limpiar):
cat(" ", paste(unique(ventas_crudas$vendedor), collapse = " | "), "\n") ana | PEDRO | María | NA | carmen | luis
cat("\nValores únicos en 'region' (antes de limpiar):\n")
Valores únicos en 'region' (antes de limpiar):
cat(" ", paste(unique(ventas_crudas$region), collapse = " | "), "\n") norte | SUR | Norte | sur | NORTE
cat("\nValores únicos en 'completada' (antes de limpiar):\n")
Valores únicos en 'completada' (antes de limpiar):
cat(" ", paste(unique(ventas_crudas$completada), collapse = " | "), "\n") SI | NO | NA
# ── Outliers (IQR) ───────────────────────────────────────────────────────────
q1 <- quantile(ventas_crudas$monto, 0.25, na.rm = TRUE)
q3 <- quantile(ventas_crudas$monto, 0.75, na.rm = TRUE)
iqr <- q3 - q1
lim_sup <- q3 + 1.5 * iqr
cat("\nDetección de outliers en 'monto' (método IQR):\n")
Detección de outliers en 'monto' (método IQR):
cat(" Q1 = RD$", format(q1, big.mark = ","), "\n") Q1 = RD$ 17,750
cat(" Q3 = RD$", format(q3, big.mark = ","), "\n") Q3 = RD$ 21,750
cat(" IQR = RD$", format(iqr, big.mark = ","), "\n") IQR = RD$ 4,000
cat(" Límite sup = RD$", format(lim_sup, big.mark = ","), "\n") Límite sup = RD$ 27,750
cat(" Outliers detectados:",
sum(ventas_crudas$monto > lim_sup, na.rm = TRUE), "\n") Outliers detectados: 2
cat(" Valor(es) outlier:",
ventas_crudas$monto[ventas_crudas$monto > lim_sup], "\n") Valor(es) outlier: NA 31000 9500000
Resumen del diagnóstico:
- 1 fila duplicada:
id_venta = 3aparece dos veces (filas 3 y 11).
- NAs:
vendedortiene 1 NA (fila 4),montotiene 1 NA (fila 4),completadatiene 1 NA (fila 5).
- Inconsistencias de texto:
vendedormezcla minúsculas, mayúsculas y espacios al final.regiontiene el mismo problema.
- Outlier: el valor
9,500,000enmonto(fila 8) supera el límite superior del IQR ampliamente; los demás valores rondan entre 15,000 y 31,000.
3 Parte 2 — Limpieza
# ── Paso 1: Calcular mediana del mes 3 (para imputar monto NA en mes 2) ──────
# El NA de monto está en la fila con mes = 2
mediana_mes2 <- ventas_crudas |>
filter(mes == 2, !is.na(monto)) |>
pull(monto) |>
median()
cat("Mediana de monto en mes 2 (para imputación):", mediana_mes2, "\n")Mediana de monto en mes 2 (para imputación): 23500
# ── Paso 2: Pipeline de limpieza completo ────────────────────────────────────
ventas_limpias <- ventas_crudas |>
# 1. Eliminar duplicados — conserva la primera ocurrencia de cada id_venta
distinct(id_venta, .keep_all = TRUE) |>
# 2. Limpiar texto: trim de espacios + estandarizar capitalización
mutate(
vendedor = str_trim(vendedor), # quita espacios al inicio/fin
vendedor = str_to_title(vendedor), # Title Case: "Ana", "Pedro"…
vendedor = replace_na(vendedor, "Desconocido"), # imputa NA con "Desconocido"
region = str_trim(region),
region = str_to_lower(region), # lower case: "norte", "sur"
) |>
# 3. Imputar NA de monto con mediana del mes correspondiente
mutate(
monto = if_else(is.na(monto), mediana_mes2, monto)
) |>
# 4. Convertir 'completada' a factor, reemplazando NA con "NO"
mutate(
completada = replace_na(completada, "NO"),
completada = factor(completada, levels = c("NO", "SI"))
) |>
# 5. Marcar outliers usando IQR sobre el dataset ya sin duplicados
mutate(
q1_m = quantile(monto, 0.25),
q3_m = quantile(monto, 0.75),
iqr_m = q3_m - q1_m,
es_outlier = monto > (q3_m + 1.5 * iqr_m) | monto < (q1_m - 1.5 * iqr_m)
) |>
select(-q1_m, -q3_m, -iqr_m) # elimina columnas auxiliares del IQR
cat("\n=== Dataset limpio ===\n")
=== Dataset limpio ===
print(ventas_limpias)# A tibble: 10 × 7
id_venta vendedor region monto mes completada es_outlier
<dbl> <chr> <chr> <dbl> <dbl> <fct> <lgl>
1 1 Ana norte 15000 1 SI FALSE
2 2 Pedro sur 22000 1 SI FALSE
3 3 María norte 18500 1 NO FALSE
4 4 Desconocido sur 23500 2 SI FALSE
5 5 Carmen norte 31000 2 NO FALSE
6 6 Ana norte 16000 2 SI FALSE
7 7 Pedro sur 19500 3 NO FALSE
8 8 Luis norte 9500000 3 SI TRUE
9 9 Carmen sur 21000 3 SI FALSE
10 10 Luis norte 17500 3 NO FALSE
3.1 Verificación post-limpieza
cat("Filas originales: ", nrow(ventas_crudas), "\n")Filas originales: 11
cat("Filas limpias: ", nrow(ventas_limpias), "\n")Filas limpias: 10
cat("Duplicados restantes:", sum(duplicated(ventas_limpias$id_venta)), "\n")Duplicados restantes: 0
cat("NAs restantes: ", sum(is.na(ventas_limpias)), "\n")NAs restantes: 0
cat("\nNiveles de 'completada':", levels(ventas_limpias$completada), "\n")
Niveles de 'completada': NO SI
cat("Outliers marcados: ", sum(ventas_limpias$es_outlier), "\n")Outliers marcados: 1
cat("\nValores únicos 'vendedor' (limpio):\n")
Valores únicos 'vendedor' (limpio):
cat(" ", paste(sort(unique(ventas_limpias$vendedor)), collapse = " | "), "\n") Ana | Carmen | Desconocido | Luis | María | Pedro
cat("\nValores únicos 'region' (limpio):\n")
Valores únicos 'region' (limpio):
cat(" ", paste(sort(unique(ventas_limpias$region)), collapse = " | "), "\n") norte | sur
4 Parte 3 — Transformación con dplyr
ventas_transformadas <- ventas_limpias |>
# 1. Columna 'comision': 5% del monto si completada == "SI", 0 si no
mutate(
comision = if_else(completada == "SI", monto * 0.05, 0)
) |>
# 2. Columna 'categoria_venta' con case_when()
mutate(
categoria_venta = case_when(
monto < 15000 ~ "Baja",
monto <= 25000 ~ "Media",
TRUE ~ "Alta"
),
categoria_venta = factor(categoria_venta,
levels = c("Baja", "Media", "Alta"),
ordered = TRUE)
) |>
# 3. Ranking por monto dentro de cada región (mayor monto = rank 1)
group_by(region) |>
mutate(
ranking_region = rank(-monto, ties.method = "min")
) |>
ungroup()
cat("=== Dataset transformado ===\n")=== Dataset transformado ===
print(ventas_transformadas)# A tibble: 10 × 10
id_venta vendedor region monto mes completada es_outlier comision
<dbl> <chr> <chr> <dbl> <dbl> <fct> <lgl> <dbl>
1 1 Ana norte 15000 1 SI FALSE 750
2 2 Pedro sur 22000 1 SI FALSE 1100
3 3 María norte 18500 1 NO FALSE 0
4 4 Desconocido sur 23500 2 SI FALSE 1175
5 5 Carmen norte 31000 2 NO FALSE 0
6 6 Ana norte 16000 2 SI FALSE 800
7 7 Pedro sur 19500 3 NO FALSE 0
8 8 Luis norte 9500000 3 SI TRUE 475000
9 9 Carmen sur 21000 3 SI FALSE 1050
10 10 Luis norte 17500 3 NO FALSE 0
# ℹ 2 more variables: categoria_venta <ord>, ranking_region <int>
4.1 Vista de las columnas nuevas
ventas_transformadas |>
select(id_venta, vendedor, region, monto, completada,
comision, categoria_venta, ranking_region) |>
arrange(region, ranking_region) |>
print()# A tibble: 10 × 8
id_venta vendedor region monto completada comision categoria_venta
<dbl> <chr> <chr> <dbl> <fct> <dbl> <ord>
1 8 Luis norte 9500000 SI 475000 Alta
2 5 Carmen norte 31000 NO 0 Alta
3 3 María norte 18500 NO 0 Media
4 10 Luis norte 17500 NO 0 Media
5 6 Ana norte 16000 SI 800 Media
6 1 Ana norte 15000 SI 750 Media
7 4 Desconocido sur 23500 SI 1175 Media
8 2 Pedro sur 22000 SI 1100 Media
9 9 Carmen sur 21000 SI 1050 Media
10 7 Pedro sur 19500 NO 0 Media
# ℹ 1 more variable: ranking_region <int>
5 Parte 4 — Resumen Analítico por Vendedor
resumen_vendedor <- ventas_transformadas |>
# Excluir outlier del resumen de montos para no distorsionar promedios
# (se indica con nota; se puede incluir si el instructor lo prefiere)
group_by(vendedor) |>
summarise(
ventas_completadas = sum(completada == "SI"),
monto_total = sum(monto),
monto_promedio = round(mean(monto), 0),
comision_total = sum(comision),
n_ventas = n(),
.groups = "drop"
) |>
arrange(desc(monto_total))
cat("=== Resumen analítico por vendedor ===\n")=== Resumen analítico por vendedor ===
print(resumen_vendedor)# A tibble: 6 × 6
vendedor ventas_completadas monto_total monto_promedio comision_total n_ventas
<chr> <int> <dbl> <dbl> <dbl> <int>
1 Luis 1 9517500 4758750 475000 2
2 Carmen 1 52000 26000 1050 2
3 Pedro 1 41500 20750 1100 2
4 Ana 2 31000 15500 1550 2
5 Descono… 1 23500 23500 1175 1
6 María 0 18500 18500 0 1
5.1 Resumen incluyendo categoría más frecuente
resumen_extendido <- ventas_transformadas |>
group_by(vendedor) |>
summarise(
n_ventas = n(),
ventas_completadas = sum(completada == "SI"),
pct_completadas = round(mean(completada == "SI") * 100, 1),
monto_total = sum(monto),
monto_promedio = round(mean(monto), 0),
comision_total = sum(comision),
categoria_frecuente = names(sort(table(categoria_venta), decreasing = TRUE))[1],
.groups = "drop"
) |>
arrange(desc(monto_total))
cat("=== Resumen extendido por vendedor ===\n")=== Resumen extendido por vendedor ===
print(resumen_extendido)# A tibble: 6 × 8
vendedor n_ventas ventas_completadas pct_completadas monto_total
<chr> <int> <int> <dbl> <dbl>
1 Luis 2 1 50 9517500
2 Carmen 2 1 50 52000
3 Pedro 2 1 50 41500
4 Ana 2 2 100 31000
5 Desconocido 1 1 100 23500
6 María 1 0 0 18500
# ℹ 3 more variables: monto_promedio <dbl>, comision_total <dbl>,
# categoria_frecuente <chr>
5.2 Resumen por región
cat("=== Resumen por región ===\n")=== Resumen por región ===
ventas_transformadas |>
group_by(region) |>
summarise(
n_ventas = n(),
monto_total = sum(monto),
monto_promedio = round(mean(monto), 0),
comision_total = sum(comision),
pct_completadas = round(mean(completada == "SI") * 100, 1),
.groups = "drop"
) |>
arrange(desc(monto_total)) |>
print()# A tibble: 2 × 6
region n_ventas monto_total monto_promedio comision_total pct_completadas
<chr> <int> <dbl> <dbl> <dbl> <dbl>
1 norte 6 9598000 1599667 476550 50
2 sur 4 86000 21500 3325 75
5.3 Distribución por categoría de venta
cat("=== Distribución por categoría de venta ===\n")=== Distribución por categoría de venta ===
ventas_transformadas |>
count(categoria_venta, completada) |>
arrange(categoria_venta, completada) |>
print()# A tibble: 4 × 3
categoria_venta completada n
<ord> <fct> <int>
1 Media NO 3
2 Media SI 5
3 Alta NO 1
4 Alta SI 1
Sesión de R:
R version: 4.6.0
tidyverse: 2.0.0
Fecha: 2026-06-05
Práctica Semana 06 — Introducción al Lenguaje de Programación y Estadística R — ITLA T2 2026