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")
)
ventas_crudas| id_venta | vendedor | region | monto | mes | completada |
|---|---|---|---|---|---|
| 1 | ana | norte | 15000 | 1 | SI |
| 2 | PEDRO | SUR | 22000 | 1 | SI |
| 3 | María | Norte | 18500 | 1 | NO |
| 4 | NA | sur | NA | 2 | SI |
| 5 | carmen | NORTE | 31000 | 2 | NA |
| 6 | ana | norte | 16000 | 2 | SI |
| 7 | PEDRO | sur | 19500 | 3 | NO |
| 8 | luis | Norte | 9500000 | 3 | SI |
| 9 | carmen | sur | 21000 | 3 | SI |
| 10 | luis | norte | 17500 | 3 | NO |
| 3 | María | Norte | 18500 | 1 | NO |
## 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", "…
## 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
##
## id_venta vendedor region monto mes completada
## 0 1 0 1 0 1
Tres columnas tienen valores ausentes: vendedor (1),
monto (1) y completada (1).
| id_venta | n |
|---|---|
| 3 | 2 |
id_venta = 3 aparece dos veces. Los valores en ambas
filas son idénticos, por lo que se conserva la primera ocurrencia.
| id_venta | monto |
|---|---|
| 8 | 9500000 |
| 5 | 31000 |
| 2 | 22000 |
| 9 | 21000 |
| 7 | 19500 |
| 3 | 18500 |
| 3 | 18500 |
| 10 | 17500 |
| 6 | 16000 |
| 1 | 15000 |
| 4 | NA |
id_venta = 8 tiene un monto de 9,500,000. El resto de
los registros oscila entre 15,000 y 31,000. La diferencia es de
aproximadamente 300 veces la mediana del resto, lo que confirma un
outlier.
La mediana por mes se calcula sobre ventas_crudas antes
de cualquier filtro, para preservar la información disponible en los
datos originales.
medianas_por_mes <- ventas_crudas |>
group_by(mes) |>
summarise(mediana_mes = median(monto, na.rm = TRUE))
ventas_limpias <- ventas_crudas |>
# Eliminar duplicados: conservar primera ocurrencia de cada id_venta
distinct(id_venta, .keep_all = TRUE) |>
# Limpiar vendedor: trim de espacios + title case
mutate(vendedor = str_to_title(str_trim(vendedor))) |>
# Imputar NA de vendedor
mutate(vendedor = replace_na(vendedor, "Desconocido")) |>
# Limpiar region: trim + lower case consistente
mutate(region = str_to_lower(str_trim(region))) |>
# Unir medianas para imputar monto
left_join(medianas_por_mes, by = "mes") |>
# Imputar NA de monto con la mediana del mes correspondiente
mutate(monto = if_else(is.na(monto), mediana_mes, monto)) |>
# Eliminar columna auxiliar
select(-mediana_mes) |>
# Convertir completada a factor; NAs → "NO"
mutate(
completada = replace_na(completada, "NO"),
completada = factor(completada, levels = c("SI", "NO"))
) |>
# Marcar outlier con criterio IQR
mutate(
q1 = quantile(monto, 0.25),
q3 = quantile(monto, 0.75),
iqr = q3 - q1,
es_outlier = monto < (q1 - 1.5 * iqr) | monto > (q3 + 1.5 * iqr)
) |>
select(-q1, -q3, -iqr)
glimpse(ventas_limpias)## Rows: 10
## Columns: 7
## $ id_venta <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
## $ vendedor <chr> "Ana", "Pedro", "María", "Desconocido", "Carmen", "Ana", "P…
## $ region <chr> "norte", "sur", "norte", "sur", "norte", "norte", "sur", "n…
## $ monto <dbl> 15000, 22000, 18500, 23500, 31000, 16000, 19500, 9500000, 2…
## $ mes <dbl> 1, 1, 1, 2, 2, 2, 3, 3, 3, 3
## $ completada <fct> SI, SI, NO, SI, NO, SI, NO, SI, SI, NO
## $ es_outlier <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, FALS…
| id_venta | vendedor | region | monto | mes | completada | es_outlier |
|---|---|---|---|---|---|---|
| 1 | Ana | norte | 15000 | 1 | SI | FALSE |
| 2 | Pedro | sur | 22000 | 1 | SI | FALSE |
| 3 | María | norte | 18500 | 1 | NO | FALSE |
| 4 | Desconocido | sur | 23500 | 2 | SI | FALSE |
| 5 | Carmen | norte | 31000 | 2 | NO | FALSE |
| 6 | Ana | norte | 16000 | 2 | SI | FALSE |
| 7 | Pedro | sur | 19500 | 3 | NO | FALSE |
| 8 | Luis | norte | 9500000 | 3 | SI | TRUE |
| 9 | Carmen | sur | 21000 | 3 | SI | FALSE |
| 10 | Luis | norte | 17500 | 3 | NO | FALSE |
ventas_transformadas <- ventas_limpias |>
# Comisión: 5% si la venta está completada, 0 si no
mutate(comision = if_else(completada == "SI", monto * 0.05, 0)) |>
# Categoría según monto
mutate(categoria_venta = case_when(
monto < 15000 ~ "Baja",
monto >= 15000 & monto <= 25000 ~ "Media",
monto > 25000 ~ "Alta"
)) |>
# Ranking por monto dentro de cada región
group_by(region) |>
mutate(ranking_region = rank(desc(monto), ties.method = "min")) |>
ungroup()ventas_transformadas |>
select(id_venta, vendedor, region, monto, completada,
comision, categoria_venta, ranking_region, es_outlier)| id_venta | vendedor | region | monto | completada | comision | categoria_venta | ranking_region | es_outlier |
|---|---|---|---|---|---|---|---|---|
| 1 | Ana | norte | 15000 | SI | 750 | Media | 6 | FALSE |
| 2 | Pedro | sur | 22000 | SI | 1100 | Media | 2 | FALSE |
| 3 | María | norte | 18500 | NO | 0 | Media | 3 | FALSE |
| 4 | Desconocido | sur | 23500 | SI | 1175 | Media | 1 | FALSE |
| 5 | Carmen | norte | 31000 | NO | 0 | Alta | 2 | FALSE |
| 6 | Ana | norte | 16000 | SI | 800 | Media | 5 | FALSE |
| 7 | Pedro | sur | 19500 | NO | 0 | Media | 4 | FALSE |
| 8 | Luis | norte | 9500000 | SI | 475000 | Alta | 1 | TRUE |
| 9 | Carmen | sur | 21000 | SI | 1050 | Media | 3 | FALSE |
| 10 | Luis | norte | 17500 | NO | 0 | Media | 4 | FALSE |
Decisiones de diseño:
if_else() sobre case_when() para
comision: la condición es binaria.ties.method = "min" en rank(): en caso de
empate, ambos vendedores reciben el rango menor.es_outlier lo
señala sin modificar el valor, para que cada análisis posterior decida
si incluirlo.resumen_vendedor <- ventas_transformadas |>
group_by(vendedor) |>
summarise(
ventas_completadas = sum(completada == "SI"),
monto_total = sum(monto),
monto_promedio = mean(monto),
comision_total = sum(comision)
) |>
arrange(desc(monto_total))
resumen_vendedor| vendedor | ventas_completadas | monto_total | monto_promedio | comision_total |
|---|---|---|---|---|
| Luis | 1 | 9517500 | 4758750 | 475000 |
| Carmen | 1 | 52000 | 26000 | 1050 |
| Pedro | 1 | 41500 | 20750 | 1100 |
| Ana | 2 | 31000 | 15500 | 1550 |
| Desconocido | 1 | 23500 | 23500 | 1175 |
| María | 0 | 18500 | 18500 | 0 |
resumen_vendedor |>
filter(vendedor != "Desconocido") |>
mutate(vendedor = fct_reorder(vendedor, monto_total)) |>
ggplot(aes(x = vendedor, y = monto_total, fill = comision_total)) +
geom_col(width = 0.6) +
coord_flip() +
scale_y_continuous(labels = scales::comma) +
scale_fill_gradient(low = "#c6dbef", high = "#2171b5", labels = scales::comma) +
labs(
title = "Monto total por vendedor",
x = NULL,
y = "Monto total",
fill = "Comisión total"
) +
theme_minimal(base_size = 12) +
theme(
panel.grid.major.y = element_blank(),
plot.title = element_text(face = "bold")
)Nota sobre el outlier. Luis tiene el monto total más alto por el registro de 9,500,000 (
id_venta = 8, marcado comoes_outlier = TRUE). Si se excluye ese registro, Carmen o Ana lideran el ranking. El gráfico usa todos los datos sin filtrar el outlier.