Clustering o Análisis de Conglomerados, en español, comprende un gran número de metódos que tienen como objetivo descubir un número limitado de grupos significativos (clusters) para un conjunto de datos.
Un grupo es significativo si sus unidades son similares entre si y diferentes a las unidades de otros grupos.
El concepto de similitud o disimilitud entre unidades es facil de entender y bien aceptado: Todos estamos en la capacidad de evaluar si dos unidades son similares o diferentes.
Se debe tener el supuesto implícito de que existe algún tipo de homogeneidad y separación en el conjunto de datos.
El Clustering es un Método No Supervisado cuyo objetivo es descubrir grupos o clusters sugeridos de manera bastante natural por los datos sin tener un conocimiento previo de grupos ya formados (Unlabeled Data).
En contraste, los metodos que se usan en problemas de clasificación son Métodos Supervisados donde los grupos o clases se conocen a priori y el problema de investigación es predecir la pertenencia de nuevas unidades a una de estas clases basandonos en las variables de las unidades pasadas (Labeled Data).
Existe otro tipo llamado Aprendizaje Semi-Supervisado el cual combina tecnicas de ambos tipos de aprendizaje para conjuntos de datos que contienen datos etiquetados como no etiquetados.
Decimos que un metodo es hard clustering (agrupamiento duro) si una unidad solo puede pertenecer a un cluster.
Los metodos soft clustering (agrupamiento suave) relajan esa asignación dura, de modo que cada unidad tiene grados de pertenencia a cada cluster, estos grados son valores en el intervalo \([0,1]\) donde valores cercanos a \(1\) indican una fuerte pertenencia a un cluster en especifico. En esta categoria caen los enfoques Difusos (Fuzzy Aproach) y basados en modelos (Model-based Aproach).
| Caracteristica | Estandar | Difuso | Basado en Modelos |
|---|---|---|---|
| Hard clustering | Sí | No | No |
| Soft clustering | No | Sí | Sí |
| Probabilistic assumptions | No | No | Sí |
Todos los metodos de agrupamiento jerarquico requieren de:
Una vez se tienen estos 2 insumos, todos los metodos jerarquicos tradicionales se dividen en 2 tipos:
Aglomerativos (Agglomerative Nesting): Funcionan de “de abajo hacia arriba”. Es decir, cada unidad se considera inicialmente como un cluster de un solo elemento. En cada paso del algoritmo, los dos clusters que son más similares se combinan en un nuevo cluster más grande. El algoritmo termina cuando todas las unidades pertenecen a un solo cluster.
Divisivos (DIvisive ANAlysis Clustering): Funcionan de “de arriba hacia abajo”. Es decir, en el inicio del algoritmo todas las unidades pertenecen al mismo grupo. Luego, los clusters más heterogéneos se dividen sucesivamente hasta que todas las unidades terminan en su propio cluster.
Los resultados de los metodos jerárquica se representan mediante un arbol que indica todas las particiones obtenidas durante el proceso, esta representación se le conoce como dendograma.
Observación: Note que no fue necesario especificar un número de clusters a priori.
Aunque hemos hablado de medidas de similitud y disimilitud, los metodos (en su mayoria por no decir que todos) funcionan con medidas de disimilitud. Además se suele emplear el termino Matriz de Distancias en lugar de Matriz de Disimilitudes, aunque muchas medidas no sean propiamente medidas de distancia en el estricto sentido.
A continuación se enlistan algunas medidas de habitual uso.
| Medida de distancia | Fórmula | Tipo de variable |
|---|---|---|
| Máxima (Chebyshev) | \(d(x,y) = \max_i |x_i - y_i|\) | Continuas |
| Euclidiana | \(d(x,y) = \sqrt{\sum_{i=1}^p (x_i - y_i)^2}\) | Continuas |
| Manhattan | \(d(x,y) = \sum_{i=1}^p |x_i - y_i|\) | Continuas o Discretas |
| Minkowski | \(d(x,y) = \left( \sum_{i=1}^p |x_i - y_i|^q \right)^{1/q}\) | Numéricas \(q \geq 1\) |
| Canberra | \(d(x,y) = \sum_{i=1}^p \frac{|x_i - y_i|}{|x_i| + |y_i|}\) | Numéricas (sensible a valores pequeños) |
| Binaria (Jaccard) |
\(d(x,y) = 1 - \frac{a}{a + b + c}\)
Más detalles aquí |
Binarias |
| Mahalanobis | \(d(x,y) = \sqrt{(x - y)^T S^{-1} (x - y)}\) | Continuas |
| Gower |
\(d_{ij} = \frac{\sum_{k=1}^p w_k
\delta_{ij}^{(k)} d_{ij}^{(k)}}{\sum_{k=1}^p w_k
\delta_{ij}^{(k)}}\) Más detalles aquí |
Datos Mixtos (Numericas, Ordinales, Nominales, Binarias) |
| Correlación Pearson | \(d(x,y) = 1 - r_{xy}\) | Numéricas |
| Correlación Spearman | \(d(x,y) = 1 - \rho_{xy}\) | Numéricas, Ordinales |
| Correlación Kendall | \(d(x,y) = 1 - \tau_{xy}\) | Numéricas, Ordinales |
Observación: La Matriz de Distancias NO necesariamente tiene que provenir de los datos.
Por ejemplo, supongamos que se invita a un consumidor a expresar sus niveles subjetivos de disimilitud entre pares de artículos. En este caso, las distancias no se calculan según las variables de los artículos, sino a segun relaciones percibidas entre ellos por el consumidor.
Los métodos de clustering jerárquico aglomerativo producen una serie de particiones donde las dos unidades o clusters más similares se fusionan sucesivamente. En detalle, dada una matriz de distancias \(D_n\) de orden \((n \times n)\), consisten en los siguientes pasos. Nótese que, en el primer paso, la matriz de distancias proporciona las medidas de distancia entre \(n\) clusters singleton.
De acuerdo con \(D_n\), fusionar las dos unidades/clusters con la distancia mínima en un nuevo cluster. Esto conduce a una nueva partición con \(n - 1\) clusters: el nuevo cluster de tamaño 2 y los restantes \(n - 2\) clusters singleton.
Calcular la nueva matriz de distancias \(D_{n-1}\), de orden \((n - 1) \times (n - 1)\). Las medidas de distancia entre los clusters singleton se heredan de la matriz original \(D_n\). Existen varias alternativas para calcular las distancias entre el nuevo cluster y los restantes. Esta elección distingue los diferentes métodos aglomerativos y se discutirá más adelante.
Fusionar los clusters con la distancia mínima usando \(D_{n-1}\), obteniendo una partición con \(n - 2\) clusters.
Repetir los pasos 2 y 3 hasta que permanezca un único cluster. En el último paso, \(D_2\) tiene orden \((2 \times 2)\) y contiene las medidas de distancia entre los dos clusters que se fusionan para obtener la partición final trivial con un único cluster de \(n\) unidades.
El punto crucial de un procedimiento de clustering aglomerativo radica en el método para calcular distancias entre un cluster formado por la fusión de dos clusters y los otros. En general, diferentes métodos producen soluciones distintas.
A continuación se muestran algunas de las funciones de enlace más usadas:
| Método | Fórmula |
|---|---|
| Enlace Simple o Mínimo | \(\min d(i_1, i_2) \ \ , \ \ i_1 \in C_1,\, i_2 \in C_2\) |
| Enlace Completo o Máximo | \(\max d(i_1, i_2) \ \ , \ \ i_1 \in C_1,\, i_2 \in C_2\) |
| Enlace Promedio | \(\dfrac{\sum_{i_1 \in C_1} \sum_{i_2 \in C_2} d(i_1, i_2)}{|C_1|\,|C_2|}\) |
| Método de Ward | \(d(\bar{x}_{C_1},\, \bar{x}_{C_2})\), donde \(\bar{x}_{C_1} = \dfrac{\sum_{i_1 \in C_1} x_i}{|C_1|}\) y \(\bar{x}_{C_2} = \dfrac{\sum_{i_2 \in C_2} x_i}{|C_2|}\) |
Estos métodos pueden definirse según la Fórmula de Lance–Williams. Sean \(C_1\) y \(C_2\) dos clusters que se fusionan para formar el cluster \(C_{1,2}\). La distancia entre este nuevo cluster y el cluster \(C_3\) puede expresarse como:
\[ d(C_{1,2}, C_3) = \alpha_1 d(C_1, C_3) + \alpha_2 d(C_2, C_3) + \beta d(C_1, C_2) + \gamma |d(C_1, C_3) - d(C_2, C_3)| \]
| Método | \(\alpha_1\) | \(\alpha_2\) | \(\beta\) | \(\gamma\) |
|---|---|---|---|---|
| Enlace Simple | 0.5 | 0.5 | 0 | −0.5 |
| Enlace Completo | 0.5 | 0.5 | 0 | 0.5 |
| Enlace Promedio | \(\dfrac{|C_1|}{|C_1| + |C_2|}\) | \(\dfrac{|C_2|}{|C_1| + |C_2|}\) | 0 | 0 |
| Enlace de Ward | \(\dfrac{|C_1| + |C_3|}{|C_1| + |C_2| + |C_3|}\) | \(\dfrac{|C_2| + |C_3|}{|C_1| + |C_2| + |C_3|}\) | \(-\,\dfrac{|C_3|}{|C_1| + |C_2| + |C_3|}\) | 0 |
El agrupamiento divisivo es menos común que el agrupamiento aglomerativo, y los métodos relacionados se han ignorado con frecuencia, especialmente en el pasado. La razón es que son computacionalmente más costosos, por ejemplo, en el primer paso, existen \(2^{(n−1)} − 1\) posibles maneras de dividir el clúster inicial en dos.
Es el algoritmo más famoso de clustering jerárquico divisivo y uno de los procedimientos más optimos para realizar la divison de clusters. Las ideas principales del algoritmo son las siguientes:
Se inicia de un cluster que contiene todas las unidades del conjunto de datos.
Dentro del cluster actual, se identifica la observación con la mayor disimilitud promedio respecto a las demás. Esta observación se convierte en el “núcleo” de un nuevo cluster inicial para una partición.
Se evalúa para cada observación restante si es más similar al nuevo cluster que al cluster original. Si es así, la observación se transfiere al nuevo cluster. A este cluster se le suele llamar splinter group (grupo disidente).
Una vez que ninguna observación adicional deba ser movida, se obtiene una partición en dos clusters.
Se selecciona el cluster con mayor diámetro (mayor disimilitud interna) y se repite el procedimiento de división hasta obtener tantas particiones como se desee o hasta llegar a clusters individuales.
Tambien conocido como agrupamiento particionado (Partitioning Clustering). Si bien los metodos jerarquicos nos dan una solución intuitiva al problema, existen al menos 2 propiedas valiosas que justifican la existencia de los metodos no jerarquicos:
No requieren el calculo de la Matrix de Distancias. Esto es particularmente relevante para conjunto de datos moderadamente grandes para los cuales el calculo de la matriz de distancias consume mucha memoria y tiempo.
En los metodos jerarquicos aglomerativos, una vez 2 unidades se unen en un cluster, estas no se separarn durante el resto del algoritmo. Similarmente, en los metodos divisivos, si 2 unidades pertenecen a clusters diferentes, ellas no pueden volver a estar juntas. Por lo tanto, para ciertas estructuras los metodos jerarquicos no detectan bien los clusters implicitos.
Observación: A diferencia de los metodos jerarquicos, aquí se debe especificar el número de cluster que se desea obtener.
El algoritmo K-Means es uno de los métodos de clustering no jerárquicos más utilizados. Busca minimizar la variabilidad interna dentro de cada cluster. Su objetivo es asignar cada observación al cluster cuyo centro (o centroide) sea el más cercano.
Es adecuado cuando todas las variables son cuantitativas y busca encontrar la mejor partición de \(n\) unidades en \(k\) clusters.
Para evaluar qué tan buena es una partición, se descompone la suma total de cuadrados (\(T\)) en dos componentes:
La relación fundamental es:
\[ T = W + B. \]
donde:
\[ T = \sum_{i=1}^n \sum_{j=1}^p (x_{ij}-\bar{x}_j)^2. \]
\[ W = \sum_{g=1}^k W_g, \qquad W_g = \sum_{i=1}^{n_g} \sum_{j=1}^p (x_{ij}-\bar{x}_{gj})^2. \]
\[ B = \sum_{g=1}^k n_g (\bar{x}_{gj}-\bar{x}_j)^2. \]
Aquí:
Cuando las observaciones de un cluster coinciden exactamente con su centroide (\(x_{ij}=\bar{x}_{gj}\)), se obtiene \(W_g = 0\).
Por tanto, la mejor partición de \(n\) unidades en \(k\) clusters se obtiene minimizando la suma de cuadrados dentro de los clusters:
\[ \min W. \]
El procedimiento estándar de K-means consiste en los siguientes pasos:
Inicialización
Se eligen \(K\) centroides iniciales,
ya sea al azar o mediante un método específico como
k-means++.
Asignación de observaciones a clusters
Cada observación se asigna al cluster cuyo centroide esté más cerca
según la distancia Euclidiana:
\[ \text{Asignar } x_i \rightarrow C_j \quad \text{si} \quad d(x_i, \mu_j) = \min_{k} d(x_i, \mu_k) \]
Recomputación de centroides
Para cada cluster, se actualiza su centroide calculando el promedio de
las observaciones asignadas:
\[ \mu_j = \frac{1}{|C_j|}\sum_{x_i \in C_j} x_i \]
Iteración
Se repiten los pasos de asignación y actualización hasta que las
asignaciones no cambien o la disminución de la suma de cuadrados dentro
de los clusters sea mínima.
Criterio de optimización
K-means minimiza la suma de cuadrados dentro de los
clusters:
\[ \sum_{j=1}^K \sum_{x_i \in C_j} d(x_i, \mu_j)^2 \]
Observaciones:
El algoritmo k-medoids es un enfoque de clustering similar a k-means, cuyo objetivo es particionar un conjunto de datos en \(k\) grupos. A diferencia de k-means, donde cada cluster es representado por un centroide, en k-medoids cada cluster está representado por uno de los puntos reales del conjunto de datos, llamado medoide.
Un medoide es la observación dentro del cluster cuya disimilitud promedio respecto a las demás observaciones del cluster es mínima. En otras palabras, es el punto más central y representativo del cluster. Esto proporciona una interpretación más intuitiva que los centroides, ya que los medoides corresponden a observaciones reales.
El método k-medoids es más robusto al ruido y a los valores atípicos que k-means, ya que no utiliza medias sino observaciones reales como prototipos de cluster.
El algoritmo k-medoids más utilizado es PAM (Partitioning Around Medoids).
El procedimiento estándar de K-medoids consiste en los siguientes pasos:
Seleccionar \(k\)
objetos como medoides iniciales
Si el usuario proporciona los medoides, utilizarlos
directamente.
Calcular la matriz de disimilitud
Este cálculo se realiza únicamente si no se ha proporcionado
previamente.
Asignación de objetos a clusters
Cada objeto se asigna al medoide más cercano según la medida de
disimilitud elegida.
Actualización de medoides
Para cada cluster:
Criterio de parada
No existe conocimiento a priori de la existencia ni del número de grupos.
Como medir el grado de similitud o disimilitud entre unidades. El como tiene una gran influencia sobre los resultados que se obtengan. Aquí hay almenos 3 puntos a tener en cuenta:
El Metodo de Clustering a usar.
No existen reglas de oro para estas elecciones porque dependen del problema de investigación específico de los datos subyacentes.
Ver dataset Insurance Claims.
El dataset Insurance Claims contiene información detallada sobre reclamaciones de seguros por parte de clientes. Incluye variables demográficas, características del conductor y del vehículo, información de la póliza, historial de incidentes y una etiqueta de fraude utilizada originalmente para el contexto supervisado. Para este análisis de clustering solo se utilizarán las variables relevantes no identificadoras.
El archivo está compuesto por aproximadamente 1.000 observaciones y más de 30 variables, de tipo numérico y categórico. A continuación, se presenta una descripción general de las principales variables:
"?" que deben ser tratadas en el preprocesamiento.## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ forcats 1.0.0 ✔ readr 2.1.5
## ✔ ggplot2 4.0.1 ✔ stringr 1.5.1
## ✔ lubridate 1.9.4 ✔ tibble 3.3.0
## ✔ purrr 1.0.4 ✔ tidyr 1.3.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ kableExtra::group_rows() masks dplyr::group_rows()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
##
## Adjuntando el paquete: 'janitor'
##
## The following objects are masked from 'package:stats':
##
## chisq.test, fisher.test
# Cargar archivo y estructura de la base
df_raw <- read_csv("insurance_claims.csv") %>%
clean_names()## Rows: 1000 Columns: 40
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (19): policy_state, policy_csl, insured_sex, insured_education_level, i...
## dbl (18): months_as_customer, age, policy_number, policy_deductable, policy...
## lgl (1): _c39
## date (2): policy_bind_date, incident_date
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 1,000
## Columns: 40
## $ months_as_customer <dbl> 328, 228, 134, 256, 228, 256, 137, 165, 27…
## $ age <dbl> 48, 42, 29, 41, 44, 39, 34, 37, 33, 42, 42…
## $ policy_number <dbl> 521585, 342868, 687698, 227811, 367455, 10…
## $ policy_bind_date <date> 2014-10-17, 2006-06-27, 2000-09-06, 1990-…
## $ policy_state <chr> "OH", "IN", "OH", "IL", "IL", "OH", "IN", …
## $ policy_csl <chr> "250/500", "250/500", "100/300", "250/500"…
## $ policy_deductable <dbl> 1000, 2000, 2000, 2000, 1000, 1000, 1000, …
## $ policy_annual_premium <dbl> 1406.91, 1197.22, 1413.14, 1415.74, 1583.9…
## $ umbrella_limit <dbl> 0e+00, 5e+06, 5e+06, 6e+06, 6e+06, 0e+00, …
## $ insured_zip <dbl> 466132, 468176, 430632, 608117, 610706, 47…
## $ insured_sex <chr> "MALE", "MALE", "FEMALE", "FEMALE", "MALE"…
## $ insured_education_level <chr> "MD", "MD", "PhD", "PhD", "Associate", "Ph…
## $ insured_occupation <chr> "craft-repair", "machine-op-inspct", "sale…
## $ insured_hobbies <chr> "sleeping", "reading", "board-games", "boa…
## $ insured_relationship <chr> "husband", "other-relative", "own-child", …
## $ capital_gains <dbl> 53300, 0, 35100, 48900, 66000, 0, 0, 0, 0,…
## $ capital_loss <dbl> 0, 0, 0, -62400, -46000, 0, -77000, 0, 0, …
## $ incident_date <date> 2015-01-25, 2015-01-21, 2015-02-22, 2015-…
## $ incident_type <chr> "Single Vehicle Collision", "Vehicle Theft…
## $ collision_type <chr> "Side Collision", "?", "Rear Collision", "…
## $ incident_severity <chr> "Major Damage", "Minor Damage", "Minor Dam…
## $ authorities_contacted <chr> "Police", "Police", "Police", "Police", "N…
## $ incident_state <chr> "SC", "VA", "NY", "OH", "NY", "SC", "NY", …
## $ incident_city <chr> "Columbus", "Riverwood", "Columbus", "Arli…
## $ incident_location <chr> "9935 4th Drive", "6608 MLK Hwy", "7121 Fr…
## $ incident_hour_of_the_day <dbl> 5, 8, 7, 5, 20, 19, 0, 23, 21, 14, 22, 21,…
## $ number_of_vehicles_involved <dbl> 1, 1, 3, 1, 1, 3, 3, 3, 1, 1, 1, 3, 1, 1, …
## $ property_damage <chr> "YES", "?", "NO", "?", "NO", "NO", "?", "?…
## $ bodily_injuries <dbl> 1, 0, 2, 1, 0, 0, 0, 2, 1, 2, 2, 1, 1, 1, …
## $ witnesses <dbl> 2, 0, 3, 2, 1, 2, 0, 2, 1, 1, 2, 2, 0, 1, …
## $ police_report_available <chr> "YES", "?", "NO", "NO", "NO", "NO", "?", "…
## $ total_claim_amount <dbl> 71610, 5070, 34650, 63400, 6500, 64100, 78…
## $ injury_claim <dbl> 6510, 780, 7700, 6340, 1300, 6410, 21450, …
## $ property_claim <dbl> 13020, 780, 3850, 6340, 650, 6410, 7150, 9…
## $ vehicle_claim <dbl> 52080, 3510, 23100, 50720, 4550, 51280, 50…
## $ auto_make <chr> "Saab", "Mercedes", "Dodge", "Chevrolet", …
## $ auto_model <chr> "92x", "E400", "RAM", "Tahoe", "RSX", "95"…
## $ auto_year <dbl> 2004, 2007, 2007, 2014, 2009, 2003, 2012, …
## $ fraud_reported <chr> "Y", "Y", "N", "Y", "N", "Y", "N", "N", "N…
## $ c39 <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## months_as_customer age
## 0 0
## policy_number policy_bind_date
## 0 0
## policy_state policy_csl
## 0 0
## policy_deductable policy_annual_premium
## 0 0
## umbrella_limit insured_zip
## 0 0
## insured_sex insured_education_level
## 0 0
## insured_occupation insured_hobbies
## 0 0
## insured_relationship capital_gains
## 0 0
## capital_loss incident_date
## 0 0
## incident_type collision_type
## 0 0
## incident_severity authorities_contacted
## 0 0
## incident_state incident_city
## 0 0
## incident_location incident_hour_of_the_day
## 0 0
## number_of_vehicles_involved property_damage
## 0 0
## bodily_injuries witnesses
## 0 0
## police_report_available total_claim_amount
## 0 0
## injury_claim property_claim
## 0 0
## vehicle_claim auto_make
## 0 0
## auto_model auto_year
## 0 0
## fraud_reported c39
## 0 1000
# Limpieza de los signos "?"
df <- df_raw %>%
mutate(
across(
.cols = where(is.character),
.fns = ~ na_if(., "?")
)
)
# Variables que se omiten del analisis
cols_drop <- c(
"policy_number",
"policy_bind_date",
"incident_date",
"incident_location",
"insured_zip",
"insured_hobbies",
"fraud_reported",
"c39"
)
df <- df %>% select(-all_of(cols_drop))
# Variables numericas y categoricas
numeric_vars <- df %>% select(where(is.numeric)) %>% names()
categorical_vars <- df %>% select(where(is.character)) %>% names()
numeric_vars## [1] "months_as_customer" "age"
## [3] "policy_deductable" "policy_annual_premium"
## [5] "umbrella_limit" "capital_gains"
## [7] "capital_loss" "incident_hour_of_the_day"
## [9] "number_of_vehicles_involved" "bodily_injuries"
## [11] "witnesses" "total_claim_amount"
## [13] "injury_claim" "property_claim"
## [15] "vehicle_claim" "auto_year"
## [1] "policy_state" "policy_csl"
## [3] "insured_sex" "insured_education_level"
## [5] "insured_occupation" "insured_relationship"
## [7] "incident_type" "collision_type"
## [9] "incident_severity" "authorities_contacted"
## [11] "incident_state" "incident_city"
## [13] "property_damage" "police_report_available"
## [15] "auto_make" "auto_model"
Usaremos distancia de Gower (función daisy()) para métodos basados en distancia (jerárquico aglomerativo, PAM/k-medoids).
Para k-means será necesario usar solo variables numéricas escaladas.
# Volver variables caracteres en factor
df <- df %>%
mutate(
across(
.cols = where(is.character),
.fns = as.factor
)
)
str(df)## tibble [340 × 32] (S3: tbl_df/tbl/data.frame)
## $ months_as_customer : num [1:340] 328 134 256 27 447 60 180 473 140 160 ...
## $ age : num [1:340] 48 29 39 33 61 23 38 58 31 37 ...
## $ policy_state : Factor w/ 3 levels "IL","IN","OH": 3 3 3 1 3 3 3 2 2 3 ...
## $ policy_csl : Factor w/ 3 levels "100/300","250/500",..: 2 1 2 1 1 3 2 1 3 3 ...
## $ policy_deductable : num [1:340] 1000 2000 1000 500 2000 500 2000 2000 500 500 ...
## $ policy_annual_premium : num [1:340] 1407 1413 1351 1443 1137 ...
## $ umbrella_limit : num [1:340] 0e+00 5e+06 0e+00 0e+00 0e+00 3e+06 0e+00 0e+00 6e+06 0e+00 ...
## $ insured_sex : Factor w/ 2 levels "FEMALE","MALE": 2 1 1 1 1 2 1 1 2 1 ...
## $ insured_education_level : Factor w/ 7 levels "Associate","College",..: 6 7 7 7 3 6 2 6 3 6 ...
## $ insured_occupation : Factor w/ 14 levels "adm-clerical",..: 3 12 13 8 4 11 7 14 7 3 ...
## $ insured_relationship : Factor w/ 6 levels "husband","not-in-family",..: 1 4 5 4 3 6 2 3 5 3 ...
## $ capital_gains : num [1:340] 53300 35100 0 0 0 0 41300 55700 53500 45500 ...
## $ capital_loss : num [1:340] 0 0 0 0 -51000 0 -55500 0 0 -37800 ...
## $ incident_type : Factor w/ 2 levels "Multi-vehicle Collision",..: 2 1 1 2 1 2 2 1 2 2 ...
## $ collision_type : Factor w/ 3 levels "Front Collision",..: 3 2 2 1 1 2 2 3 3 3 ...
## $ incident_severity : Factor w/ 3 levels "Major Damage",..: 1 2 1 3 1 3 3 1 3 3 ...
## $ authorities_contacted : Factor w/ 4 levels "Ambulance","Fire",..: 4 4 2 4 2 1 4 3 4 3 ...
## $ incident_state : Factor w/ 7 levels "NC","NY","OH",..: 5 2 5 7 5 5 5 7 7 2 ...
## $ incident_city : Factor w/ 7 levels "Arlington","Columbus",..: 2 2 1 1 7 4 7 3 4 5 ...
## $ incident_hour_of_the_day : num [1:340] 5 7 19 21 21 9 12 12 9 19 ...
## $ number_of_vehicles_involved: num [1:340] 1 3 3 1 3 1 1 4 1 1 ...
## $ property_damage : Factor w/ 2 levels "NO","YES": 2 1 1 1 2 2 1 2 1 2 ...
## $ bodily_injuries : num [1:340] 1 2 0 1 1 1 0 0 0 1 ...
## $ witnesses : num [1:340] 2 3 2 1 2 0 2 0 2 0 ...
## $ police_report_available : Factor w/ 2 levels "NO","YES": 2 1 1 2 2 1 2 1 2 1 ...
## $ total_claim_amount : num [1:340] 71610 34650 64100 27700 114920 ...
## $ injury_claim : num [1:340] 6510 7700 6410 2770 17680 ...
## $ property_claim : num [1:340] 13020 3850 6410 2770 17680 ...
## $ vehicle_claim : num [1:340] 52080 23100 51280 22160 79560 ...
## $ auto_make : Factor w/ 14 levels "Accura","Audi",..: 11 5 11 13 2 11 5 1 12 1 ...
## $ auto_model : Factor w/ 39 levels "3 Series","92x",..: 2 31 4 9 5 4 28 26 22 35 ...
## $ auto_year : num [1:340] 2004 2007 2003 2012 2006 ...
## Warning: `aes_string()` was deprecated in ggplot2 3.0.0.
## ℹ Please use tidy evaluation idioms with `aes()`.
## ℹ See also `vignette("ggplot2-in-packages")` for more information.
## ℹ The deprecated feature was likely used in the factoextra package.
## Please report the issue at <https://github.com/kassambara/factoextra/issues>.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
library(hopkins)
set.seed(123)
hopkins_value <- hopkins::hopkins(df_num_scaled, m = nrow(df_num_scaled) - 1)
hopkins_value## [1] 0.9946715
pca_res <- prcomp(df_num_scaled, scale. = FALSE)
fviz_pca_ind(pca_res,
geom = "point",
repel = TRUE)
## Paso 3 — Clustering Jerárquico Aglomerativo (AGNES)
## Call: agnes(x = gower_dist, method = "ward")
## Agglomerative coefficient: 0.8690064
## Order of objects:
## [1] 1 285 301 334 7 282 79 323 56 276 50 73 188 101 214 171 272 95
## [19] 195 241 103 153 117 191 197 235 179 319 156 185 169 4 206 267 320 17
## [37] 208 138 280 242 306 30 62 41 83 115 308 231 295 270 61 76 135 238
## [55] 119 204 274 139 335 218 271 9 281 64 78 102 300 173 202 14 182 89
## [73] 311 46 317 84 269 251 291 15 45 161 329 71 122 162 26 108 121 336
## [91] 114 257 60 287 113 127 6 189 63 80 110 22 196 112 177 338 12 210
## [109] 47 232 128 283 146 296 164 123 198 298 330 181 288 154 193 200 254 10
## [127] 327 315 339 18 107 325 32 33 133 65 88 305 314 20 245 233 332 34
## [145] 304 68 74 131 145 261 2 222 24 152 149 302 37 192 94 303 175 220
## [163] 259 318 31 228 278 90 104 249 299 125 203 186 223 207 266 3 86 21
## [181] 219 13 310 28 132 297 69 85 194 72 109 155 201 248 5 237 27 313
## [199] 38 293 40 59 216 292 331 55 286 140 157 44 229 290 316 187 212 289
## [217] 333 52 263 273 326 275 57 98 81 250 35 183 143 91 148 100 170 167
## [235] 209 11 324 166 199 215 42 165 96 144 262 150 163 264 77 244 258 268
## [253] 16 99 66 75 25 307 87 240 252 19 309 111 147 36 178 93 43 158
## [271] 39 260 236 213 225 49 234 70 126 247 340 58 246 168 151 227 53 190
## [289] 180 106 328 284 312 8 48 322 118 136 116 176 256 294 29 221 120 321
## [307] 211 255 51 184 97 230 23 92 337 217 243 277 129 279 134 141 54 137
## [325] 67 226 142 172 105 265 224 253 82 159 239 124 174 160 130 205
## Height (summary):
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.2383 0.3254 0.3921 0.4624 0.5207 2.5165
##
## Available components:
## [1] "order" "height" "ac" "merge" "diss" "call" "method"
# Metodo jerarquico
fviz_dend(hc_agnes,
k = NULL,
cex = 0.6,
main = "Dendrograma - AGNES (Ward)",
rect = FALSE)## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## ℹ The deprecated feature was likely used in the factoextra package.
## Please report the issue at <https://github.com/kassambara/factoextra/issues>.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## Warning: The `<scale>` argument of `guides()` cannot be `FALSE`. Use "none" instead as
## of ggplot2 3.3.4.
## ℹ The deprecated feature was likely used in the factoextra package.
## Please report the issue at <https://github.com/kassambara/factoextra/issues>.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
### Determinacion del numero optimo de clusters
# Usando Silhouette
fviz_nbclust(df, FUN = hcut, method = "silhouette",
diss = gower_dist) +
ggtitle("Número óptimo de clusters — Silhouette (AGNES)")## Warning in stats::dist(x, method = method, ...): NAs introducidos por coerción
## Warning in stats::dist(x, method = method, ...): NAs introducidos por coerción
## Warning in stats::dist(x, method = method, ...): NAs introducidos por coerción
## Warning in stats::dist(x, method = method, ...): NAs introducidos por coerción
## Warning in stats::dist(x, method = method, ...): NAs introducidos por coerción
## Warning in stats::dist(x, method = method, ...): NAs introducidos por coerción
## Warning in stats::dist(x, method = method, ...): NAs introducidos por coerción
## Warning in stats::dist(x, method = method, ...): NAs introducidos por coerción
## Warning in stats::dist(x, method = method, ...): NAs introducidos por coerción
# Visualizacion del Clustering
df_vis <- df %>%
select(where(is.numeric)) # o escoger un subconjunto manualmente
fviz_cluster(hc_final,
data = df_vis,
geom = "point",
pointsize = 2,
ellipse.type = "convex",
main = paste("Clusters AGNES con k =", k_opt))# Eleccion del numero de Clusters
fviz_nbclust(df_num_scaled, kmeans, method = "wss") +
ggtitle("Elbow Method — K-means")# Aplicar el k-means con 2 clusters
k_opt_km <- 2
set.seed(123)
km_res <- kmeans(df_num_scaled, centers = k_opt_km, nstart = 25)
km_res## K-means clustering with 2 clusters of sizes 146, 194
##
## Cluster means:
## months_as_customer age policy_deductable policy_annual_premium
## 1 0.2687356 0.2437651 0.1776577 0.010941947
## 2 -0.2022443 -0.1834521 -0.1337012 -0.008234661
## umbrella_limit capital_gains capital_loss incident_hour_of_the_day
## 1 -0.12202821 0.02857083 -0.12208120 -0.04360346
## 2 0.09183566 -0.02150176 0.09187554 0.03281498
## number_of_vehicles_involved bodily_injuries witnesses total_claim_amount
## 1 -0.05945138 0.03728160 0.11891325 0.8902618
## 2 0.04474176 -0.02805729 -0.08949141 -0.6699909
## injury_claim property_claim vehicle_claim auto_year
## 1 0.6929119 0.6476988 0.7934455 0.09357760
## 2 -0.5214697 -0.4874434 -0.5971291 -0.07042438
##
## Clustering vector:
## [1] 1 2 2 2 1 2 2 1 1 1 1 1 1 1 1 2 2 2 2 2 2 1 2 2 2 2 1 2 1 1 2 1 2 2 2 2 2
## [38] 1 2 1 1 2 2 2 2 1 1 1 2 1 1 1 2 2 1 1 1 2 1 1 1 1 1 1 2 2 2 2 1 2 2 2 1 2
## [75] 2 1 2 2 2 2 1 1 1 2 1 2 2 2 2 2 1 1 2 2 2 1 1 2 2 1 1 1 1 2 2 1 2 2 2 1 1
## [112] 1 1 2 1 1 2 1 2 2 2 1 2 2 2 2 1 1 1 2 1 1 2 2 1 2 2 2 2 1 2 1 2 1 1 1 1 2
## [149] 2 2 1 2 2 1 1 1 1 1 1 2 2 1 1 1 2 2 2 1 1 1 2 2 1 2 2 1 1 2 1 2 1 1 2 1 2
## [186] 2 2 1 2 2 1 1 1 1 2 2 2 2 1 1 1 1 2 2 2 2 2 2 2 1 1 1 2 1 1 2 2 2 1 2 2 2
## [223] 2 2 2 2 1 2 2 2 1 1 2 2 2 1 1 1 1 2 1 2 1 2 2 1 1 1 2 1 2 2 2 1 1 2 1 2 2
## [260] 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 1 1 2 2 1 2 1 2 1 1 2 2 1 1 2 1 2 2 1 2 2 2
## [297] 2 2 2 2 1 2 2 2 2 2 2 1 1 2 2 2 1 1 2 2 1 2 1 2 2 1 1 1 2 1 2 1 1 2 2 2 2
## [334] 1 2 1 2 2 2 2
##
## Within cluster sum of squares by cluster:
## [1] 2199.276 2573.411
## (between_SS / total_SS = 12.0 %)
##
## Available components:
##
## [1] "cluster" "centers" "totss" "withinss" "tot.withinss"
## [6] "betweenss" "size" "iter" "ifault"
# Visualizacion
fviz_cluster(km_res,
data = df_num_scaled,
geom = "point",
ellipse.type = "norm",
main = paste("K-means con k =", k_opt_km))