Teoría

Agrupamiento o clustering es una técnica de aprendizaje automático no supervisado que agrupa datos en función de su similitud.

Algunos usos típicos de esta técnica son:

  • Segmentación de clientes
  • Detección de anormalidades
  • Categorización de documentos

Instalar paquetes y llamar librerías

# install.packages("cluster") #analisis de agrupamiento
library(cluster)
#install.packages("ggplot2") #graficar
library(ggplot2)
#install.packages("data.table") #manejo de muchos datos
library(data.table)
## 
## Attaching package: 'data.table'
## The following object is masked from 'package:base':
## 
##     %notin%
#install.packages("factoextra") #grafica de optimizacion del numero de clusters
library(factoextra)
## Welcome to factoextra!
## Want to learn more? See two factoextra-related books at https://www.datanovia.com/library/principal-component-methods
# install.packages("datasets")
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.2.1     ✔ readr     2.2.0
## ✔ forcats   1.0.1     ✔ stringr   1.6.0
## ✔ lubridate 1.9.5     ✔ tibble    3.3.1
## ✔ purrr     1.2.2     ✔ tidyr     1.3.2
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::between()     masks data.table::between()
## ✖ dplyr::filter()      masks stats::filter()
## ✖ dplyr::first()       masks data.table::first()
## ✖ lubridate::hour()    masks data.table::hour()
## ✖ lubridate::isoweek() masks data.table::isoweek()
## ✖ lubridate::isoyear() masks data.table::isoyear()
## ✖ dplyr::lag()         masks stats::lag()
## ✖ dplyr::last()        masks data.table::last()
## ✖ lubridate::mday()    masks data.table::mday()
## ✖ lubridate::minute()  masks data.table::minute()
## ✖ lubridate::month()   masks data.table::month()
## ✖ lubridate::quarter() masks data.table::quarter()
## ✖ lubridate::second()  masks data.table::second()
## ✖ purrr::transpose()   masks data.table::transpose()
## ✖ lubridate::wday()    masks data.table::wday()
## ✖ lubridate::week()    masks data.table::week()
## ✖ lubridate::yday()    masks data.table::yday()
## ✖ lubridate::year()    masks data.table::year()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors

Ejercicio 1: Puntos

Contexto

Agrupa los siguientes 8 puntos.

Obtener datos

df1 <- data.frame(x=c(2,2,8,5,7,6,1,4), y=c(10,5,4,8,5,4,2,9))

Entender datos

summary(df1)
##        x               y         
##  Min.   :1.000   Min.   : 2.000  
##  1st Qu.:2.000   1st Qu.: 4.000  
##  Median :4.500   Median : 5.000  
##  Mean   :4.375   Mean   : 5.875  
##  3rd Qu.:6.250   3rd Qu.: 8.250  
##  Max.   :8.000   Max.   :10.000
str(df1)
## 'data.frame':    8 obs. of  2 variables:
##  $ x: num  2 2 8 5 7 6 1 4
##  $ y: num  10 5 4 8 5 4 2 9
plot(df1)

Escalar datos

# ejemplo:
# datos_escalados <- scale(datos_originales)

Asignar numero de grupos

grupos1 <- 3

Agrupar los puntos

set.seed(123)
clusters1 <- kmeans(df1,grupos1)
clusters1
## K-means clustering with 3 clusters of sizes 2, 3, 3
## 
## Cluster means:
##          x        y
## 1 1.500000 3.500000
## 2 3.666667 9.000000
## 3 7.000000 4.333333
## 
## Clustering vector:
## [1] 2 1 3 2 3 3 1 2
## 
## Within cluster sum of squares by cluster:
## [1] 5.000000 6.666667 2.666667
##  (between_SS / total_SS =  85.8 %)
## 
## Available components:
## 
## [1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss"
## [6] "betweenss"    "size"         "iter"         "ifault"

Optimizar numero de grupos

set.seed(123)
optimizacion1 <- clusGap(df1, FUN=kmeans, nstart=1, K.max=7)
# El K.max normalmente es 10, en este ejercicio al ser 8 datos unicamente se dejó en 7.
plot(optimizacion1, xlab="Numero de clusters k", main="Optimización de Clusters")

# Se selecciona como óptico el primer punto más alto.

Graficar los grupos

fviz_cluster(clusters1,data=df1)

Agregar grupos a la base de datos

df1_clusters <- cbind(df1, cluster = clusters1$cluster)
head(df1_clusters)
##   x  y cluster
## 1 2 10       2
## 2 2  5       1
## 3 8  4       3
## 4 5  8       2
## 5 7  5       3
## 6 6  4       3

Conclusiones

La técnica de clustering permite identificar patrones o grupos naturales en los datos sin necesidad de etiquetas previas.

Ejercicio 2: USArrests

Contexto

La base de datos USArrests contiene estadisticas en arestos por cada 100,000 residentes por agresión, asesinato y violación en cada uno d elos 50 estados de EE.UU. en 1973.

Obtener datos

df2 <- USArrests
df2 <- df2 %>% select(-UrbanPop) 

Entender datos

summary(df2)
##      Murder          Assault           Rape      
##  Min.   : 0.800   Min.   : 45.0   Min.   : 7.30  
##  1st Qu.: 4.075   1st Qu.:109.0   1st Qu.:15.07  
##  Median : 7.250   Median :159.0   Median :20.10  
##  Mean   : 7.788   Mean   :170.8   Mean   :21.23  
##  3rd Qu.:11.250   3rd Qu.:249.0   3rd Qu.:26.18  
##  Max.   :17.400   Max.   :337.0   Max.   :46.00
str(df2)
## 'data.frame':    50 obs. of  3 variables:
##  $ Murder : num  13.2 10 8.1 8.8 9 7.9 3.3 5.9 15.4 17.4 ...
##  $ Assault: int  236 263 294 190 276 204 110 238 335 211 ...
##  $ Rape   : num  21.2 44.5 31 19.5 40.6 38.7 11.1 15.8 31.9 25.8 ...

Escalar datos

df2_escalados <- scale(df2)
summary(df2_escalados)
##      Murder           Assault             Rape        
##  Min.   :-1.6044   Min.   :-1.5090   Min.   :-1.4874  
##  1st Qu.:-0.8525   1st Qu.:-0.7411   1st Qu.:-0.6574  
##  Median :-0.1235   Median :-0.1411   Median :-0.1209  
##  Mean   : 0.0000   Mean   : 0.0000   Mean   : 0.0000  
##  3rd Qu.: 0.7949   3rd Qu.: 0.9388   3rd Qu.: 0.5277  
##  Max.   : 2.2069   Max.   : 1.9948   Max.   : 2.6444

Asignar numero de grupos

grupos2 <- 5

Agrupar los puntos

set.seed(123)
clusters2 <- kmeans(df2_escalados,grupos2)

Optimizar numero de grupos

set.seed(123)
optimizacion2 <- clusGap(df2_escalados, FUN=kmeans, nstart=1, K.max=10)
plot(optimizacion2, xlab="Numero de clusters k", main="Optimización de Clusters")

# Se selecciona como óptico el primer punto más alto.

Graficar los grupos

fviz_cluster(clusters2,data=df2_escalados)

# aggregate(df2, by=list(Grupo=clusters2))

Agregar grupos a la base de datos

df2_clusters <- cbind(df2, cluster = clusters2$cluster)
head(df2_clusters)
##            Murder Assault Rape cluster
## Alabama      13.2     236 21.2       5
## Alaska       10.0     263 44.5       4
## Arizona       8.1     294 31.0       1
## Arkansas      8.8     190 19.5       3
## California    9.0     276 40.6       4
## Colorado      7.9     204 38.7       4
df2_clusters %>% group_by(cluster) %>% summarise_all(mean) %>%
  mutate(indicador_inseguridad=Murder+Assault+Rape)
## # A tibble: 5 × 5
##   cluster Murder Assault  Rape indicador_inseguridad
##     <int>  <dbl>   <dbl> <dbl>                 <dbl>
## 1       1  11.4    282.   29.7                 323. 
## 2       2   3.08    80.9  11.8                  95.8
## 3       3   6.59   146.   20.1                 172. 
## 4       4   9.78   249.   42.4                 301. 
## 5       5  14.4    245    22.2                 282.
df2_clusters <- df2_clusters  %>%
  mutate(cluster= case_when(
    cluster == 1 ~ "Inseguridad Muy Alta",
    cluster == 4 ~ "Inseguridad Alta",
    cluster == 5 ~ "Inseguridad Media",
    cluster == 3 ~ "Inseguridad Baja",
    cluster == 2 ~ "Inseguridad Muy Baja"
  ))
head(df2_clusters)
##            Murder Assault Rape              cluster
## Alabama      13.2     236 21.2    Inseguridad Media
## Alaska       10.0     263 44.5     Inseguridad Alta
## Arizona       8.1     294 31.0 Inseguridad Muy Alta
## Arkansas      8.8     190 19.5     Inseguridad Baja
## California    9.0     276 40.6     Inseguridad Alta
## Colorado      7.9     204 38.7     Inseguridad Alta

Ejercicio 3. Segmentación de Clientes

Contexto

La base de datos ventas tiene los registros entre el 1 de diciembre de 2010 y el 9 de diciembre de 2011 de las ventas de una empresa minorista en línea sin tienda física, basada en Reino Unido.
La empresa vende principalmente regalos únicos para toda ocasión, y muchos de sus clientes son mayoristas.
Objetivo: segmentar clientes, asignarles nombre y caracteristicas de comportamiento y proponer sugerencias a la empresa para aumentar ventas.

Obtener Datos

# file.choose()
df3 <- read.csv("/Users/gabotejeda/Desktop/codigos R/ventas.csv")

Entender los Datos

summary(df3)
##        Ticket            Producto         Cantidad              Fecha       
##  Length   :522064   Length   :522064   Min.   :-9600.00   Length   :522064  
##  N.unique : 21663   N.unique :  4183   1st Qu.:    1.00   N.unique :   305  
##  N.blank  :     0   N.blank  :  1455   Median :    3.00   N.blank  :     0  
##  Min.nchar:     6   Min.nchar:     0   Mean   :   10.09   Min.nchar:    10  
##  Max.nchar:     7   Max.nchar:    36   3rd Qu.:   10.00   Max.nchar:    10  
##                                        Max.   :80995.00                     
##                                                                             
##         Hora            Precio              Cliente              País       
##  Length   :522064   Min.   :-11062.060   Min.   :12346    Length   :522064  
##  N.unique :   739   1st Qu.:     1.250   1st Qu.:13950    N.unique :    30  
##  N.blank  :     0   Median :     2.080   Median :15265    N.blank  :     0  
##  Min.nchar:     8   Mean   :     3.827   Mean   :15317    Min.nchar:     3  
##  Max.nchar:     8   3rd Qu.:     4.130   3rd Qu.:16837    Max.nchar:    20  
##                     Max.   : 13541.330   Max.   :18287                      
##                                          NAs    :134041
str(df3)
## 'data.frame':    522064 obs. of  8 variables:
##  $ Ticket  : chr  "536365" "536365" "536365" "536365" ...
##  $ Producto: chr  "WHITE HANGING HEART T-LIGHT HOLDER" "WHITE METAL LANTERN" "CREAM CUPID HEARTS COAT HANGER" "KNITTED UNION FLAG HOT WATER BOTTLE" ...
##  $ Cantidad: int  6 6 8 6 6 2 6 6 6 32 ...
##  $ Fecha   : chr  "01/12/2010" "01/12/2010" "01/12/2010" "01/12/2010" ...
##  $ Hora    : chr  "08:26:00" "08:26:00" "08:26:00" "08:26:00" ...
##  $ Precio  : num  2.55 3.39 2.75 3.39 3.39 7.65 4.25 1.85 1.85 1.69 ...
##  $ Cliente : int  17850 17850 17850 17850 17850 17850 17850 17850 17850 13047 ...
##  $ País    : chr  "United Kingdom" "United Kingdom" "United Kingdom" "United Kingdom" ...
sum(is.na(df3$Cliente))
## [1] 134041

Limpieza de datos

# Quitamos compras sin cliente identificado
df3 <- df3 %>% dplyr::filter(!is.na(Cliente))

# Quitamos cancelaciones/devoluciones (Cantidad o Precio negativos)
df3 <- df3 %>% dplyr::filter(Cantidad > 0, Precio > 0)

# Convertimos Fecha a formato fecha
df3$Fecha <- as.Date(df3$Fecha, format="%d/%m/%Y")

# Calculamos el monto de cada línea de compra
df3 <- df3 %>% dplyr::mutate(Monto = Cantidad * Precio)

Construir tabla RFM (Recencia, Frecuencia, Monetario) por cliente

fecha_referencia <- max(df3$Fecha) + 1

rfm <- df3 %>%
  dplyr::group_by(Cliente) %>%
  dplyr::summarise(
    Recencia = as.numeric(fecha_referencia - max(Fecha)),
    Frecuencia = dplyr::n_distinct(Ticket),
    Monetario = sum(Monto)
  )

summary(rfm)
##     Cliente         Recencia        Frecuencia        Monetario        
##  Min.   :12346   Min.   :  1.00   Min.   :  1.000   Min.   :     3.75  
##  1st Qu.:13832   1st Qu.: 18.00   1st Qu.:  1.000   1st Qu.:   306.72  
##  Median :15322   Median : 51.00   Median :  2.000   Median :   668.85  
##  Mean   :15316   Mean   : 93.16   Mean   :  4.227   Mean   :  1993.61  
##  3rd Qu.:16790   3rd Qu.:143.00   3rd Qu.:  5.000   3rd Qu.:  1652.79  
##  Max.   :18287   Max.   :374.00   Max.   :209.000   Max.   :280206.02
nrow(rfm)  
## [1] 4296

Escalar Datos

rfm_escalados <- scale(rfm %>% dplyr::select(Recencia, Frecuencia, Monetario))
summary(rfm_escalados)
##     Recencia         Frecuencia        Monetario       
##  Min.   :-0.9202   Min.   :-0.4556   Min.   :-0.23167  
##  1st Qu.:-0.7504   1st Qu.:-0.4556   1st Qu.:-0.19640  
##  Median :-0.4209   Median :-0.3144   Median :-0.15424  
##  Mean   : 0.0000   Mean   : 0.0000   Mean   : 0.00000  
##  3rd Qu.: 0.4977   3rd Qu.: 0.1092   3rd Qu.:-0.03968  
##  Max.   : 2.8042   Max.   :28.9132   Max.   :32.39138

Asignar numero de grupos

grupos3 <- 4

Agrupar los clientes

set.seed(123)
clusters3 <- kmeans(rfm_escalados, grupos3, nstart = 25)
clusters3
## K-means clustering with 4 clusters of sizes 1059, 11, 208, 3018
## 
## Cluster means:
##     Recencia  Frecuencia   Monetario
## 1  1.5532174 -0.37830233 -0.17729740
## 2 -0.8511775  9.89019046 14.52438662
## 3 -0.7719747  2.53867434  1.21945293
## 4 -0.4887089 -0.07826845 -0.07477022
## 
## Clustering vector:
##    [1] 3 4 4 1 4 1 1 1 4 4 4 4 1 4 4 4 1 4 1 4 1 4 4 4 4 1 4 1 4 4 4 1 4 4 4 4 1
##   [38] 1 4 4 4 1 4 1 2 4 4 4 4 4 1 4 1 4 1 4 3 4 4 4 4 3 4 1 4 4 4 4 1 4 4 1 4 4
##   [75] 4 4 4 4 4 4 4 4 4 4 4 1 3 4 4 3 4 4 4 4 4 4 4 4 4 4 1 4 4 4 1 4 4 4 4 1 4
##  [112] 4 1 4 4 1 4 4 4 1 1 4 4 4 4 4 1 4 4 4 4 4 4 4 4 4 4 4 4 4 4 3 4 4 4 4 1 1
##  [149] 4 1 4 4 4 4 4 1 4 1 4 1 1 4 4 3 4 4 1 1 4 4 4 4 1 4 3 4 1 4 4 1 1 4 4 4 4
##  [186] 4 4 4 4 1 4 4 4 4 4 4 4 4 4 1 4 4 4 4 4 4 3 1 1 4 1 4 4 4 4 4 4 4 1 4 4 4
##  [223] 4 4 4 4 4 1 4 4 1 1 1 4 4 4 4 4 4 4 1 4 1 4 1 4 4 4 3 4 4 3 3 3 4 4 1 4 4
##  [260] 1 4 4 4 4 4 4 4 4 4 4 1 4 3 4 4 4 4 4 4 1 4 4 3 4 4 4 4 1 4 4 4 4 3 1 1 1
##  [297] 1 1 1 4 4 4 4 4 2 4 4 4 4 1 1 4 3 4 4 4 4 1 4 1 1 4 4 4 4 4 4 4 1 4 4 1 1
##  [334] 1 1 4 4 1 4 1 4 1 1 4 4 1 4 1 1 4 1 4 4 4 4 4 4 1 4 1 4 1 1 4 1 4 3 4 3 4
##  [371] 4 4 1 4 4 4 1 4 4 1 4 1 4 4 4 1 4 1 4 1 1 4 4 4 1 4 1 4 4 4 4 4 1 4 1 4 4
##  [408] 1 3 1 4 4 1 4 4 4 4 1 4 4 4 4 3 1 4 4 4 4 1 4 3 4 4 4 4 4 4 4 4 4 1 4 4 4
##  [445] 4 4 4 4 4 1 4 4 4 4 4 1 4 4 3 1 1 1 4 4 1 4 4 4 1 3 4 4 4 4 4 4 1 4 3 1 4
##  [482] 3 1 4 1 1 4 4 4 4 4 4 3 4 4 4 4 4 4 4 4 1 4 4 4 4 4 1 1 4 4 4 4 4 3 1 4 4
##  [519] 1 1 4 1 4 1 4 4 4 3 1 1 4 4 3 4 1 3 4 4 1 2 3 4 4 1 4 4 4 3 4 1 3 4 4 4 4
##  [556] 1 4 4 3 4 4 4 4 1 1 4 4 4 4 4 4 1 1 4 1 4 4 4 4 4 4 4 1 4 4 4 4 4 1 4 4 4
##  [593] 4 4 4 1 4 4 1 4 4 4 4 4 4 4 4 4 4 4 4 4 1 4 1 4 4 4 4 4 4 4 3 4 4 4 4 4 4
##  [630] 4 4 4 1 4 4 4 1 1 4 4 1 1 4 1 4 4 4 1 4 4 1 4 4 1 4 4 4 4 4 4 1 4 4 4 4 1
##  [667] 1 3 4 4 3 4 3 1 4 4 4 4 4 4 4 4 4 4 1 4 1 4 1 1 4 4 4 1 1 4 1 1 4 4 4 4 4
##  [704] 4 4 4 4 4 4 3 4 4 4 4 4 4 4 1 4 4 4 4 4 1 1 4 1 1 4 1 4 4 4 4 4 4 4 4 4 1
##  [741] 1 4 4 4 4 4 4 1 1 1 4 4 4 4 4 4 1 4 1 4 4 4 1 4 1 4 4 1 4 4 4 4 3 4 4 4 4
##  [778] 4 3 4 1 4 4 4 4 4 4 4 4 4 4 4 4 4 1 4 4 4 4 4 4 1 1 4 4 3 4 4 1 4 1 4 3 4
##  [815] 4 4 1 1 1 4 1 4 1 4 1 1 4 4 3 4 4 4 1 1 4 4 1 4 4 1 4 4 4 4 1 4 1 4 4 1 4
##  [852] 4 4 4 4 4 4 4 4 4 4 4 4 4 4 1 4 1 3 4 4 4 4 4 4 4 1 4 4 4 4 4 1 4 4 4 4 4
##  [889] 1 4 1 4 1 4 4 3 4 4 1 1 1 4 4 4 4 4 4 4 4 4 4 3 1 4 4 4 4 4 4 4 4 1 4 1 4
##  [926] 4 1 4 4 4 4 4 4 4 4 4 4 4 1 4 4 4 4 1 4 4 4 4 4 1 4 4 1 4 1 4 1 3 4 4 1 4
##  [963] 1 1 4 4 1 1 4 1 4 4 4 3 3 4 1 4 1 4 4 1 1 1 3 4 1 1 1 1 4 1 4 4 4 4 1 4 4
## [1000] 4 4 4 1 4 4 4 1 4 1 4 4 4 4 1 1 4 4 1 4 1 1 4 3 4 4 4 4 1 4 4 3 4 4 4 4 3
## [1037] 4 4 1 4 4 1 4 4 4 4 3 4 1 4 4 1 4 4 4 1 4 4 1 4 4 4 4 4 4 1 1 4 4 4 1 4 4
## [1074] 4 4 1 4 1 4 1 4 4 4 4 4 4 4 4 4 3 1 4 1 4 4 1 4 4 4 4 4 4 4 4 1 4 4 4 4 4
## [1111] 3 4 4 4 1 4 1 4 4 4 4 1 4 4 4 1 4 1 4 4 4 4 4 4 4 1 4 4 4 4 4 4 1 4 4 1 4
## [1148] 1 1 1 4 4 4 4 4 4 1 4 4 1 1 4 4 1 4 4 4 4 1 1 3 4 4 1 4 4 1 4 4 4 4 4 4 3
## [1185] 4 4 4 1 4 4 4 4 4 4 1 4 4 4 4 4 1 1 4 4 4 4 4 1 1 4 4 4 4 4 3 4 4 4 1 1 4
## [1222] 4 4 1 4 4 1 4 1 4 4 3 4 4 4 4 3 3 1 3 3 4 1 4 1 4 4 4 4 4 4 4 4 1 1 4 3 4
## [1259] 3 4 1 3 4 3 4 4 4 4 4 1 1 4 4 4 4 4 4 4 1 4 1 4 4 4 4 4 4 4 1 4 4 3 4 4 4
## [1296] 4 1 4 4 4 4 1 1 4 4 1 1 4 4 4 1 1 4 4 1 4 4 4 4 4 1 4 4 4 3 4 1 4 3 4 4 3
## [1333] 1 4 4 1 4 4 4 1 4 4 4 4 4 4 4 4 3 4 4 1 4 1 4 4 4 4 4 1 4 4 4 4 4 1 1 4 4
## [1370] 1 1 4 1 4 1 1 4 4 1 4 4 4 4 4 1 4 1 1 1 4 4 4 4 1 3 4 4 4 4 1 4 4 4 4 4 4
## [1407] 4 3 4 4 4 1 4 4 4 4 1 4 4 4 4 1 4 4 1 1 4 4 4 4 4 4 4 1 1 1 4 4 4 4 4 1 4
## [1444] 1 1 1 1 4 1 4 4 4 4 4 4 3 1 1 1 1 1 4 4 4 4 4 1 4 4 4 4 1 4 4 3 1 4 4 4 4
## [1481] 4 4 4 4 1 4 1 4 4 4 4 4 4 4 4 1 4 4 4 4 1 4 4 4 1 1 1 4 4 4 4 4 4 4 1 4 4
## [1518] 1 4 1 1 4 1 4 4 1 4 4 4 4 4 4 4 1 4 1 4 1 4 1 4 4 4 4 1 1 4 4 4 1 1 4 4 4
## [1555] 1 4 4 4 4 4 4 4 4 1 4 4 4 4 4 4 4 4 4 4 3 4 4 4 1 4 3 4 1 4 4 4 4 4 1 3 4
## [1592] 4 4 1 1 4 4 1 4 4 4 4 3 4 3 4 4 4 1 4 1 1 4 4 4 4 4 1 4 1 4 1 4 4 4 4 4 4
## [1629] 4 1 4 1 3 3 4 4 1 4 1 1 4 1 4 4 1 4 4 4 4 1 1 4 4 4 4 4 4 1 4 1 2 1 4 4 4
## [1666] 4 4 1 4 4 4 4 4 4 4 4 4 3 1 1 1 4 4 4 3 4 1 4 4 3 1 4 4 4 1 4 1 4 1 4 4 3
## [1703] 4 4 1 4 4 4 4 4 4 4 4 4 4 4 1 4 4 1 1 4 4 4 3 3 4 4 4 1 4 4 4 1 4 4 4 4 4
## [1740] 4 4 4 4 1 4 1 4 4 4 4 4 1 4 4 4 4 1 4 1 4 4 4 4 4 4 4 4 1 3 4 4 4 4 1 4 4
## [1777] 4 4 4 1 4 1 1 4 4 4 1 1 4 4 1 4 4 4 4 4 1 4 4 4 1 3 1 4 4 4 4 4 4 4 4 4 4
## [1814] 4 4 4 3 1 4 4 1 4 1 4 4 4 4 4 4 4 1 4 1 1 1 1 4 4 3 1 4 4 4 4 4 4 1 4 4 4
## [1851] 4 4 4 4 4 1 4 4 1 4 4 4 4 4 4 4 4 4 4 1 1 4 4 4 1 4 1 4 4 4 4 4 4 4 4 4 4
## [1888] 4 4 1 4 1 4 1 4 4 1 4 4 1 3 4 1 4 4 4 4 1 4 4 1 4 4 4 4 4 4 4 4 4 4 1 4 4
## [1925] 4 4 4 3 1 1 4 3 1 3 1 4 1 4 4 1 4 4 1 4 4 4 3 4 1 4 4 4 4 1 1 4 4 4 4 1 3
## [1962] 1 4 1 1 4 4 1 4 1 4 4 4 4 3 4 1 4 4 1 4 4 1 1 4 4 4 4 4 4 4 4 1 4 4 4 4 4
## [1999] 3 4 4 4 1 4 4 4 1 4 1 4 4 1 3 4 1 1 4 4 4 4 4 4 4 4 4 3 1 4 4 1 1 4 1 4 1
## [2036] 4 1 4 1 4 1 4 4 4 3 3 4 4 3 4 4 4 4 4 4 1 4 4 4 1 4 1 1 4 4 4 4 1 4 1 1 1
## [2073] 1 1 1 4 4 1 4 1 1 1 4 4 4 4 4 4 4 4 1 4 4 4 4 4 4 4 1 1 1 4 4 1 4 4 1 1 4
## [2110] 4 4 3 4 4 4 4 4 1 1 4 4 4 4 4 3 3 1 4 4 4 4 4 4 1 4 4 4 1 2 4 4 1 4 1 4 1
## [2147] 4 4 4 4 1 4 4 4 1 1 1 4 4 4 4 4 4 4 1 1 4 1 1 4 1 4 4 3 4 1 1 4 4 4 4 4 4
## [2184] 4 4 4 4 1 4 1 4 1 4 1 1 4 4 4 1 4 1 4 4 4 1 1 4 4 1 4 4 1 4 1 4 4 4 4 1 4
## [2221] 1 4 4 4 4 4 1 4 4 4 1 4 4 1 4 4 1 1 1 4 4 4 4 1 1 1 4 1 4 4 1 3 4 4 4 4 4
## [2258] 4 4 4 4 4 3 1 4 4 4 4 4 4 4 4 4 3 4 3 1 4 4 4 1 1 1 4 1 3 4 4 1 4 4 4 4 4
## [2295] 4 4 4 4 1 4 4 4 4 4 1 1 4 4 4 1 4 4 3 4 4 4 1 4 1 3 4 4 4 1 4 4 4 4 4 4 4
## [2332] 4 4 1 4 1 1 4 4 4 4 1 4 1 4 4 4 4 4 4 4 3 4 4 4 4 4 1 4 1 4 4 4 3 4 4 4 4
## [2369] 4 4 4 4 4 4 4 4 4 4 4 4 3 4 1 1 4 1 1 1 4 4 4 4 1 4 4 1 4 4 1 4 4 1 4 1 4
## [2406] 1 4 4 4 4 4 4 4 4 4 4 4 4 4 4 1 4 3 1 4 4 1 4 1 4 1 4 4 4 1 4 4 4 4 1 4 4
## [2443] 4 4 1 1 4 4 1 4 1 1 4 4 3 4 4 4 4 4 4 4 4 3 4 4 1 4 4 4 4 4 1 4 4 4 4 4 3
## [2480] 4 4 1 4 1 1 4 4 4 4 4 4 1 4 4 4 1 4 4 4 4 4 1 4 4 3 4 4 1 4 4 4 4 1 4 4 4
## [2517] 1 4 4 1 1 4 4 4 4 4 1 4 4 4 4 3 4 4 4 1 4 4 4 4 1 4 4 4 3 4 4 4 4 4 4 4 4
## [2554] 4 3 1 4 4 4 4 1 1 1 4 4 4 4 1 1 1 1 1 4 1 4 4 4 4 4 4 1 4 1 4 1 1 4 1 1 1
## [2591] 1 1 4 4 4 1 4 4 3 1 4 1 1 4 4 4 4 4 4 4 3 4 4 4 4 4 4 1 4 1 4 4 1 4 4 4 4
## [2628] 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 1 4 4 4 1 3 4 3 4 4 4 4 4 4 4 1 4 4 4 4 4 2
## [2665] 1 4 3 1 4 1 4 4 4 4 4 4 1 4 1 4 4 1 1 4 4 4 4 1 1 4 4 4 1 1 4 1 4 4 4 4 1
## [2702] 4 4 1 4 4 4 1 1 4 4 4 4 4 4 1 4 4 4 1 4 4 1 1 4 1 1 4 4 4 4 4 1 1 4 4 4 4
## [2739] 3 4 1 4 4 4 4 1 4 1 4 1 4 1 4 4 1 4 4 4 1 4 3 1 1 4 3 4 4 4 4 4 4 4 4 4 4
## [2776] 4 4 4 4 4 4 4 3 4 4 4 4 1 4 4 4 1 4 4 4 4 4 4 4 3 4 4 4 1 4 1 1 4 4 1 4 4
## [2813] 1 1 4 1 4 4 4 4 4 4 4 4 4 1 4 1 4 4 1 4 4 1 1 4 4 4 4 4 4 1 4 4 1 1 1 4 4
## [2850] 4 1 4 4 1 4 4 4 1 4 4 4 4 1 4 4 4 4 1 4 4 4 1 1 4 4 1 4 4 4 4 4 4 3 4 1 1
## [2887] 4 4 4 4 1 4 4 4 1 4 1 4 3 4 1 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 1 1 4 4 4
## [2924] 4 4 4 1 4 1 4 4 4 1 4 4 4 1 1 4 1 4 4 4 4 4 4 4 4 4 4 4 3 4 1 1 4 1 4 4 1
## [2961] 4 4 4 4 4 4 4 4 4 2 4 1 1 1 4 4 4 4 1 3 4 1 4 4 4 4 4 4 4 1 1 4 4 4 4 4 4
## [2998] 4 1 1 4 4 4 4 1 4 4 4 1 1 4 4 4 4 4 1 1 4 4 4 4 1 4 4 1 3 3 1 4 4 1 4 4 4
## [3035] 4 4 4 4 1 1 4 4 4 1 4 4 4 4 4 3 4 4 4 1 4 1 4 4 4 4 4 4 1 4 1 4 1 1 4 4 3
## [3072] 4 4 4 4 4 1 4 4 4 4 4 4 1 1 4 4 1 4 1 4 1 4 4 1 4 1 3 4 4 4 4 1 4 4 4 4 4
## [3109] 1 1 1 4 4 4 4 4 4 3 4 1 1 4 1 4 4 4 4 4 4 3 4 4 4 1 4 4 4 3 4 4 4 4 4 1 4
## [3146] 4 1 1 4 4 3 4 4 4 4 4 4 3 1 4 1 4 1 4 4 1 4 4 1 4 1 4 3 4 4 4 4 4 1 1 4 4
## [3183] 4 3 3 4 4 4 4 1 4 1 4 1 1 4 4 4 4 1 4 1 1 4 4 1 4 4 4 4 4 4 4 4 3 4 1 4 4
## [3220] 4 4 4 4 1 4 4 1 4 4 1 4 4 1 4 4 4 4 1 4 1 4 3 4 4 4 4 1 4 4 4 4 1 4 1 4 1
## [3257] 4 4 4 1 3 4 4 3 4 4 4 1 1 4 4 4 4 1 4 4 4 4 4 4 4 4 4 4 4 4 4 4 1 4 4 4 1
## [3294] 4 4 1 4 1 4 4 4 4 4 3 4 4 4 4 4 4 1 4 4 4 1 4 4 4 3 4 1 4 3 4 4 3 4 4 4 4
## [3331] 4 4 4 1 3 1 4 4 4 4 4 4 4 1 4 1 4 4 4 1 1 4 1 4 4 4 1 4 4 1 4 4 4 4 4 4 4
## [3368] 4 1 4 4 1 1 4 4 4 4 4 4 4 4 4 4 3 4 4 4 4 4 1 4 4 4 4 4 4 1 4 4 4 4 4 4 4
## [3405] 4 1 4 4 4 4 1 4 4 4 4 4 1 4 1 4 4 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4
## [3442] 1 4 4 4 4 4 1 1 4 4 1 4 4 4 1 1 4 1 4 1 1 4 1 1 4 4 4 4 1 3 1 1 4 4 4 4 1
## [3479] 4 4 4 4 4 4 4 4 4 4 1 4 4 1 4 4 1 4 1 1 4 4 4 4 1 4 4 4 4 4 1 4 4 4 4 1 4
## [3516] 4 4 1 4 4 1 4 4 4 4 4 1 1 4 4 4 1 4 4 4 1 4 4 4 3 1 4 3 4 1 4 4 4 1 4 4 4
## [3553] 4 4 1 4 1 4 4 4 4 4 4 1 1 4 4 4 4 4 4 4 4 1 4 4 4 4 4 4 4 1 3 4 4 4 4 4 3
## [3590] 4 4 4 4 4 1 4 4 4 4 1 1 4 4 3 1 1 4 4 3 4 4 1 4 4 4 4 1 4 1 4 4 4 1 4 4 1
## [3627] 4 4 4 4 4 1 1 4 3 4 3 4 4 4 4 4 4 4 3 1 1 1 4 4 4 4 4 4 3 4 1 1 1 4 4 4 4
## [3664] 4 4 4 4 4 4 4 4 4 4 3 4 4 1 4 4 4 4 1 1 4 1 4 4 2 4 4 1 1 1 4 4 4 4 4 4 4
## [3701] 1 4 4 4 4 1 4 4 4 4 1 4 4 4 1 4 1 4 1 4 1 4 4 1 1 4 1 1 4 4 2 4 4 4 4 4 1
## [3738] 4 4 4 4 4 1 1 4 4 4 4 1 4 1 1 4 4 4 1 4 1 4 1 1 4 4 4 4 1 4 4 4 4 4 4 4 1
## [3775] 4 4 4 1 3 1 4 1 3 1 4 4 4 4 4 4 4 4 4 4 1 4 1 4 4 1 4 4 3 4 3 4 4 1 4 1 4
## [3812] 4 4 4 4 4 4 1 4 4 4 4 4 1 4 4 1 4 1 4 4 4 4 4 4 4 1 4 4 4 4 4 4 4 4 4 4 4
## [3849] 1 4 4 4 4 3 4 3 1 4 4 4 4 4 4 3 1 4 4 4 4 4 1 1 4 1 4 4 1 4 3 1 1 1 4 4 1
## [3886] 1 4 1 3 4 4 4 4 4 4 4 4 4 1 4 4 3 4 4 4 4 4 4 1 4 1 4 1 4 3 4 4 4 4 1 4 1
## [3923] 4 4 4 1 4 4 4 1 4 4 1 4 4 4 1 4 1 4 4 4 1 4 4 4 4 4 4 4 4 3 4 4 4 4 4 4 4
## [3960] 4 1 4 4 4 4 4 4 4 4 2 1 4 4 1 3 4 1 1 4 3 4 1 1 4 1 4 4 3 1 4 4 4 1 4 1 1
## [3997] 4 1 1 1 4 4 4 4 4 4 1 1 1 4 1 4 4 4 1 4 1 1 4 4 4 1 1 4 1 4 4 4 4 3 4 1 4
## [4034] 1 4 4 4 4 4 4 4 4 4 4 1 4 4 4 1 4 1 3 4 4 4 1 1 4 4 1 3 1 4 4 4 1 1 1 1 1
## [4071] 4 4 4 1 4 1 4 1 1 4 4 1 4 1 1 4 1 4 4 4 1 4 4 4 1 1 1 1 4 4 4 4 4 4 4 1 4
## [4108] 4 1 4 4 4 4 1 4 3 4 4 4 4 1 1 4 4 1 4 4 4 1 4 4 4 4 1 4 1 1 4 1 4 4 1 4 4
## [4145] 4 4 4 1 1 4 4 4 4 4 1 4 4 4 4 2 4 4 1 1 3 4 4 1 1 4 4 3 1 1 1 4 4 4 4 4 1
## [4182] 4 1 4 4 1 4 1 4 4 4 4 4 4 4 4 4 4 4 4 1 4 4 4 1 4 1 4 4 4 3 4 4 1 4 4 4 4
## [4219] 4 1 4 1 4 4 1 1 4 1 4 4 3 4 1 1 4 4 4 4 4 1 1 4 4 4 1 4 1 4 4 3 1 4 4 1 4
## [4256] 3 4 1 4 1 4 4 4 1 4 3 4 4 4 4 4 1 4 4 4 4 4 1 4 4 4 4 4 1 4 4 4 4 4 4 4 1
## [4293] 1 4 3 4
## 
## Within cluster sum of squares by cluster:
## [1]  493.5184 1676.5843 1068.7255  972.0582
##  (between_SS / total_SS =  67.3 %)
## 
## Available components:
## 
## [1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss"
## [6] "betweenss"    "size"         "iter"         "ifault"

Optimizar número de grupos

set.seed(123)
optimizacion3 <- clusGap(rfm_escalados, FUN=kmeans, nstart=25, K.max=10, B=50, iter.max=20)
## Warning: Quick-TRANSfer stage steps exceeded maximum (= 214800)
## Warning: Quick-TRANSfer stage steps exceeded maximum (= 214800)
## Warning: Quick-TRANSfer stage steps exceeded maximum (= 214800)
plot(optimizacion3, xlab="Numero de clusters k", main="Optimización de Clusters")

## Graficar los grupos

fviz_cluster(clusters3, data=rfm_escalados)

Agregar grupos a la base de datos

rfm_clusters <- cbind(rfm, cluster = clusters3$cluster)
head(rfm_clusters)
##   Cliente Recencia Frecuencia Monetario cluster
## 1   12346      326          1  77183.60       3
## 2   12347        3          7   4310.00       4
## 3   12349       19          1   1757.55       4
## 4   12350      311          1    334.40       1
## 5   12352       37          8   2506.04       4
## 6   12353      205          1     89.00       1
rfm_clusters %>%
  dplyr::group_by(cluster) %>%
  dplyr::summarise(
    Recencia_prom = mean(Recencia),
    Frecuencia_prom = mean(Frecuencia),
    Monetario_prom = mean(Monetario),
    n_clientes = dplyr::n()
  )
## # A tibble: 4 × 5
##   cluster Recencia_prom Frecuencia_prom Monetario_prom n_clientes
##     <int>         <dbl>           <dbl>          <dbl>      <int>
## 1       1        249.              1.55           471.       1059
## 2       2          7.91           74.3         126745.         11
## 3       3         15.8            22.2          12468.        208
## 4       4         44.2             3.67          1351.       3018
LS0tCnRpdGxlOiAiQ2x1c3RlcnMgLSBQdW50b3MsIFVTQXJyZXN0cyB5IENsaWVudGVzIgphdXRob3I6ICJHYWJyaWVsIFRlamVkYSBBMDExNzg0NTkiCmRhdGU6ICIyMDI2LTA4LTI1IgpvdXRwdXQ6IAogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IFRSVUUKICAgIHRvY19mbG9hdDogVFJVRQogICAgY29kZV9kb3dubG9hZDogVFJVRQogICAgdGhlbWU6IHNwYWNlbGFiCi0tLQoKIVtdKGh0dHBzOi8vdG93YXJkc2RhdGFzY2llbmNlLmNvbS93cC1jb250ZW50L3VwbG9hZHMvMjAyMi8wNC8xNW9TNWIzajQ3enZDZTQzSHV5VWlVZy5naWYpCgojIDxzcGFuIHN0eWxlPSJjb2xvcjpncmVlbiI+IFRlb3LDrWEgPC9zcGFuPgoqKkFncnVwYW1pZW50byoqIG8gKmNsdXN0ZXJpbmcqIGVzIHVuYSB0w6ljbmljYSBkZSBhcHJlbmRpemFqZSBhdXRvbcOhdGljbyBubyBzdXBlcnZpc2FkbyBxdWUgYWdydXBhIGRhdG9zIGVuIGZ1bmNpw7NuIGRlIHN1IHNpbWlsaXR1ZC4gIAoKQWxndW5vcyB1c29zIHTDrXBpY29zIGRlIGVzdGEgdMOpY25pY2Egc29uOiAgCiAKKiBTZWdtZW50YWNpw7NuIGRlIGNsaWVudGVzICAKKiBEZXRlY2Npw7NuIGRlIGFub3JtYWxpZGFkZXMgIAoqIENhdGVnb3JpemFjacOzbiBkZSBkb2N1bWVudG9zICAgCgojIDxzcGFuIHN0eWxlPSJjb2xvcjpncmVlbiI+IEluc3RhbGFyIHBhcXVldGVzIHkgbGxhbWFyIGxpYnJlcsOtYXMgPC9zcGFuPgoKYGBge3J9CiMgaW5zdGFsbC5wYWNrYWdlcygiY2x1c3RlciIpICNhbmFsaXNpcyBkZSBhZ3J1cGFtaWVudG8KbGlicmFyeShjbHVzdGVyKQojaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpICNncmFmaWNhcgpsaWJyYXJ5KGdncGxvdDIpCiNpbnN0YWxsLnBhY2thZ2VzKCJkYXRhLnRhYmxlIikgI21hbmVqbyBkZSBtdWNob3MgZGF0b3MKbGlicmFyeShkYXRhLnRhYmxlKQojaW5zdGFsbC5wYWNrYWdlcygiZmFjdG9leHRyYSIpICNncmFmaWNhIGRlIG9wdGltaXphY2lvbiBkZWwgbnVtZXJvIGRlIGNsdXN0ZXJzCmxpYnJhcnkoZmFjdG9leHRyYSkKIyBpbnN0YWxsLnBhY2thZ2VzKCJkYXRhc2V0cyIpCmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCiMgPHNwYW4gc3R5bGU9ImNvbG9yOmdyZWVuIj4gRWplcmNpY2lvIDE6IFB1bnRvcyA8L3NwYW4+CgojIyA8c3BhbiBzdHlsZT0iY29sb3I6Z3JlZW4iPiBDb250ZXh0byA8L3NwYW4+CkFncnVwYSBsb3Mgc2lndWllbnRlcyA4IHB1bnRvcy4KCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjpncmVlbiI+IE9idGVuZXIgZGF0b3MgPC9zcGFuPgpgYGB7cn0KZGYxIDwtIGRhdGEuZnJhbWUoeD1jKDIsMiw4LDUsNyw2LDEsNCksIHk9YygxMCw1LDQsOCw1LDQsMiw5KSkKYGBgCgojIyA8c3BhbiBzdHlsZT0iY29sb3I6Z3JlZW4iPiBFbnRlbmRlciBkYXRvcyA8L3NwYW4+CmBgYHtyfQpzdW1tYXJ5KGRmMSkKc3RyKGRmMSkKcGxvdChkZjEpCmBgYAoKIyMgPHNwYW4gc3R5bGU9ImNvbG9yOmdyZWVuIj4gRXNjYWxhciBkYXRvcyA8L3NwYW4+CmBgYHtyfQojIGVqZW1wbG86CiMgZGF0b3NfZXNjYWxhZG9zIDwtIHNjYWxlKGRhdG9zX29yaWdpbmFsZXMpCmBgYAoKIyMgPHNwYW4gc3R5bGU9ImNvbG9yOmdyZWVuIj4gQXNpZ25hciBudW1lcm8gZGUgZ3J1cG9zIDwvc3Bhbj4KYGBge3J9CmdydXBvczEgPC0gMwpgYGAKCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjpncmVlbiI+IEFncnVwYXIgbG9zIHB1bnRvcyA8L3NwYW4+CmBgYHtyfQpzZXQuc2VlZCgxMjMpCmNsdXN0ZXJzMSA8LSBrbWVhbnMoZGYxLGdydXBvczEpCmNsdXN0ZXJzMQpgYGAKCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjpncmVlbiI+IE9wdGltaXphciBudW1lcm8gZGUgZ3J1cG9zIDwvc3Bhbj4KYGBge3J9CnNldC5zZWVkKDEyMykKb3B0aW1pemFjaW9uMSA8LSBjbHVzR2FwKGRmMSwgRlVOPWttZWFucywgbnN0YXJ0PTEsIEsubWF4PTcpCiMgRWwgSy5tYXggbm9ybWFsbWVudGUgZXMgMTAsIGVuIGVzdGUgZWplcmNpY2lvIGFsIHNlciA4IGRhdG9zIHVuaWNhbWVudGUgc2UgZGVqw7MgZW4gNy4KcGxvdChvcHRpbWl6YWNpb24xLCB4bGFiPSJOdW1lcm8gZGUgY2x1c3RlcnMgayIsIG1haW49Ik9wdGltaXphY2nDs24gZGUgQ2x1c3RlcnMiKQojIFNlIHNlbGVjY2lvbmEgY29tbyDDs3B0aWNvIGVsIHByaW1lciBwdW50byBtw6FzIGFsdG8uCmBgYAojIyA8c3BhbiBzdHlsZT0iY29sb3I6Z3JlZW4iPiBHcmFmaWNhciBsb3MgZ3J1cG9zIDwvc3Bhbj4KYGBge3J9CmZ2aXpfY2x1c3RlcihjbHVzdGVyczEsZGF0YT1kZjEpCmBgYAoKIyMgPHNwYW4gc3R5bGU9ImNvbG9yOmdyZWVuIj5BZ3JlZ2FyIGdydXBvcyBhIGxhIGJhc2UgZGUgZGF0b3MgPC9zcGFuPgpgYGB7cn0KZGYxX2NsdXN0ZXJzIDwtIGNiaW5kKGRmMSwgY2x1c3RlciA9IGNsdXN0ZXJzMSRjbHVzdGVyKQpoZWFkKGRmMV9jbHVzdGVycykKYGBgCgojIyA8c3BhbiBzdHlsZT0iY29sb3I6Z3JlZW4iPiBDb25jbHVzaW9uZXMgPC9zcGFuPgpMYSB0w6ljbmljYSBkZSAqY2x1c3RlcmluZyogcGVybWl0ZSBpZGVudGlmaWNhciBwYXRyb25lcyBvIGdydXBvcyBuYXR1cmFsZXMgZW4gbG9zIGRhdG9zIHNpbiBuZWNlc2lkYWQgZGUgZXRpcXVldGFzIHByZXZpYXMuCgoKIyA8c3BhbiBzdHlsZT0iY29sb3I6Z3JlZW4iPiBFamVyY2ljaW8gMjogVVNBcnJlc3RzIDwvc3Bhbj4KCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjpncmVlbiI+IENvbnRleHRvIDwvc3Bhbj4KTGEgYmFzZSBkZSBkYXRvcyAqKlVTQXJyZXN0cyoqIGNvbnRpZW5lIGVzdGFkaXN0aWNhcyBlbiBhcmVzdG9zIHBvciBjYWRhIDEwMCwwMDAgcmVzaWRlbnRlcyBwb3IgYWdyZXNpw7NuLCBhc2VzaW5hdG8geSB2aW9sYWNpw7NuIGVuIGNhZGEgdW5vIGQgZWxvcyA1MCBlc3RhZG9zIGRlIEVFLlVVLiBlbiAxOTczLgoKIyMgPHNwYW4gc3R5bGU9ImNvbG9yOmdyZWVuIj4gT2J0ZW5lciBkYXRvcyA8L3NwYW4+CmBgYHtyfQpkZjIgPC0gVVNBcnJlc3RzCmRmMiA8LSBkZjIgJT4lIHNlbGVjdCgtVXJiYW5Qb3ApIApgYGAKCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjpncmVlbiI+IEVudGVuZGVyIGRhdG9zIDwvc3Bhbj4KYGBge3J9CnN1bW1hcnkoZGYyKQpzdHIoZGYyKQpgYGAKCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjpncmVlbiI+IEVzY2FsYXIgZGF0b3MgPC9zcGFuPgpgYGB7cn0KZGYyX2VzY2FsYWRvcyA8LSBzY2FsZShkZjIpCnN1bW1hcnkoZGYyX2VzY2FsYWRvcykKYGBgCgojIyA8c3BhbiBzdHlsZT0iY29sb3I6Z3JlZW4iPiBBc2lnbmFyIG51bWVybyBkZSBncnVwb3MgPC9zcGFuPgpgYGB7cn0KZ3J1cG9zMiA8LSA1CmBgYAoKIyMgPHNwYW4gc3R5bGU9ImNvbG9yOmdyZWVuIj4gQWdydXBhciBsb3MgcHVudG9zIDwvc3Bhbj4KYGBge3J9CnNldC5zZWVkKDEyMykKY2x1c3RlcnMyIDwtIGttZWFucyhkZjJfZXNjYWxhZG9zLGdydXBvczIpCmBgYAoKIyMgPHNwYW4gc3R5bGU9ImNvbG9yOmdyZWVuIj4gT3B0aW1pemFyIG51bWVybyBkZSBncnVwb3MgPC9zcGFuPgpgYGB7cn0Kc2V0LnNlZWQoMTIzKQpvcHRpbWl6YWNpb24yIDwtIGNsdXNHYXAoZGYyX2VzY2FsYWRvcywgRlVOPWttZWFucywgbnN0YXJ0PTEsIEsubWF4PTEwKQpwbG90KG9wdGltaXphY2lvbjIsIHhsYWI9Ik51bWVybyBkZSBjbHVzdGVycyBrIiwgbWFpbj0iT3B0aW1pemFjacOzbiBkZSBDbHVzdGVycyIpCiMgU2Ugc2VsZWNjaW9uYSBjb21vIMOzcHRpY28gZWwgcHJpbWVyIHB1bnRvIG3DoXMgYWx0by4KYGBgCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjpncmVlbiI+IEdyYWZpY2FyIGxvcyBncnVwb3MgPC9zcGFuPgpgYGB7cn0KZnZpel9jbHVzdGVyKGNsdXN0ZXJzMixkYXRhPWRmMl9lc2NhbGFkb3MpCiMgYWdncmVnYXRlKGRmMiwgYnk9bGlzdChHcnVwbz1jbHVzdGVyczIpKQpgYGAKCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjpncmVlbiI+QWdyZWdhciBncnVwb3MgYSBsYSBiYXNlIGRlIGRhdG9zIDwvc3Bhbj4KYGBge3J9CmRmMl9jbHVzdGVycyA8LSBjYmluZChkZjIsIGNsdXN0ZXIgPSBjbHVzdGVyczIkY2x1c3RlcikKaGVhZChkZjJfY2x1c3RlcnMpCgpkZjJfY2x1c3RlcnMgJT4lIGdyb3VwX2J5KGNsdXN0ZXIpICU+JSBzdW1tYXJpc2VfYWxsKG1lYW4pICU+JQogIG11dGF0ZShpbmRpY2Fkb3JfaW5zZWd1cmlkYWQ9TXVyZGVyK0Fzc2F1bHQrUmFwZSkKCmRmMl9jbHVzdGVycyA8LSBkZjJfY2x1c3RlcnMgICU+JQogIG11dGF0ZShjbHVzdGVyPSBjYXNlX3doZW4oCiAgICBjbHVzdGVyID09IDEgfiAiSW5zZWd1cmlkYWQgTXV5IEFsdGEiLAogICAgY2x1c3RlciA9PSA0IH4gIkluc2VndXJpZGFkIEFsdGEiLAogICAgY2x1c3RlciA9PSA1IH4gIkluc2VndXJpZGFkIE1lZGlhIiwKICAgIGNsdXN0ZXIgPT0gMyB+ICJJbnNlZ3VyaWRhZCBCYWphIiwKICAgIGNsdXN0ZXIgPT0gMiB+ICJJbnNlZ3VyaWRhZCBNdXkgQmFqYSIKICApKQpoZWFkKGRmMl9jbHVzdGVycykKCmBgYAoKIyA8c3BhbiBzdHlsZT0iY29sb3I6Z3JlZW4iPiBFamVyY2ljaW8gMy4gU2VnbWVudGFjacOzbiBkZSBDbGllbnRlcyA8L3NwYW4+CgojIyA8c3BhbiBzdHlsZT0iY29sb3I6Z3JlZW4iPiBDb250ZXh0byA8L3NwYW4+CkxhIGJhc2UgZGUgZGF0b3MgKip2ZW50YXMqKiB0aWVuZSBsb3MgcmVnaXN0cm9zIGVudHJlIGVsIDEgZGUgZGljaWVtYnJlIGRlIDIwMTAgeSBlbCA5IGRlIGRpY2llbWJyZSBkZSAyMDExIGRlIGxhcyB2ZW50YXMgZGUgdW5hIGVtcHJlc2EgbWlub3Jpc3RhIGVuIGzDrW5lYSBzaW4gdGllbmRhIGbDrXNpY2EsIGJhc2FkYSBlbiBSZWlubyBVbmlkby4gIApMYSBlbXByZXNhIHZlbmRlIHByaW5jaXBhbG1lbnRlIHJlZ2Fsb3Mgw7puaWNvcyBwYXJhIHRvZGEgb2Nhc2nDs24sIHkgbXVjaG9zIGRlIHN1cyBjbGllbnRlcyBzb24gbWF5b3Jpc3Rhcy4gIApPYmpldGl2bzogc2VnbWVudGFyIGNsaWVudGVzLCBhc2lnbmFybGVzIG5vbWJyZSB5IGNhcmFjdGVyaXN0aWNhcyBkZSBjb21wb3J0YW1pZW50byB5IHByb3BvbmVyIHN1Z2VyZW5jaWFzIGEgbGEgZW1wcmVzYSBwYXJhIGF1bWVudGFyIHZlbnRhcy4KCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjpncmVlbiI+IE9idGVuZXIgRGF0b3MgPC9zcGFuPgpgYGB7cn0KIyBmaWxlLmNob29zZSgpCmRmMyA8LSByZWFkLmNzdigiL1VzZXJzL2dhYm90ZWplZGEvRGVza3RvcC9jb2RpZ29zIFIvdmVudGFzLmNzdiIpCmBgYAoKIyMgPHNwYW4gc3R5bGU9ImNvbG9yOmdyZWVuIj4gRW50ZW5kZXIgbG9zIERhdG9zIDwvc3Bhbj4KYGBge3J9CnN1bW1hcnkoZGYzKQpzdHIoZGYzKQpzdW0oaXMubmEoZGYzJENsaWVudGUpKQpgYGAKCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjpncmVlbiI+IExpbXBpZXphIGRlIGRhdG9zIDwvc3Bhbj4KYGBge3J9CiMgUXVpdGFtb3MgY29tcHJhcyBzaW4gY2xpZW50ZSBpZGVudGlmaWNhZG8KZGYzIDwtIGRmMyAlPiUgZHBseXI6OmZpbHRlcighaXMubmEoQ2xpZW50ZSkpCgojIFF1aXRhbW9zIGNhbmNlbGFjaW9uZXMvZGV2b2x1Y2lvbmVzIChDYW50aWRhZCBvIFByZWNpbyBuZWdhdGl2b3MpCmRmMyA8LSBkZjMgJT4lIGRwbHlyOjpmaWx0ZXIoQ2FudGlkYWQgPiAwLCBQcmVjaW8gPiAwKQoKIyBDb252ZXJ0aW1vcyBGZWNoYSBhIGZvcm1hdG8gZmVjaGEKZGYzJEZlY2hhIDwtIGFzLkRhdGUoZGYzJEZlY2hhLCBmb3JtYXQ9IiVkLyVtLyVZIikKCiMgQ2FsY3VsYW1vcyBlbCBtb250byBkZSBjYWRhIGzDrW5lYSBkZSBjb21wcmEKZGYzIDwtIGRmMyAlPiUgZHBseXI6Om11dGF0ZShNb250byA9IENhbnRpZGFkICogUHJlY2lvKQpgYGAKCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjpncmVlbiI+IENvbnN0cnVpciB0YWJsYSBSRk0gKFJlY2VuY2lhLCBGcmVjdWVuY2lhLCBNb25ldGFyaW8pIHBvciBjbGllbnRlIDwvc3Bhbj4KYGBge3J9CmZlY2hhX3JlZmVyZW5jaWEgPC0gbWF4KGRmMyRGZWNoYSkgKyAxCgpyZm0gPC0gZGYzICU+JQogIGRwbHlyOjpncm91cF9ieShDbGllbnRlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKAogICAgUmVjZW5jaWEgPSBhcy5udW1lcmljKGZlY2hhX3JlZmVyZW5jaWEgLSBtYXgoRmVjaGEpKSwKICAgIEZyZWN1ZW5jaWEgPSBkcGx5cjo6bl9kaXN0aW5jdChUaWNrZXQpLAogICAgTW9uZXRhcmlvID0gc3VtKE1vbnRvKQogICkKCnN1bW1hcnkocmZtKQpucm93KHJmbSkgIApgYGAKIyMgPHNwYW4gc3R5bGU9ImNvbG9yOmdyZWVuIj4gRXNjYWxhciBEYXRvcyA8L3NwYW4+CmBgYHtyfQpyZm1fZXNjYWxhZG9zIDwtIHNjYWxlKHJmbSAlPiUgZHBseXI6OnNlbGVjdChSZWNlbmNpYSwgRnJlY3VlbmNpYSwgTW9uZXRhcmlvKSkKc3VtbWFyeShyZm1fZXNjYWxhZG9zKQpgYGAKIyMgPHNwYW4gc3R5bGU9ImNvbG9yOmdyZWVuIj4gQXNpZ25hciBudW1lcm8gZGUgZ3J1cG9zIDwvc3Bhbj4KYGBge3J9CmdydXBvczMgPC0gNApgYGAKCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjpncmVlbiI+IEFncnVwYXIgbG9zIGNsaWVudGVzIDwvc3Bhbj4KYGBge3J9CnNldC5zZWVkKDEyMykKY2x1c3RlcnMzIDwtIGttZWFucyhyZm1fZXNjYWxhZG9zLCBncnVwb3MzLCBuc3RhcnQgPSAyNSkKY2x1c3RlcnMzCmBgYAoKIyMgPHNwYW4gc3R5bGU9ImNvbG9yOmdyZWVuIj4gT3B0aW1pemFyIG7Dum1lcm8gZGUgZ3J1cG9zIDwvc3Bhbj4KCmBgYHtyfQpzZXQuc2VlZCgxMjMpCm9wdGltaXphY2lvbjMgPC0gY2x1c0dhcChyZm1fZXNjYWxhZG9zLCBGVU49a21lYW5zLCBuc3RhcnQ9MjUsIEsubWF4PTEwLCBCPTUwLCBpdGVyLm1heD0yMCkKcGxvdChvcHRpbWl6YWNpb24zLCB4bGFiPSJOdW1lcm8gZGUgY2x1c3RlcnMgayIsIG1haW49Ik9wdGltaXphY2nDs24gZGUgQ2x1c3RlcnMiKQpgYGAKIyMgPHNwYW4gc3R5bGU9ImNvbG9yOmdyZWVuIj4gR3JhZmljYXIgbG9zIGdydXBvcyA8L3NwYW4+CmBgYHtyfQpmdml6X2NsdXN0ZXIoY2x1c3RlcnMzLCBkYXRhPXJmbV9lc2NhbGFkb3MpCmBgYAoKIyMgPHNwYW4gc3R5bGU9ImNvbG9yOmdyZWVuIj4gQWdyZWdhciBncnVwb3MgYSBsYSBiYXNlIGRlIGRhdG9zIDwvc3Bhbj4KYGBge3J9CnJmbV9jbHVzdGVycyA8LSBjYmluZChyZm0sIGNsdXN0ZXIgPSBjbHVzdGVyczMkY2x1c3RlcikKaGVhZChyZm1fY2x1c3RlcnMpCgpyZm1fY2x1c3RlcnMgJT4lCiAgZHBseXI6Omdyb3VwX2J5KGNsdXN0ZXIpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoCiAgICBSZWNlbmNpYV9wcm9tID0gbWVhbihSZWNlbmNpYSksCiAgICBGcmVjdWVuY2lhX3Byb20gPSBtZWFuKEZyZWN1ZW5jaWEpLAogICAgTW9uZXRhcmlvX3Byb20gPSBtZWFuKE1vbmV0YXJpbyksCiAgICBuX2NsaWVudGVzID0gZHBseXI6Om4oKQogICkKYGBgCgoKCg==