Primero cargo las librerías necesarias, cargo la base de datos y convierto las variables categóricas a factor.
library(FactoMineR)
library(factoextra)
library(tidyverse)
library(psych)
library(corrplot)
library(ggpubr)
library(ggpointdensity)
library(NbClust)
library(GPArotation)
library(hrbrthemes)
library(kableExtra)
library(viridis)
library(gridExtra)
library(ggforce)
#Cargo la base de datos
data <- read.csv("~/Downloads/music_genre.csv")
#convierto variables
data$artist_name <- as.factor(data$artist_name)
data$music_genre <- as.factor(data$music_genre)
Vemos qué niveles tiene la variable categórica de género musical:
levels(data$music_genre)
## [1] "" "Alternative" "Anime" "Blues" "Classical"
## [6] "Country" "Electronic" "Hip-Hop" "Jazz" "Rap"
## [11] "Rock"
Elijo las variables numericas de interes, y armamos un dataframe agregandole tambien genero, popularidad y nombre de artista y cancion. Luego descarto las observaciones con NAs:
data_num <- data%>%
select(acousticness, danceability, energy,
instrumentalness, loudness,
speechiness, valence, music_genre, popularity, artist_name, track_name)
data_num_compl <- na.omit(data_num)
dim(data_num_compl)
## [1] 50000 11
Vemos que nos queda una base de datos de 50000 canciones. Cuan correlacionadas estan las variables numericas elegidas? Realizo primero un corrplot para analizarlo:
corrplot(cor(data_num_compl[,1:7]), method="ellipse", order="AOE", type="upper")
Y complementamos esto con los tests para analizar correlacion: Test de esfericidad de Bartlett y el determinante indican alta correlacion entre variables.
cortest.bartlett(cor(data_num_compl[,1:7]), n=nrow(data_num_compl))
## $chisq
## [1] 165493.4
##
## $p.value
## [1] 0
##
## $df
## [1] 21
det(cor(data_num_compl[,1:7]))
## [1] 0.0365109
Primero vamos a reducir dimensiones realizando Análisis de Componentes Principales (Principal component analysis, PCA)
pca <- princomp(data_num_compl[,1:7],cor=T)
Realizando el screeplot, vemos que los 2 primeros componentes explican aproximadamente el 64% de la variabilidad (PC1 el 48.4%):
fviz_screeplot(pca, addlabels=T)
Intentemos interpretar los componentes; para ello, veamos los loadings y realicemos un biplot:
pca$loadings
##
## Loadings:
## Comp.1 Comp.2 Comp.3 Comp.4 Comp.5 Comp.6 Comp.7
## acousticness 0.452 0.275 0.153 0.242 0.173 0.737 0.256
## danceability -0.315 0.545 0.277 -0.133 -0.686 0.182
## energy -0.472 -0.322 -0.231 0.192 0.202 0.733
## instrumentalness 0.346 0.107 -0.907 0.149 -0.126
## loudness -0.486 -0.225 0.614 -0.567
## speechiness -0.169 0.633 -0.634 -0.207 0.351
## valence -0.299 0.252 0.687 0.577 -0.110 -0.156
##
## Comp.1 Comp.2 Comp.3 Comp.4 Comp.5 Comp.6 Comp.7
## SS loadings 1.000 1.000 1.000 1.000 1.000 1.000 1.000
## Proportion Var 0.143 0.143 0.143 0.143 0.143 0.143 0.143
## Cumulative Var 0.143 0.286 0.429 0.571 0.714 0.857 1.000
fviz_pca_var(pca)
Vemos que en el componente 1, contribuyen la “acusticidad” e “instrumentalidad” (y negativamente, el volumen y la energía). Es decir, de alguna forma refleja cuán “tranquila” es una canción. En cuanto al componente 2, contribuyen positivamente la “bailabilidad”, “speechiness” y la valencia. O sea, cuán “alegre” y cuán “rapeada” es la canción.
Hecho ésto, podemos empezar a comparar géneros. Realicemos un gráfico de dispersión de PC1 vs PC2 comparando música clásica y rock:
data_num_compl$PC1 <- pca$scores[,1]
data_num_compl$PC2 <- pca$scores[,2]
data_num_compl %>% filter(music_genre%in%c("Classical", "Rock"))%>%
ggplot(aes(PC1,PC2, col=music_genre))+geom_point(alpha=0.6)+
ggtitle("PCA: música por género: Clásica y Rock")+
scale_color_discrete("Género", labels=c("Clasica", "Rock"), type=c("dodgerblue", "red"))+
theme_ipsum()
Vemos que parecen separarse bastante los dos géneros, sobre todo en PC1: el rock es más energético y fuerte que la música clásica, que al mismo tiempo tiende a ser más instrumental y acústica que el rock. Hay un grupo de temas de “música clásica” que tienen PC1 muy bajo, enseguida los analizaremos. Escuchemos algunos ejemplos extremos:
#Vemos los PC1 más altos en Clásica:
data_num_compl %>%filter(music_genre=="Classical")%>%
arrange(-PC1)%>%select(PC1, artist_name,track_name)%>%head()
El 2do PC1 más alto entre las obras clásicas es el archiconocido “Claro de Luna” de Beethoven: acústico, instrumental, para nada alegre!
knitr::include_url("https://open.spotify.com/embed/track/630zE7N1xPxdK2Zz4S3rPA", height = "380")
Veamos ahora las obras clásicas con menor score en PC1:
data_num_compl %>% filter(music_genre=="Classical")%>%
arrange(PC1)%>%select(PC1, artist_name,track_name)%>%head()
knitr::include_url("https://open.spotify.com/embed/track/2oQmcRQwiaijDjxCGe1YqL", height = "380")
Momento! No son obras de música clásica!!! La etiqueta de género en estos casos está mal aplicada, con lo que se explican los valores bajos en PC1. Veamos una lista de todos los temas de “música clásica” con un score de PC1 menor a 0:
data_num_compl %>% filter(music_genre=="Classical"&PC1<0)%>%
arrange(PC1)%>%select(PC1, artist_name,track_name)
Todos temas de hard rock o metal. Tendremos en cuenta esto para más adelante. Veamos extremos en PC1 en Rock:
data_num_compl %>%filter(music_genre=="Rock")%>%
arrange(PC1)%>%select(PC1, artist_name, track_name)%>%head()
data_num_compl %>%filter(music_genre=="Rock")%>%
arrange(-PC1)%>%select(PC1, artist_name, track_name)%>%head()
2 ejemplos claros: PC1 bajo, New Sensation de INXS (fuerte, para nada acústica, energética, alegre):
knitr::include_url("https://open.spotify.com/embed/track/2xcrseImDFEf8Urommws03", height = "380")
PC1 alto, Finding it There de Goldmund (instrumental de piano, lento):
knitr::include_url("https://open.spotify.com/embed/track/5G9rra29bm2HtXcyDtRf1y", height = "380")
Y PC2?
data_num_compl %>%filter(music_genre=="Classical")%>%
arrange(-PC2)%>%select(PC2, artist_name, track_name)%>%head()
2do score de PC2 más alto en clásica es un fragmento hablado de una opereta de Mozart:
data_num_compl %>%filter(music_genre=="Classical")%>%
arrange(-PC2)%>%select(PC2, artist_name, track_name)%>%head()
knitr::include_url("https://open.spotify.com/embed/track/3gQo5jtJoBOQJNjdTZsuBr", height = "380")
El menor PC2 en “Clásica” es de nuevo un tema de rock, For the Love of God de Steve Vai: instrumental, nada bailable y no muy alegre.
data_num_compl %>%filter(music_genre=="Classical")%>%
arrange(PC2)%>%select(PC2, artist_name, track_name)%>%head()
knitr::include_url("https://open.spotify.com/embed/track/5m5I3KcV83zf2p2qbsnr3y", height = "380")
Los dos temas con mayor score en PC2 de “rock” son en realidad temas de reggae: alegres, bailables.
data_num_compl %>%filter(music_genre=="Rock")%>%
arrange(-PC2)%>%select(PC2, artist_name, track_name)%>%head()
knitr::include_url("https://open.spotify.com/embed/track/7vp89zcbYncoBCbX5HA3tZ", height = "380")
Temas de rock con PC2 más bajos:
data_num_compl %>%filter(music_genre=="Rock")%>%
arrange(PC2)%>%select(PC2, artist_name, track_name)%>%head()
Holy Mountains de System of a Down: claramente de valencia baja y nada bailable!!!
knitr::include_url("https://open.spotify.com/embed/track/4mj2UMyJTBTaO7pffAK29j", height = "380")
Ahora analicemos los dos primeros componentes de PCA en canciones de Jazz vs Hip Hop:
data_num_compl%>%filter(music_genre%in%c("Hip-Hop", "Jazz"))%>%
ggplot(aes(PC1,PC2, col=music_genre))+geom_point(alpha=0.6)+
ggtitle("PCA: música por género: Hip-Hop y Jazz")+
scale_color_discrete("Género", labels=c("Hip-Hop", "Jazz"), type=c("violet", "darkgreen"))+
theme_ipsum()
Pasa algo muy parecido que entre Rock y Clásica: los temas de Jazz se acumulan en valores altos de PC1, los temas de Hip Hop en valores bajos de PC1 y altos de PC2 (rapeados, bailables, nada acústicos!) Veamos ejemplos:
data_num_compl %>%filter(music_genre=="Jazz")%>%
arrange(-PC1)%>%select(PC1, artist_name, track_name)%>%head()
Nada más representativo del “jazz tranquilo” que Bill Evans!
knitr::include_url("https://open.spotify.com/embed/track/1qTNMWo0Al8AfAX6aPGo6k", height = "380")
Vamos a la otra punta de PC1: busquemos un hip-hop bien “hiphopero”:
data_num_compl %>%filter(music_genre=="Hip-Hop")%>%
arrange(PC1)%>%select(PC1, artist_name, track_name)%>%head()
knitr::include_url("https://open.spotify.com/embed/track/1xMPwgmLHVXkIFYL0rvGiZ", height = "380")
Busquemos ahora algo no tan definido: Jazz con scores bajos de PC1…
data_num_compl %>%filter(music_genre=="Jazz")%>%
arrange(PC1)%>%select(PC1, artist_name, track_name)%>%head()
Bambous de Caravan Palace, para nada acústico (electrónico!), muy energético, casi que no parece Jazz, tiene el menor valor de PC1.
knitr::include_url("https://open.spotify.com/embed/track/3UQ4lYxFMVf31oLCkhI8dB", height = "380")
Comparemos ahora Blues y Electrónica… Deberían ser muy diferentes no?
data_num_compl%>%filter(music_genre%in%c("Electronic", "Blues"))%>%
ggplot(aes(PC1,PC2, col=music_genre))+geom_point(alpha=0.3)+
ggtitle("PCA: música por género: Electrónica y Blues")+
scale_color_discrete("Género", labels=c("Blues", "Electronica"), type=c("yellowgreen", "black"))+
theme_ipsum()
Vemos como ahora es mucho más dificil diferenciar un tema entre Blues y Electrónica por estos dos primeros componentes del PCA (están “amontonados” en valores medios de ambos componentes con mucha superposición de sus valores). Analicemos algunos “outliers”; hay un conjunto de 7 “temas” de Blues con altísimo score de PC2:
data_num_compl %>%filter(music_genre=="Blues")%>%
arrange(-PC2)%>%select(PC2, speechiness, artist_name, track_name)%>%head(7)
Son todos tracks hablados! Altísimo score en PC2 por altos valores de “speechiness”.
knitr::include_url("https://open.spotify.com/embed/track/5P90rwxH5I7LiwOg3cD7Yc", height = "380")
También hay algunos temas de Electrónica con valores extremadamente altos en PC1:
data_num_compl %>%filter(music_genre=="Electronic")%>%
arrange(-PC1)%>%select(PC1, artist_name, track_name)%>%head()
El tema con valor más alto de PC1 es música orquestal, instrumental, acústica, para nada la típica música electrónica..
knitr::include_url("https://open.spotify.com/embed/track/5OfjcALT0C7eVbauM0iB3e", height = "380")
Veamos ahora en el mismo gráfico de dispersión todos los géneros que analizamos hasta ahora:
data_num_compl%>%filter(music_genre%in%c("Classical", "Rock", "Jazz", "Hip-Hop", "Blues", "Electronic"))%>%
ggplot(aes(PC1,PC2, col=music_genre))+geom_point(alpha=0.4)+
ggtitle("PCA: música por género: (casi) todos")+
scale_color_discrete("Género",
labels=c("Blues", "Clasica", "Electronica",
"Hip-Hop", "Jazz", "Rock"),
type=c("yellowgreen","dodgerblue", "black", "violet", "darkgreen", "red"))+
theme_ipsum()
Veamos en el siguiente gráfico que representa la densidad de
observaciones en el scatterplot de los 2 primeros componentes
principales de la base de datos, la notoria acumulación de datos
alrededor de aproximadamente PC1=-1 y PC2=-1.
data_num_compl%>%filter(music_genre%in%c("Classical", "Rock", "Jazz", "Hip-Hop", "Blues", "Electronic"))%>%
ggplot(aes(PC1,PC2))+geom_pointdensity()+
scale_color_viridis("Puntos vecinos", direction=1)+
ggtitle("PCA: densidad de observaciones")+
theme_ipsum()
Pero ¿qué ocurre con la popularidad de estas canciones?¿Está la popularidad de la música relacionada con sus características musicales? Probemos con un modelo lineal múltiple:
modelo_pop <- lm(popularity~acousticness+danceability+energy+instrumentalness+loudness+speechiness+valence, data_num_compl)
summary(modelo_pop)
##
## Call:
## lm(formula = popularity ~ acousticness + danceability + energy +
## instrumentalness + loudness + speechiness + valence, data = data_num_compl)
##
## Residuals:
## Min 1Q Median 3Q Max
## -55.708 -8.257 0.687 9.284 48.869
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 46.07544 0.57732 79.81 <2e-16 ***
## acousticness -6.59749 0.30886 -21.36 <2e-16 ***
## danceability 19.68755 0.43268 45.50 <2e-16 ***
## energy -8.69907 0.52929 -16.43 <2e-16 ***
## instrumentalness -10.78063 0.22774 -47.34 <2e-16 ***
## loudness 0.34098 0.02093 16.29 <2e-16 ***
## speechiness 14.94027 0.63916 23.38 <2e-16 ***
## valence -4.23608 0.30082 -14.08 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 13.64 on 49992 degrees of freedom
## Multiple R-squared: 0.2295, Adjusted R-squared: 0.2294
## F-statistic: 2127 on 7 and 49992 DF, p-value: < 2.2e-16
Todas las variables resultan estadísticamente significativas con p-valores cercanos a 0 (es esperable por el tamaño de la base de datos), con un modelo que explica el 23% de la variabilidad. Y si en lugar de usar las variables usamos los scores de los dos primeros componentes principales como variables explicativas de la regresion?
modelo_pop_pca <- lm(popularity~PC1+PC2, data_num_compl)
summary(modelo_pop_pca)
##
## Call:
## lm(formula = popularity ~ PC1 + PC2, data = data_num_compl)
##
## Residuals:
## Min 1Q Median 3Q Max
## -60.817 -8.749 0.790 9.698 49.396
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 44.22042 0.06313 700.46 <2e-16 ***
## PC1 -3.21573 0.03428 -93.80 <2e-16 ***
## PC2 2.58180 0.06065 42.56 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 14.12 on 49997 degrees of freedom
## Multiple R-squared: 0.1751, Adjusted R-squared: 0.175
## F-statistic: 5305 on 2 and 49997 DF, p-value: < 2.2e-16
Ambas variables también significativas, con un ligero descenso del R cuadrado ajustado a 17.5%. Veamos un scatterplot de popularidad según PC1 y PC2:
ggplot(data_num_compl, aes(PC1, PC2, col=popularity))+geom_point(alpha=0.6)+
ggtitle("PCA: popularidad de las canciones según PC1 y PC2")+
scale_color_continuous("Popularidad", type="viridis")+
theme_ipsum()
Se observa que los temas más populares tienden a a acumularse en valores bajos de PC1 y medios de PC2 (la zona donde habiamos visto se acumulan la mayoría de los casos de Rock y Hip Hop).
Esta diferencia en la popularidad ¿está determinada por el género o por las características musicales de cada canción?
ggplot(data_num_compl, aes(reorder(music_genre, -popularity), popularity, col=music_genre))+geom_violin(show.legend = F)+geom_jitter(alpha=0.01, show.legend = F)+
ggtitle("Popularidad según género musical")+
xlab("Género musical")+
ylab("Popularidad")+
theme_ipsum()+
theme(axis.text.x = element_text(angle = 30))
mod_pop_genero <- lm(popularity~music_genre, data_num_compl)
summary(mod_pop_genero)
##
## Call:
## lm(formula = popularity ~ music_genre, data = data_num_compl)
##
## Residuals:
## Min 1Q Median 3Q Max
## -59.641 -6.010 -0.804 5.503 57.888
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 50.2242 0.1353 371.12 <2e-16 ***
## music_genreAnime -25.9526 0.1914 -135.60 <2e-16 ***
## music_genreBlues -15.4202 0.1914 -80.57 <2e-16 ***
## music_genreClassical -20.9084 0.1914 -109.25 <2e-16 ***
## music_genreCountry -4.2142 0.1914 -22.02 <2e-16 ***
## music_genreElectronic -12.1124 0.1914 -63.29 <2e-16 ***
## music_genreHip-Hop 8.1754 0.1914 42.72 <2e-16 ***
## music_genreJazz -9.2956 0.1914 -48.57 <2e-16 ***
## music_genreRap 10.2732 0.1914 53.68 <2e-16 ***
## music_genreRock 9.4170 0.1914 49.20 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 9.569 on 49990 degrees of freedom
## Multiple R-squared: 0.621, Adjusted R-squared: 0.6209
## F-statistic: 9100 on 9 and 49990 DF, p-value: < 2.2e-16
mod_pop_generoycaract <- lm(popularity~music_genre+acousticness+danceability+energy+instrumentalness+loudness+speechiness+valence, data_num_compl)
summary(mod_pop_generoycaract)
##
## Call:
## lm(formula = popularity ~ music_genre + acousticness + danceability +
## energy + instrumentalness + loudness + speechiness + valence,
## data = data_num_compl)
##
## Residuals:
## Min 1Q Median 3Q Max
## -58.833 -5.951 -0.742 5.439 57.506
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 51.34531 0.42371 121.182 < 2e-16 ***
## music_genreAnime -25.45092 0.19572 -130.037 < 2e-16 ***
## music_genreBlues -15.13869 0.19627 -77.130 < 2e-16 ***
## music_genreClassical -17.98042 0.25479 -70.569 < 2e-16 ***
## music_genreCountry -4.47326 0.19397 -23.061 < 2e-16 ***
## music_genreElectronic -11.76955 0.20032 -58.754 < 2e-16 ***
## music_genreHip-Hop 8.49867 0.20741 40.974 < 2e-16 ***
## music_genreJazz -8.43350 0.20413 -41.315 < 2e-16 ***
## music_genreRap 10.48076 0.20338 51.533 < 2e-16 ***
## music_genreRock 9.30622 0.19227 48.401 < 2e-16 ***
## acousticness -0.50286 0.22137 -2.272 0.0231 *
## danceability 1.97178 0.34259 5.755 8.69e-09 ***
## energy -0.68859 0.37813 -1.821 0.0686 .
## instrumentalness -1.32633 0.17409 -7.619 2.61e-14 ***
## loudness 0.13586 0.01577 8.616 < 2e-16 ***
## speechiness -6.12435 0.50397 -12.152 < 2e-16 ***
## valence -0.23574 0.22435 -1.051 0.2934
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 9.524 on 49983 degrees of freedom
## Multiple R-squared: 0.6246, Adjusted R-squared: 0.6245
## F-statistic: 5198 on 16 and 49983 DF, p-value: < 2.2e-16
Vemos cómo en el gráfico de violín de popularidad según género se evidencia una marcada diferencia entre géneros (Rap, Rock, y Hip Hop dominando). En cuanto al modelo lineal, al considerar solamente el género como variable explicativa se explica el 62% de la variabilidad. Al controlar por la variable género e incluir como explicativas las características musicales, las variables “energía” y “valencia” dejan de ser significativas. Entonces, las tendencias en la relación de PC1 y PC2 con la popularidad de las canciones parecerían estar más determinadas por el género musical que por los “features musicales” específicos.
Si realizamos un clustering por k-medias teniendo en cuenta solamente las variables “musicales”, cuánto se corresponden estos clusters con los géneros de la muestra? Veamos primero Hip-Hop y música clásica:
set.seed(123)
library(gridExtra)
data_hip_clas <- data_num_compl%>%
filter(music_genre %in% c("Hip-Hop", "Classical"))
#fviz_nbclust(data_hip_clas[1:7], kmeans, method="silhouette")
clust_hip_clas <- kmeans(data_hip_clas[1:7],2)
data_hip_clas$claseskm <- as.factor(clust_hip_clas$cluster)
p1 <-ggplot(data_hip_clas, aes(PC1,PC2, col=claseskm))+
geom_point(alpha=0.4)+
scale_color_discrete("Clusters", labels=c("Cluster 1", "Cluster 2"), type=c("dodgerblue", "violet"))+
ggtitle("Clustering por K-medias")+
theme_pubr()
p2 <-ggplot(data_hip_clas, aes(PC1,PC2, col=music_genre))+
geom_point(alpha=0.4)+
scale_color_discrete("Género musical", labels=c("Clásica", "Hip-Hop"), type=c("dodgerblue", "violet"))+
ggtitle("Hip-hop vs Música Clásica")+
theme_pubr()
grid.arrange(p1,p2, ncol=2)
data_hip_clas$music_genre <- droplevels(data_hip_clas$music_genre)
tabla_hip_clas<-table(data_hip_clas$music_genre, data_hip_clas$claseskm)
kbl(tabla_hip_clas, caption="K-means clustering: Clásica y Hip Hop")%>%kable_classic()
| 1 | 2 | |
|---|---|---|
| Classical | 4021 | 979 |
| Hip-Hop | 23 | 4977 |
accuracy_hip_clas <- (tabla_hip_clas[1,1]+tabla_hip_clas[2,2])/nrow(data_hip_clas)
print(accuracy_hip_clas*100)
## [1] 89.98
El clustering efectuado por k-medias (con k=2) tiene un 90% de coincidencia con la clasificación por género entre Hip-Hop y música Clásica. Recordemos que había observaciones (casi todas con PC1 menor a 0) que eran temas de rock mal clasificados en la base de datos; repitamos este último clustering retirando esas canciones mal clasificadas:
set.seed(123)
library(ggforce)
data_hip_clas2 <- data_num_compl%>%
filter(music_genre=="Hip-Hop"|music_genre=="Classical"&PC1>0)
clust_hip_clas2 <- kmeans(data_hip_clas2[1:7],2)
data_hip_clas2$claseskm <- as.factor(clust_hip_clas2$cluster)
p1<-ggplot(data_hip_clas2, aes(PC1,PC2, col=claseskm))+
geom_point(alpha=0.4)+
scale_color_discrete("Clusters", labels=c("Cluster 2", "Cluster 1"), type=c("violet", "dodgerblue"))+
ggtitle("Clustering por K-medias 2.0")+
theme_pubr()
p2<- ggplot(data_hip_clas2, aes(PC1,PC2, col=music_genre))+
geom_point(alpha=0.4)+
geom_circle(aes(x0 = -0.8, y0 = -1.4, r = 0.7),
inherit.aes = FALSE, linetype=3, linewidth=0.05)+
scale_color_discrete("Género", labels=c("Clásica", "Hip-Hop"), type=c("dodgerblue", "violet"))+
ggtitle("Hip hop vs música clásica")+
theme_pubr()
grid.arrange(p1,p2, ncol=2)
data_hip_clas2$music_genre <- droplevels(data_hip_clas2$music_genre)
tabla_hip_clas2<-table(data_hip_clas2$music_genre, data_hip_clas2$claseskm)
kbl(tabla_hip_clas2, caption="K-means clustering: Clásica y Hip Hop (versión 2)")%>%kable_classic()
| 1 | 2 | |
|---|---|---|
| Classical | 687 | 4005 |
| Hip-Hop | 4977 | 23 |
accuracy_hip_clas2 <- (tabla_hip_clas2[1,2]+tabla_hip_clas2[2,1])/nrow(data_hip_clas2)
print(accuracy_hip_clas2*100)
## [1] 92.67437
Vemos que al retirar las observaciones “incorrectas” (temas de Rock mal clasificados como Clásica), el clustering coincide con el género en un 93% (diferenciando Hip-Hop de Clásica). Pero, ¿qué pasaría si usamos la información de los 2 primeros componentes principales para realizar este clustering?
set.seed(123)
clust_hip_clas_pca <- kmeans(pca$scores[data_num_compl$music_genre=="Hip-Hop"|data_num_compl$music_genre=="Classical"&data_num_compl$PC1>0,1:2],2)
data_hip_clas2$claseskmpca <- as.factor(clust_hip_clas_pca$cluster)
p1<-ggplot(data_hip_clas2, aes(PC1,PC2, col=claseskmpca))+
geom_point(alpha=0.4)+
geom_circle(aes(x0 = 2.2, y0 = -1.3, r = 1),
inherit.aes = FALSE, linetype=3, linewidth=0.05)+
scale_color_discrete("Clusters", labels=c("Cluster 2", "Cluster 1"), type=c("violet","dodgerblue"))+
ggtitle("Clustering por K-medias/PCA")+
theme_pubr()
p2<- ggplot(data_hip_clas2, aes(PC1,PC2, col=music_genre))+
geom_point(alpha=0.4)+
scale_color_discrete("Género", labels=c("Clásica", "Hip-Hop"), type=c("dodgerblue", "violet"))+
ggtitle("Hip hop vs música clásica")+
theme_pubr()
grid.arrange(p1,p2, ncol=2)
data_hip_clas2$music_genre <- droplevels(data_hip_clas2$music_genre)
tabla_hip_clas2<-table(data_hip_clas2$music_genre, data_hip_clas2$claseskmpca)
kbl(tabla_hip_clas2, caption="K-means clustering: Clásica y Hip Hop (versión 2)")%>%kable_classic()
| 1 | 2 | |
|---|---|---|
| Classical | 175 | 4517 |
| Hip-Hop | 4989 | 11 |
accuracy_hip_clas2 <- (tabla_hip_clas2[1,2]+tabla_hip_clas2[2,1])/nrow(data_hip_clas2)
print(accuracy_hip_clas2*100)
## [1] 98.08089
Vemos que la accuracy de este clustering con respecto a las etiquetas de géneros aumenta al 98%! Esto se puede deber a la buena separación que estos géneros en particular presentan en los 2 primeros componentes principales.
Y si probamos dos géneros no tan distintos? Veamos Jazz y Blues:
data_jazz_blues <- data_num_compl%>%
filter(music_genre %in% c("Jazz", "Blues"))
clust_jazz_blues <- kmeans(data_jazz_blues[1:7],2)
data_jazz_blues$claseskm <- as.factor(clust_jazz_blues$cluster)
p1<-ggplot(data_jazz_blues, aes(PC1,PC2, col=claseskm))+
geom_point(alpha=0.4)+
scale_color_discrete("Clusters", labels=c("Cluster 1", "Cluster 2"), type=c("yellowgreen", "darkgreen"))+
ggtitle("Clustering por K-medias")+
theme_pubr()
p2<-ggplot(data_jazz_blues, aes(PC1,PC2, col=music_genre))+
geom_point(alpha=0.4)+
scale_color_discrete("Género", labels=c("Blues", "Jazz"), type=c("yellowgreen", "darkgreen"))+
ggtitle("Blues vs Jazz")+
theme_pubr()
grid.arrange(p1,p2,ncol=2)
data_jazz_blues$music_genre <- droplevels(data_jazz_blues$music_genre)
tabla_jazz_blues<-table(data_jazz_blues$music_genre, data_jazz_blues$claseskm)
kbl(tabla_jazz_blues, caption="K-means clustering: Blues y Jazz")%>%kable_classic()
| 1 | 2 | |
|---|---|---|
| Blues | 3791 | 1209 |
| Jazz | 2995 | 2005 |
accuracy_jazz_blues <- (tabla_jazz_blues[1,1]+tabla_jazz_blues[2,2])/nrow(data_hip_clas)
print(accuracy_jazz_blues*100)
## [1] 57.96
Vemos que la capacidad de discriminar entre géneros en este caso es mucho menor (58%). Probemos nuevamente clusterizar con los datos de PC1 y PC2, esta vez con Jazz y Blues:
set.seed(123)
data_jazz_blues <- data_num_compl%>%
filter(music_genre %in% c("Jazz", "Blues"))
clust_jazz_blues_pca <- kmeans(pca$scores[data_num_compl$music_genre%in%c("Jazz","Blues"),1:2],2)
data_jazz_blues$claseskmpca <- as.factor(clust_jazz_blues_pca$cluster)
p1<-ggplot(data_jazz_blues, aes(PC1,PC2, col=claseskmpca))+
geom_point(alpha=0.4)+
scale_color_discrete("Clusters", labels=c("Cluster 1", "Cluster 2"), type=c("yellowgreen", "darkgreen"))+
ggtitle("Clustering por K-medias/PCA")+
theme_pubr()
p2<-ggplot(data_jazz_blues, aes(PC1,PC2, col=music_genre))+
geom_point(alpha=0.3)+
scale_color_discrete("Género", labels=c("Blues", "Jazz"), type=c("yellowgreen", "darkgreen"))+
ggtitle("Blues vs Jazz")+
theme_pubr()
grid.arrange(p1,p2,ncol=2)
data_jazz_blues$music_genre <- droplevels(data_jazz_blues$music_genre)
tabla_jazz_blues<-table(data_jazz_blues$music_genre, data_jazz_blues$claseskmpca)
kbl(tabla_jazz_blues, caption="K-means clustering: Blues y Jazz")%>%kable_classic()
| 1 | 2 | |
|---|---|---|
| Blues | 3862 | 1138 |
| Jazz | 2502 | 2498 |
accuracy_jazz_blues <- (tabla_jazz_blues[1,1]+tabla_jazz_blues[2,2])/nrow(data_hip_clas)
print(accuracy_jazz_blues*100)
## [1] 63.6
La accuracy sigue siendo mala, si bien mejora ligeramente. Llama la atención el límite neto entre ambos clusters, observable ya que el clustering se calculó con los componentes principales graficados.
Probemos clusterizar la totalidad de los géneros analizados, con k=nro de generos musicales:
data_generos <- data_num_compl%>%
filter(music_genre%in%c("Classical", "Rock", "Jazz", "Hip-Hop", "Blues", "Electronic"))
clust_todo <- kmeans(data_generos[1:7],6)
data_generos$claseskm <- as.factor(clust_todo$cluster)
ggplot(data_generos, aes(PC1,PC2, col=music_genre, shape=claseskm))+
geom_point(alpha=0.4)+
scale_shape_discrete("Cluster")+
scale_color_discrete("Género musical")+
ggtitle("Clustering por K-medias: Todos los géneros")+
theme_ipsum()
data_generos$music_genre <- droplevels(data_generos$music_genre)
tabla_todo<-table(data_generos$music_genre, data_generos$claseskm)
kbl(tabla_todo, caption="K-means clustering: todos los géneros (6 clusters)")%>%kable_classic()
| 1 | 2 | 3 | 4 | 5 | 6 | |
|---|---|---|---|---|---|---|
| Blues | 1862 | 6 | 1433 | 443 | 38 | 1218 |
| Classical | 183 | 990 | 308 | 1270 | 2007 | 242 |
| Electronic | 1589 | 0 | 932 | 178 | 19 | 2282 |
| Hip-Hop | 2280 | 0 | 570 | 51 | 3 | 2096 |
| Jazz | 1626 | 22 | 1776 | 846 | 225 | 505 |
| Rock | 1695 | 1 | 842 | 181 | 11 | 2270 |
Vemos que casi todos los clusters contienen muchos géneros y casi todos los géneros están dispersos en distintos clusters (la música clásica parecería diferenciarse más). Y si probamos con un k menor, por ejemplo 3? (nota: no tratamos de encontrar el k ideal mediante método de silueta u otros ya que todos los métodos son demasiado intensivos para la memoria de una computadora personal debido al tamaño del dataset):
clust_todo <- kmeans(data_generos[1:7],3)
data_generos$claseskm <- as.factor(clust_todo$cluster)
ggplot(data_generos, aes(PC1,PC2, col=music_genre, shape=claseskm))+
geom_point(alpha=0.6)+
scale_shape_discrete("Cluster")+
scale_color_discrete("Género musical")+
ggtitle("Clustering por K-medias: Todos los géneros (3 clusters)")+
theme_ipsum()
data_generos$music_genre <- droplevels(data_generos$music_genre)
tabla_todo<-table(data_generos$music_genre, data_generos$claseskm)
kbl(tabla_todo, caption="K-means clustering: todos los géneros (3 clusters)")%>%kable_classic()
| 1 | 2 | 3 | |
|---|---|---|---|
| Blues | 67 | 1884 | 3049 |
| Classical | 3261 | 1315 | 424 |
| Electronic | 33 | 1110 | 3857 |
| Hip-Hop | 4 | 640 | 4356 |
| Jazz | 319 | 2581 | 2100 |
| Rock | 20 | 1030 | 3950 |
data_generos %>%
group_by(claseskm)%>%
summarize(mean(PC1), mean(danceability), mean(loudness), mean(instrumentalness))%>%
kbl()%>% kable_classic()
| claseskm | mean(PC1) | mean(danceability) | mean(loudness) | mean(instrumentalness) |
|---|---|---|---|---|
| 1 | 4.3944685 | 0.3068765 | -25.523067 | 0.6531998 |
| 2 | 1.1401327 | 0.5421978 | -12.983341 | 0.3039185 |
| 3 | -0.8660341 | 0.6035143 | -6.133267 | 0.1292650 |
Vemos un cluster con alto valor de PC1, baja “bailabilidad” y “loudness”, y alta “instrumentalidad”, cuya amplia mayoría son temas de música clásica; otro cluster con valores inversos que incluye a la mayoría de los temas de Hip Hop, Rock, Electronica y Blues; y un tercer cluster con valores intermedios donde predomina el Jazz.