Code
library(tidyverse)
library(stringr)
library(dplyr)
library(kableExtra)🎯 Objetivo: Limpiar completamente un dataset con problemas reales y construir un resumen analítico usando dplyr.
library(tidyverse)
library(stringr)
library(dplyr)
library(kableExtra)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")
)| 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 |
# 1.1 - Usar glimpse() para para entender el estado 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", "…
# 1.2 - Usar summary() para para entender el estado del dataset
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
# 1.3 - Usar colSums(is.na()) para para entender el estado del dataset
colSums(is.na(ventas_crudas)) # <- Para saber donde hay NA id_venta vendedor region monto mes completada
0 1 0 1 0 1
¿Cuántos duplicados hay? Hay un duplicado en id_venta, el 3 esta 2 veces.
¿Cuántos NAs por columna? Las columnas “vendedor”, “monto” y “completada” tienen 1 NA.
¿Hay outliers? Si, en la columna “monto” el valor “9500000” esta fuera del rango de los demas valores.
ventas_limpias <- ventas_crudas |>
# 2.1 - Quitar duplicados por id_venta:
distinct(id_venta, .keep_all = TRUE) |>
# 2.2 y 2.4 - Limpiar vendedor y region / Imputar el NA de vendedor con "Desconocido"
mutate(
vendedor = str_trim(str_to_title(vendedor)) |>
replace_na("Desconocido"),
region = str_to_lower(str_trim(region))
) |>
# 2.3 - Imputar el NA de monto con la mediana del mes correspondiente:
group_by(mes) |>
mutate(monto = if_else(is.na(monto), median(monto, na.rm = TRUE), monto)) |>
ungroup() |>
# 2.5 - Convertir completada a factor con replace_na() aplicando "NO" a los NAs
mutate(
completada = replace_na(completada,"NO"),
completada = factor(completada, levels = c("SI","NO"))
)
# 2.6 - Marcar el outlier en monto con una columna booleana es_outlier usando IQR
Q1 <- quantile(ventas_limpias$monto, 0.25, na.rm = TRUE)
Q3 <- quantile(ventas_limpias$monto, 0.75, na.rm = TRUE)
iqr <- IQR(ventas_limpias$monto, na.rm = TRUE)
limite_inferior <- Q1 - 1.5 * iqr
limite_superior <- Q3 + 1.5 * iqr #si se pasa de alguno de los dos, es outlier.
ventas_limpias <- ventas_limpias |>
mutate(es_outlier = monto < limite_inferior | monto > limite_superior) # <- Columna booleana, nota "|" es el operador logico or. | 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 |
# 3.1 - Crear columna comision = 5% del monto si completada == "SI", 0 si no
ventas_limpias <- ventas_limpias |>
mutate(
comision = if_else (completada == 'SI', monto * 0.05, 0)
)# 3.2 - Crear columna categoria_venta con case_when(): Baja (<15000), Media (15000-25000), Alta (>25000)
ventas_limpias <- ventas_limpias |>
mutate(
categoria_venta = case_when(
monto < 15000 ~ "Baja",
monto >= 15000 & monto < 25000 ~ "Media",
monto >= 25000 ~ "Alta")
)# 3.3 - Calcular ranking de vendedor por monto usando rank(desc(monto)) dentro de cada region
ventas_limpias <- ventas_limpias |>
group_by(region) |>
mutate(
ranking = rank(desc(monto))
) |>
ungroup()| id_venta | vendedor | region | monto | mes | completada | es_outlier | comision | categoria_venta | ranking |
|---|---|---|---|---|---|---|---|---|---|
| 1 | Ana | norte | 15000 | 1 | SI | FALSE | 750 | Media | 6 |
| 2 | Pedro | sur | 22000 | 1 | SI | FALSE | 1100 | Media | 2 |
| 3 | María | norte | 18500 | 1 | NO | FALSE | 0 | Media | 3 |
| 4 | Desconocido | sur | 23500 | 2 | SI | FALSE | 1175 | Media | 1 |
| 5 | Carmen | norte | 31000 | 2 | NO | FALSE | 0 | Alta | 2 |
| 6 | Ana | norte | 16000 | 2 | SI | FALSE | 800 | Media | 5 |
| 7 | Pedro | sur | 19500 | 3 | NO | FALSE | 0 | Media | 4 |
| 8 | Luis | norte | 9500000 | 3 | SI | TRUE | 475000 | Alta | 1 |
| 9 | Carmen | sur | 21000 | 3 | SI | FALSE | 1050 | Media | 3 |
| 10 | Luis | norte | 17500 | 3 | NO | FALSE | 0 | Media | 4 |
# 4.1 - Usando `group_by()` + `summarise()` construye una tabla que muestre por `vendedor`:
#- Total de ventas completadas
#- Monto total vendido
#- Monto promedio por venta
#- Comisión total ganada
#- Ordenada de mayor a menor monto total
resumen <- ventas_limpias |>
group_by(vendedor) |>
summarise(
total_ventas_completadas = sum (completada == 'SI'),
monto_total = sum(monto),
monto_promedio = mean(monto),
comision_total = sum(comision)
) |>
arrange(desc(monto_total))| vendedor | total_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 |