Clustering es una técnica de aprendizaje no supervisado que agrupa observaciones según su similitud.
En este caso lo usaremos para segmentar clientes según:
Frecuencia de compra
Ticket promedio
Esto nos ayudará a generar estrategias de marketing personalizadas.
# install.packages("tidyverse")
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.2.0 ✔ readr 2.1.6
## ✔ forcats 1.0.1 ✔ stringr 1.6.0
## ✔ ggplot2 4.0.2 ✔ tibble 3.3.1
## ✔ lubridate 1.9.5 ✔ tidyr 1.3.2
## ✔ purrr 1.2.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
# install.packages("cluster")
library(cluster)
# install.packages("factoextra")
library(factoextra)
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
# install.packages("lubridate")
library(lubridate)
library(readxl)
df <- read_excel("/Users/samanthagarcia/Desktop/supermarket.xlsx")
## Warning: Expecting numeric in A522063 / R522063C1: got 'A563185'
## Warning: Expecting numeric in A522064 / R522064C1: got 'A563186'
## Warning: Expecting numeric in A522065 / R522065C1: got 'A563187'
head(df)
## # A tibble: 6 × 8
## BillNo Itemname Quantity Date Time Price
## <dbl> <chr> <dbl> <dttm> <dttm> <dbl>
## 1 536365 WHITE HANGING H… 6 2010-12-01 00:00:00 1899-12-31 08:26:00 2.55
## 2 536365 WHITE METAL LAN… 6 2010-12-01 00:00:00 1899-12-31 08:26:00 3.39
## 3 536365 CREAM CUPID HEA… 8 2010-12-01 00:00:00 1899-12-31 08:26:00 2.75
## 4 536365 KNITTED UNION F… 6 2010-12-01 00:00:00 1899-12-31 08:26:00 3.39
## 5 536365 RED WOOLLY HOTT… 6 2010-12-01 00:00:00 1899-12-31 08:26:00 3.39
## 6 536365 SET 7 BABUSHKA … 2 2010-12-01 00:00:00 1899-12-31 08:26:00 7.65
## # ℹ 2 more variables: CustomerID <dbl>, Country <chr>
df$Total <- df$Quantity * df$Price
Vamos a calcular:
Frecuencia de compra por cliente
Ticket promedio por cliente
clientes <- df %>%
group_by(CustomerID) %>%
summarise(
Frecuencia = n_distinct(BillNo),
TicketPromedio = mean(Total)
)
head(clientes)
## # A tibble: 6 × 3
## CustomerID Frecuencia TicketPromedio
## <dbl> <int> <dbl>
## 1 12346 1 77184.
## 2 12347 7 23.7
## 3 12349 1 24.1
## 4 12350 1 19.7
## 5 12352 8 29.5
## 6 12353 1 22.2
summary(clientes)
## CustomerID Frecuencia TicketPromedio
## Min. :12346 Min. : 1.00 Min. : 0.00
## 1st Qu.:13831 1st Qu.: 1.00 1st Qu.: 12.24
## Median :15321 Median : 2.00 Median : 17.67
## Mean :15316 Mean : 5.04 Mean : 68.38
## 3rd Qu.:16790 3rd Qu.: 5.00 3rd Qu.: 24.69
## Max. :18287 Max. :3498.00 Max. :77183.60
## NA's :1
sd(clientes$Frecuencia)
## [1] 53.76148
sd(clientes$TicketPromedio)
## [1] 1474.699
clientes_scaled <- scale(clientes[,2:3])
fviz_nbclust(clientes_scaled, kmeans, method = "wss") +
labs(title="Método del Codo") +
theme_minimal()
set.seed(123)
k4 <- kmeans(clientes_scaled, centers = 4, nstart = 25)
clientes$Cluster <- as.factor(k4$cluster)
ggplot(clientes, aes(x=Frecuencia, y=TicketPromedio, color=Cluster)) +
geom_point(size=3, alpha=0.7) +
theme_minimal() +
labs(
title="Segmentación de Clientes",
x="Frecuencia de Compra",
y="Ticket Promedio"
)
clientes <- clientes %>%
mutate(
NombreCluster = case_when(
Cluster == 1 ~ "Clientes Premium",
Cluster == 2 ~ "Clientes Frecuentes",
Cluster == 3 ~ "Clientes Ocasionales",
Cluster == 4 ~ "Clientes Bajo Valor"
)
)
clientes %>%
group_by(NombreCluster) %>%
summarise(
Frecuencia_Promedio = mean(Frecuencia),
Ticket_Promedio = mean(TicketPromedio)
)
## # A tibble: 4 × 3
## NombreCluster Frecuencia_Promedio Ticket_Promedio
## <chr> <dbl> <dbl>
## 1 Clientes Bajo Valor 4.02 29.9
## 2 Clientes Frecuentes 1.5 66671.
## 3 Clientes Ocasionales 56.3 1911.
## 4 Clientes Premium 3498 12.8
Clientes Premium → Programas VIP y recompensas exclusivas
Clientes Frecuentes → Promociones por volumen
Clientes Ocasionales → Descuentos personalizados
Clientes Bajo Valor → Campañas de reactivación