En este notebook voy a realizar un caso de análisis multivariante con kmeans para agrupar las actividades con diferentes características o perfile
Recordemos que estamos trabajando sobre un dataset de actividades de carrera de un atleta
act_data <- read.csv('act_data.csv')
# Selecciono todas las variables menos las relacionadas con la fecha
act_data_case <- act_data %>% select(Distancia, Calorias, Tiempo, FCMedia, Zancada, Ascenso, Velocidad)
act_data_caseact_data_norm <- scale(act_data_case)
# Vemos un resumen de como han quedado ahora los datos
head(act_data_norm)## Distancia Calorias Tiempo FCMedia Zancada Ascenso Velocidad
## [1,] -0.1996620 -0.008104384 -0.4183274 1.0842880 0.8535718 -0.75400942 0.8270334
## [2,] -2.1415920 -1.337813429 -1.7713653 1.9640520 -2.8125135 -1.37390028 -2.9672049
## [3,] -0.8753007 -0.518129771 -0.7357067 1.6441378 -0.5659213 0.13539921 -0.6322890
## [4,] -0.8625528 -1.732475931 -0.9027484 -0.2753473 0.1397577 -0.91572008 0.1946604
## [5,] -0.4503707 -0.645636118 -0.5040271 0.1245454 0.1916324 0.08149565 0.1946604
## [6,] -0.4503707 -0.451340732 -0.6507333 0.8443523 0.8630125 0.29710987 0.8464911
# Fijo semilla dada la aleatoriedad de kmeans
set.seed(123)
# Lanzo para un centroide
act_data_km <- (kmeans(act_data_norm, centers = 1)$betweenss)/
(kmeans(act_data_norm, centers = 1)$totss)
# Itero para más centroides y poder ver la curva de progreso y valorar
for(i in 2:10) act_data_km[i] <-
(kmeans(act_data_norm, centers = i)$betweenss)/(kmeans(act_data_norm, centers = 1)$totss)
# Grafico la curva
plot(1:10, act_data_km, type="b", xlab = "número de clusters",
ylab = "% de varianza explicada")Vemos que con la gráfica del porcentaje de la varianza explicada, no vemos ningún “codo” o cambio de inclinación reseñable que nos haga decidirnos de forma relativamente objetiva sobre el número de clusters a usar, por lo que opto por hacerlo subjetivamente analizando bajo mi criterio los resultados de la tabla y de una gráfica que muestro.
# Pongo semilla de nuevo
set.seed(123)
# Calculo 4 clusters
act_data_km_4 <- kmeans(act_data_norm, centers = 4)
# Creo los grupos agrupados por la media y los muestro
grupos <- aggregate(act_data_case, by = list(act_data_km_4$cluster), mean)
kable(grupos, col.names = c("cluster","Distancia","Calorias","Tiempo","FCMedia","Zancada","Ascenso","Velocidad"))| cluster | Distancia | Calorias | Tiempo | FCMedia | Zancada | Ascenso | Velocidad |
|---|---|---|---|---|---|---|---|
| 1 | 4.436622 | 259.2838 | 28.33608 | 133.3919 | 0.9308382 | 35.56757 | 9.428649 |
| 2 | 10.586750 | 683.2625 | 60.80087 | 152.5125 | 1.0259838 | 110.43750 | 10.510375 |
| 3 | 7.113102 | 459.9572 | 42.69107 | 146.9465 | 0.9809167 | 70.25668 | 10.012086 |
| 4 | 6.294672 | 387.3443 | 32.76344 | 149.6066 | 1.1398865 | 48.70492 | 11.586967 |
fviz_cluster(act_data_km_4, data = act_data_case,
palette = "Set2",
geom = "point",
ellipse.type = "convex",
ggtheme = theme_bw()
)Tras evaluarlo y decidir entre 3 y 4 clusters, me decido por 4 ya que se crean perfiles de carrera bien remarcados. 3 clusteres también habría sido una opción correcta.
Clasificos los resultados: - Custer 1: Distancias cortas con media menor de 5k, de menos de 30 minutos de carreta con un consumo bajo de calorias, poco ascenso (posible llanos) y velocidad cercana a 9.5 kmh. Frecuencia media baja, sobre 130. Podrían ser actividades de calentamiento o entrenamientos en época de lesiones para probar. - Cluster 2: Distancias largas, con la media por encima de 10Km, con alto consumo de calorias, casi 700. Duración aproximada de 1 hora por sesión, con una zancada de un metro y ascenso total por encima de 100m. La velocidad ronda los 10.5 kmh. Entrenos más serios, incluso alguna carrera. - Clústeres 3 y 4: Los comparo porque comparten distancias similares, entre 6 y 7, pero con distintas características. En el cluster 4 la velocidad y zancada es mayor y el ascenso y el tiempo menor, lo que hace indicar que son tipos de actividades similares. Pero el cluster 3, más “tranquilo”, pueden ser entrenos, y en el 4 se agrupen entrenos más exigentes, con terreno más llano, y revisando la velocidad, incluso competitivas.
# Calculo los centroides
centroides <- fitted(act_data_km_4)
# Calculo las distancias
distancias <- sqrt(rowSums((act_data_case - centroides)^2))
# Cuento los outliers
length(boxplot.stats(distancias)$out)## [1] 12
# Calculo porcentaje de outliers
round(length(boxplot.stats(distancias)$out) / nrow(act_data_case), 3)## [1] 0.026
Nos aparecen 12 outliers, que suponen un 2.6% de los registros, lo cual puedo considerar totalmente asumible en el modelo.