Para comenzar este análisis de datos, importamos la librería ggplot; esto es para poder visualizar de mejor manera los gráficos.
Los datos son sobre los arrestos por 100,000 ciudadanos en Estados Unidos por asalto, homicidios y secuestros en 50 estados en 1973. Este dato corresponde al porcentaje de la población que vive en zonas urbanas.
Se implementa la función scale, una herramienta diseñada para centrar y escalar las columnas de un dataframe numérico. Este funciona con la siguiente fórmula.
\[ z = \frac{x - \bar{x}}{s} \]
Donde:
Es decir, los datos están “centrados” en el origen.
Ya que están centrados, calcula la desviación estándar de los datos. Divide cada número entre la desviación y haz la dispersión de los datos uniformemente (unidad de medida igual a 1).
También la función devuelve lo siguiente:
scaled:center: El valor que se usó para restar (la media).
scaled:scale: El valor que se usó para dividir (la desviación estándar).
library(ggplot2)
data("USArrests")
# It is recommended to scale the data
usarrests_scaled <- scale(USArrests)
print(usarrests_scaled)
## Murder Assault UrbanPop Rape
## Alabama 1.24256408 0.78283935 -0.52090661 -0.003416473
## Alaska 0.50786248 1.10682252 -1.21176419 2.484202941
## Arizona 0.07163341 1.47880321 0.99898006 1.042878388
## Arkansas 0.23234938 0.23086801 -1.07359268 -0.184916602
## California 0.27826823 1.26281442 1.75892340 2.067820292
## Colorado 0.02571456 0.39885929 0.86080854 1.864967207
## Connecticut -1.03041900 -0.72908214 0.79172279 -1.081740768
## Delaware -0.43347395 0.80683810 0.44629400 -0.579946294
## Florida 1.74767144 1.97077766 0.99898006 1.138966691
## Georgia 2.20685994 0.48285493 -0.38273510 0.487701523
## Hawaii -0.57123050 -1.49704226 1.20623733 -0.110181255
## Idaho -1.19113497 -0.60908837 -0.79724965 -0.750769945
## Illinois 0.59970018 0.93883125 1.20623733 0.295524916
## Indiana -0.13500142 -0.69308401 -0.03730631 -0.024769429
## Iowa -1.28297267 -1.37704849 -0.58999237 -1.060387812
## Kansas -0.41051452 -0.66908525 0.03177945 -0.345063775
## Kentucky 0.43898421 -0.74108152 -0.93542116 -0.526563903
## Louisiana 1.74767144 0.93883125 0.03177945 0.103348309
## Maine -1.30593210 -1.05306531 -1.00450692 -1.434064548
## Maryland 0.80633501 1.55079947 0.10086521 0.701231086
## Massachusetts -0.77786532 -0.26110644 1.34440885 -0.526563903
## Michigan 0.99001041 1.01082751 0.58446551 1.480613993
## Minnesota -1.16817555 -1.18505846 0.03177945 -0.676034598
## Mississippi 1.90838741 1.05882502 -1.48810723 -0.441152078
## Missouri 0.27826823 0.08687549 0.30812248 0.743936999
## Montana -0.41051452 -0.74108152 -0.86633540 -0.515887425
## Nebraska -0.80082475 -0.82507715 -0.24456358 -0.505210947
## Nevada 1.01296983 0.97482938 1.06806582 2.644350114
## New Hampshire -1.30593210 -1.36504911 -0.65907813 -1.252564419
## New Jersey -0.08908257 -0.14111267 1.62075188 -0.259651949
## New Mexico 0.82929443 1.37080881 0.30812248 1.160319648
## New York 0.76041616 0.99882813 1.41349461 0.519730957
## North Carolina 1.19664523 1.99477641 -1.41902147 -0.547916860
## North Dakota -1.60440462 -1.50904164 -1.48810723 -1.487446939
## Ohio -0.11204199 -0.60908837 0.65355127 0.017936483
## Oklahoma -0.27275797 -0.23710769 0.16995096 -0.131534211
## Oregon -0.66306820 -0.14111267 0.10086521 0.861378259
## Pennsylvania -0.34163624 -0.77707965 0.44629400 -0.676034598
## Rhode Island -1.00745957 0.03887798 1.48258036 -1.380682157
## South Carolina 1.51807718 1.29881255 -1.21176419 0.135377743
## South Dakota -0.91562187 -1.01706718 -1.41902147 -0.900240639
## Tennessee 1.24256408 0.20686926 -0.45182086 0.605142783
## Texas 1.12776696 0.36286116 0.99898006 0.455672088
## Utah -1.05337842 -0.60908837 0.99898006 0.178083656
## Vermont -1.28297267 -1.47304350 -2.31713632 -1.071064290
## Virginia 0.16347111 -0.17711080 -0.17547783 -0.056798864
## Washington -0.86970302 -0.30910395 0.51537975 0.530407436
## West Virginia -0.47939280 -1.07706407 -1.83353601 -1.273917376
## Wisconsin -1.19113497 -1.41304662 0.03177945 -1.113770203
## Wyoming -0.22683912 -0.11711392 -0.38273510 -0.601299251
## attr(,"scaled:center")
## Murder Assault UrbanPop Rape
## 7.788 170.760 65.540 21.232
## attr(,"scaled:scale")
## Murder Assault UrbanPop Rape
## 4.355510 83.337661 14.474763 9.366385
Cada valor es diferente y no importa si es positivo o negativo, ya que por cada estado hay una diferente densidad poblacional; es decir, toma una media en general y puede haber menos asesinatos en un estado que en otro, como por ejemplo
max(usarrests_scaled)
## [1] 2.64435
Nevada, que tiene 2.64435, a New Hampshire, que tiene -1.252564419. Estos tienen bastante diferencia debido a que New Hampshire está por debajo de la media.
Ya que se comprendieron estos datos, podemos hacer K-means, método que divide las observaciones en k grupos de tal manera que las observaciones dentro de un mismo grupo sean lo más similares posible, y las de grupos diferentes sean lo más distintas posible.
En este caso hacemos k-means escalado debido a que es un algoritmo basado puramente en geometría y distancias. K-means usa la distancia euclidiana:
\[ d(p, q) = \sqrt{\sum_{i=1}^{n} (p_i - q_i)^2} \]
Esto es que, si una variable tiene un rango de 0 a 1,000,000 y otra de 0 a 10, como por ejemplo de 0 a 1,000,000 (asesinatos en un estado) y otra de 0 a 10 (secuestros), la variable de asesinatos dominará completamente el cálculo.
La diferencia de unos pocos miles de asesinados pesará más que toda la escala de educación, haciendo que el algoritmo ignore por completo la segunda variable.
# Now, let´s run the K-means
set.seed(123)
km_sc <- kmeans(usarrests_scaled, centers = 3, nstart = 25)
km_sc$cluster
## Alabama Alaska Arizona Arkansas California
## 1 1 1 3 1
## Colorado Connecticut Delaware Florida Georgia
## 1 3 3 1 1
## Hawaii Idaho Illinois Indiana Iowa
## 3 2 1 3 2
## Kansas Kentucky Louisiana Maine Maryland
## 3 2 1 2 1
## Massachusetts Michigan Minnesota Mississippi Missouri
## 3 1 2 1 1
## Montana Nebraska Nevada New Hampshire New Jersey
## 2 2 1 2 3
## New Mexico New York North Carolina North Dakota Ohio
## 1 1 1 2 3
## Oklahoma Oregon Pennsylvania Rhode Island South Carolina
## 3 3 3 3 1
## South Dakota Tennessee Texas Utah Vermont
## 2 1 1 3 2
## Virginia Washington West Virginia Wisconsin Wyoming
## 3 3 2 2 3
library(ggplot2)
data("USArrests")
set.seed(123)
km_sc2 <- kmeans(USArrests, centers = 3, nstart = 25)
km_sc2$cluster
## Alabama Alaska Arizona Arkansas California
## 1 1 1 3 1
## Colorado Connecticut Delaware Florida Georgia
## 3 2 1 1 3
## Hawaii Idaho Illinois Indiana Iowa
## 2 2 1 2 2
## Kansas Kentucky Louisiana Maine Maryland
## 2 2 1 2 1
## Massachusetts Michigan Minnesota Mississippi Missouri
## 3 1 2 1 3
## Montana Nebraska Nevada New Hampshire New Jersey
## 2 2 1 2 3
## New Mexico New York North Carolina North Dakota Ohio
## 1 1 1 2 2
## Oklahoma Oregon Pennsylvania Rhode Island South Carolina
## 3 3 2 3 1
## South Dakota Tennessee Texas Utah Vermont
## 2 3 3 2 2
## Virginia Washington West Virginia Wisconsin Wyoming
## 3 3 2 2 3
Mientras que los estados escalados de KANSAS, CONNECTICUT, DELAWERE, PENNSYLVANIA, etc., fueron designados al clúster #tres (3), los datos no escalados agruparon a KANSAS, CONNECTICUT, PENNSYLVANIA en el grupo dos (2), y a DELAWERE en el grupo uno (1).
km_sc$centers #SCALED
## Murder Assault UrbanPop Rape
## 1 1.0049340 1.0138274 0.1975853 0.8469650
## 2 -0.9615407 -1.1066010 -0.9301069 -0.9667633
## 3 -0.4469795 -0.3465138 0.4788049 -0.2571398
km_sc2$centers #NOT SCALED
## Murder Assault UrbanPop Rape
## 1 11.812500 272.5625 68.31250 28.37500
## 2 4.270000 87.5500 59.75000 14.39000
## 3 8.214286 173.2857 70.64286 22.84286
El algoritmo selecciona una k, inicializa los k por centroides y asigna cada observación al centroide más cercano y así va recalculando, repitiéndolo hasta que no cambie mucho la distancia. En este caso, el grupo 3 fue el que más estuvo uniforme dentro de los 4 delitos; estados como Arkansas, Oregón o Pensilvania son los que tienen estos 4 delitos de manera recurrente.
Ya que hicimos el k-means para agrupar los datos en 3 categorías, hacemos el Within-Cluster Sum of Squares o, por sus siglas, WCSS; este nos puede ayudar a contestar la pregunta acerca de ¿en cuántos grupos debo dividir mis datos? Siendo así una medida de cohesión. Nos dice qué tan “apretaditos” o compactos están los puntos dentro de cada clúster.
\[ WCSS = \sum_{i=1}^{k} \sum_{x \in C_i} \|x - \mu_i\|^2 \]
Ya con esto graficamos por medio de la gráfica de tipo Elbow Method; esta gráfica es una técnica visual que usamos para encontrar el equilibrio perfecto entre tener un WCSS bajo y no tener demasiados clústeres.
wss_sc = numeric(10)
for (k in 1:10) {
set.seed(123)
wss_sc[k] <- kmeans(usarrests_scaled, centers = k, nstart = 25)$tot.withinss
}
plot(1:10, wss_sc, type = "b", pch = 19,
xlab = "Number of clusters (k) ",
ylab = "Total within-cluster sum of squares",
main = "Elbow Method for K-means - SCALED")
wss_sc = numeric(10)
for (k in 1:10) {
set.seed(123)
wss_sc[k] <- kmeans(USArrests, centers = k, nstart = 25)$tot.withinss
}
plot(1:10, wss_sc, type = "b", pch = 19,
xlab = "Number of clusters (k)",
ylab = "Total within-cluster sum of squares ",
main = "Elbow Method for K-means -NON SCALED")
Notamos que la gráfica cae y tiene un punto de inflexión haciendo la
forma de un codo. Esto se puede interpretar como que antes del codo
añade un clúster que reduce significativamente el error. Después del
codo, si añadimos más clústeres, solo reduce el error de forma
significativa (esto es sobreajustar).
El WCSS es la métrica de calidad, y la gráfica nos sirve para tomar una decisión para elegir el valor óptimo de 𝑘.
Los datos escalados se ven mejor distribuidos en la gráfica, con un codo menos pronunciado y valores WSS menores.
Ahora pasaremos a analizar estos datos con PCA:
Los principales componentes de análisis son una técnica de reducción de dimensiones que identifica direcciones, o los denominados componentes principales, que son combinaciones lineales de las variables originales.
Matemáticamente, lo que hace es lo siguiente: I. El primer componente principal es la dirección en el espacio a lo largo de la cual las proyecciones presentan la mayor varianza. II. El segundo componente principal es la dirección que maximiza la varianza entre todas las direcciones ortogonales a la primera. III. El k-ésimo componente es la dirección que maximiza la varianza y es ortogonal a los k − 1 componentes anteriores.
Básicamente, es una técnica para simplificar datos complejos sin perder la información más importante.
# ------------------------------------------
# Now let´s work the PCA on this dataset
# ------------------------------------------
# Note that we use scale = TRUE because variables are on different scales
pca <- prcomp(USArrests, scale. = TRUE)
# Summary
summary(pca)
## Importance of components:
## PC1 PC2 PC3 PC4
## Standard deviation 1.5749 0.9949 0.59713 0.41645
## Proportion of Variance 0.6201 0.2474 0.08914 0.04336
## Cumulative Proportion 0.6201 0.8675 0.95664 1.00000
# ------------------------------------------
# Now let´s work the PCA on this dataset
# ------------------------------------------
pca2 <- prcomp(USArrests)
# Summary
summary(pca2)
## Importance of components:
## PC1 PC2 PC3 PC4
## Standard deviation 83.7324 14.21240 6.4894 2.48279
## Proportion of Variance 0.9655 0.02782 0.0058 0.00085
## Cumulative Proportion 0.9655 0.99335 0.9991 1.00000
El scree plot compara las PCAs de los datos y nos muestra cómo se proyecta la proporción de varianzas para cuatro variables.
Se puede complementar con la tabla de importancia de los componentes vistos previamente, que nos indica cómo la variable PCE1 explica el 62% de los arrestos en Estados Unidos y el PCO2 un 24% de los arrestos (en conjunto, 88%). Los otros valores porcentuales parecen ser bastante insignificantes. En otras palabras, nos indica que la naturaleza de los datos cambia en su mayoría en dirección a estas variables.
# Scree plot
plot(pca, type = "l", main = "Scree Plot SCALED")
# Variance explained
var_explained <- pca$sdev^2 / sum(pca$sdev^2)
# PCA Variance plotted
plot(var_explained, type = "b", pch = 19,
xlab = "Principal Component",
ylab = "Proportion of Variance Explained",
main = "Variance Explained by Principal Components SCALED")
## NOT SCALED
# Scree plot
plot(pca2, type = "l", main = "Scree Plot- not SCALED")
# Variance explained
var_explained <- pca2$sdev^2 / sum(pca2$sdev^2)
# PCA Variance plotted
plot(var_explained, type = "b", pch = 19,
xlab = "Principal Component",
ylab = "Proportion of Variance Explained",
main = "Variance Explained by Principal Components -not SCALED")
La proporción acumulativa de 0.8675 dice que si usamos PC1 y PC2 juntos, se mantiene el 86.75% de la información original.
Se observa que se disparan los valores en el eje Y de 0 a 7000. Esto reafirma que nuestros datos originales tienen mucha diferencia de varianza entre ellos.
En general, el codo en el scree plot parece estar en el componente 2, y nos da un buen acercamiento visual para rectificar que, a partir del componente 3, la ganancia de información es muy pequeña.
Las gráficas de proporción son muy similares al scree plot; solo cambia el uso de eigenvalores.
Para aquellos datos escalados en el eje Y, tenemos la varianza en relación a 1, mostrándose la dominancia del PC1 con un valor de 2.5 y con la posibilidad de aún usar PC2.
Para los datos no escalados, la varianza de PC1 en relación a los valores de PC2 se lanza hasta 7000.
# PCA scores plot
plot(pca$x[,1], pca$x[,2],
pch = 19,
xlab = "PC1",
ylab = "PC2",
main = "PCA of USArrests")
text(pca$x[,1], pca$x[,2], labels = rownames(USArrests), pos = 3, cex = 0.7)
# ------------------------------------------
# Cool approach: K-means clusters on PCA space
# ------------------------------------------
plot(pca$x[,1], pca$x[,2],
col = km_sc$cluster,
pch = 19,
xlab = "PC1",
ylab = "PC2",
main = "K-means Clusters Visualized on PCA Space")
text(pca$x[,1], pca$x[,2], labels = rownames(USArrests), pos = 3, cex = 0.7)
Como ya lo observamos en la gráfica de proporción, la gráfica de tipo mapa de posicionamiento nos arroja que los estados en el espacio de componentes principales nos dan una estructura clara.
Gracias a esto podemos ver los grupos geográficos y cómo la alta tasa de arrestos se diferencia por estado.
# PCA scores plot
plot(pca2$x[,1], pca2$x[,2],
pch = 19,
xlab = "PC1",
ylab = "PC2",
main = "PCA of USArrests")
text(pca2$x[,1], pca2$x[,2], labels = rownames(USArrests), pos = 3, cex = 0.7)
# ------------------------------------------
# Cool approach: K-means clusters on PCA space
# ------------------------------------------
plot(pca2$x[,1], pca2$x[,2],
col = km_sc2$cluster,
pch = 19,
xlab = "PC1",
ylab = "PC2",
main = "K-means Clusters Visualized on PCA Space")
text(pca2$x[,1], pca2$x[,2], labels = rownames(USArrests), pos = 3, cex = 0.7)
Esto ocurre porque toma los valores de las variables como el número de crímenes cometidos en vez de evaluarlos en relación a su promedio por estado.
Observamos esto al comparar las gráficas obtenidas con datos escalados vs. aquellos sin escalar.
library(ggplot2)
pca_df <- data.frame(
PC1 = pca$x[,1],
PC2 = pca$x[,2],
Cluster = factor(km_sc$cluster),
State = rownames(USArrests)
)
ggplot(pca_df, aes(x = PC1, y = PC2, color = Cluster, label = State)) +
geom_point(size = 3) +
geom_text(vjust = -0.5, size = 3) +
labs(
title = "K-means Clusters in PCA Space",
x = "Principal Component 1",
y = "Principal Component 2"
) +
theme_minimal(base_size = 14)
library(ggplot2)
pca_df <- data.frame(
PC1 = pca2$x[,1],
PC2 = pca2$x[,2],
Cluster = factor(km_sc2$cluster),
State = rownames(USArrests)
)
ggplot(pca_df, aes(x = PC1, y = PC2, color = Cluster, label = State)) +
geom_point(size = 3) +
geom_text(vjust = -0.5, size = 3) +
labs(
title = "K-means Clusters in PCA Space",
x = "Principal Component 1",
y = "Principal Component 2"
) +
theme_minimal(base_size = 14)
Para concluir, la gráfica de tipo mapa de posicionamiento y la librería ggplot nos permiten una mejor visualización del gráfico. El Cluster 1 agrupa estados críticos con muchos delitos, el Cluster 2 identifica los estados con mayor seguridad y el Cluster 3 representa un punto medio.