https://www.linkedin.com/in/javier-viejo-calvo-00542351/
El objetivo de este documento es detallar el proceso llevado a cabo para el análisis, transformación y predicción de géneros musicales, partiendo de un dataset extraído de Kaggle
[https://www.kaggle.com/zaheenhamidani/ultimate-spotify-tracks-db] Este dataset contiene una descarga de Spotify, con más de 200.000 registros; y cuenta con 18 variables, calculadas por el propio algoritmo de clasificación de Spotify.
La variable objetivo será el género musical.
El primer paso va a ser cargar todas las librerías que vamos a utilizar a lo largo de este estudio
library(readxl)
library(ggplot2)
library(dplyr)
library(DataExplorer)
library(caret)
library(randomForest)
library(inspectdf)
library(tidyr)
library(RColorBrewer)
library(readr)
library(rlang)
library(lubridate)
library(data.table)
library(corrplot)
library(fastDummies)
library(textcat)
library(textmineR)
library(rpart)
library(rpart.plot)
library(stringr)
library(scales)
library(ggridges)
library(expss)
library(hrbrthemes)
library(forcats)
library(viridis)
library(plotly)
library(tidyverse)
library(fastcluster)
library(FactoMineR)
library(factoextra)
library(plyr)
library(kableExtra)
library(ranger)
library(caret)
library(MLmetrics)
library(ggpubr)
library(class)
library(CaDENCE)
library(xgboost)
library(dplyr)
library(e1071)
library(nnet)
library(tcR)Hacemos una descarga del dataset desde Kaggle y procedemos a cargar el mismo.
La descarga desde Kaggle contiene un fichero delimitado por comas. Para hacer un primer análisis visual (y poder ver qué es lo que tenía entre manos), he pasado el fichero a formato xlsx. Por ese motivo lo he cargado en este formato.
Antes de comenzar con el análisis propiamente dicho, podemos ver que el dataset contiene 232.725 observaciones. Cada una de estas observaciones se corresponde con una canción. Cada canción tendrá tantas observaciones como géneros musicales a los que pertenece (más adelante ahondaremos en este punto).
Como el objetivo final es llegar a poder predecir el género de una canción, vamos a seleccionar un subset de 10 géneros, que va a ser con el que vamos a trabajar.
## [1] "Movie" "R&B" "A Capella" "Alternative"
## [5] "Country" "Dance" "Electronic" "Anime"
## [9] "Folk" "Blues" "Opera" "Hip-Hop"
## [13] "Children's Music" "Children’s Music" "Rap" "Indie"
## [17] "Classical" "Pop" "Reggae" "Reggaeton"
## [21] "Jazz" "Rock" "Ska" "Comedy"
## [25] "Soul" "Soundtrack" "World"
De los cuales, vamos a seleccionar los siguientes:
Veamos cómo está compuesto el dataset y qué tipo de variables contiene.
Tenemos 90.709 observaciones, con 7 variables categóricas y 11 varibles numéricas.
## [1] 90709 18
Contamos con las siguientes variables (en la presentación detallo el significado de las mismas):
## genre artist_name track_name track_id
## Length:90709 Length:90709 Length:90709 Length:90709
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
##
## popularity acousticness danceability duration_ms
## Min. : 0.00 Min. :0.0000 Min. :0.0582 Min. : 15509
## 1st Qu.: 30.00 1st Qu.:0.0299 1st Qu.:0.4190 1st Qu.: 186480
## Median : 42.00 Median :0.1900 Median :0.5760 Median : 221600
## Mean : 42.46 Mean :0.3566 Mean :0.5537 Mean : 242541
## 3rd Qu.: 57.00 3rd Qu.:0.7150 3rd Qu.:0.7040 3rd Qu.: 268970
## Max. :100.00 Max. :0.9960 Max. :0.9870 Max. :5488000
## NA's :94 NA's :94 NA's :94 NA's :94
## energy instrumentalness key liveness
## Min. :0.00002 Min. :0.0000 Length:90709 Min. :0.0120
## 1st Qu.:0.36800 1st Qu.:0.0000 Class :character 1st Qu.:0.0960
## Median :0.63600 Median :0.0001 Mode :character Median :0.1270
## Mean :0.57622 Mean :0.1704 Mean :0.1968
## 3rd Qu.:0.81200 3rd Qu.:0.1030 3rd Qu.:0.2490
## Max. :0.99900 Max. :0.9940 Max. :1.0000
## NA's :94 NA's :94 NA's :94
## loudness mode speechiness tempo
## Min. :-47.599 Length:90709 Min. :0.02230 Min. : 34.15
## 1st Qu.:-12.215 Class :character 1st Qu.:0.03790 1st Qu.: 93.01
## Median : -7.333 Mode :character Median :0.05070 Median :115.49
## Mean : -9.831 Mean :0.09032 Mean :118.34
## 3rd Qu.: -5.205 3rd Qu.:0.09680 3rd Qu.:140.00
## Max. : 1.585 Max. :0.95700 Max. :239.85
## NA's :94 NA's :94 NA's :94
## time_signature valence
## Length:90709 Min. :0.0000
## Class :character 1st Qu.:0.2370
## Mode :character Median :0.4540
## Mean :0.4614
## 3rd Qu.:0.6760
## Max. :0.9920
## NA's :94
## Classes 'tbl_df', 'tbl' and 'data.frame': 90709 obs. of 18 variables:
## $ genre : chr "Country" "Country" "Country" "Country" ...
## $ artist_name : chr "Cam" "Kevin Fowler" "Roger Miller" "M. Ward" ...
## $ track_name : chr "My Mistake" "That Girl" "Chug-A-Lug" "Chinese Translation" ...
## $ track_id : chr "5ICoYTFfmUKguBHpINKkGL" "0rk6JE7ODyGWWquK4y7t1u" "3vMwtAfDNM8dDBWMvS78pn" "7IJlk42gDKt5dfSSLwtEsp" ...
## $ popularity : num 45 42 46 54 42 44 41 42 42 52 ...
## $ acousticness : num 0.00821 0.0272 0.69 0.152 0.571 0.13 0.199 0.0293 0.173 0.469 ...
## $ danceability : num 0.551 0.375 0.699 0.555 0.64 0.435 0.638 0.387 0.633 0.587 ...
## $ duration_ms : num 200013 208187 123360 238600 243000 ...
## $ energy : num 0.704 0.859 0.408 0.726 0.478 0.846 0.412 0.874 0.444 0.201 ...
## $ instrumentalness: num 2.25e-06 0.00 2.19e-04 1.78e-04 0.00 0.00 9.27e-06 1.72e-05 3.33e-06 2.58e-05 ...
## $ key : chr "G#" "G" "B" "D" ...
## $ liveness : num 0.245 0.267 0.145 0.148 0.0902 0.092 0.319 0.171 0.0821 0.119 ...
## $ loudness : num -5.43 -3.24 -11.46 -8.94 -6.96 ...
## $ mode : chr "Major" "Major" "Major" "Major" ...
## $ speechiness : num 0.0444 0.0569 0.096 0.0368 0.033 0.0496 0.0242 0.053 0.0264 0.0301 ...
## $ tempo : num 97.1 74.1 171.9 107.7 136.7 ...
## $ time_signature : chr "4/4" "4/4" "4/4" "4/4" ...
## $ valence : num 0.541 0.597 0.842 0.505 0.315 0.631 0.509 0.674 0.45 0.3 ...
En este punto, me parece importante hacer una distinción entre:
-Variables que son calculadas por el propio algoritmo de Spotify, y por lo tanto, tienen cierto punto de subjetividad, como son: acousticness, danceability, energy, instrumentalness, liveness, speechiness y valence. Con “cierto punto de subjetividad” me refiero a que esos valores están sujetos a una interpretación (como la “bailabilidad” de una canción). En cualquier caso, esto no nos afecta, porque el criterio de Spotify (mejor o peor) es homogéneo para todo el conjunto a estudiar.
-Variables objetivas: duration_ms, tempo, loudness, key o mode. Son atributos de una canción que no están sujetos a interpretación del algoritmo. Por ejemplo, la duración es la que tenga la pista y el tempo será el que determine un metrónomo.
-La variable popularity, estaría en un punto intermedio, ya que está basada en datos reales de reproducción, pero ponderada por la cercanía en el tiempo (reproducciones más recientes tienen mayor peso a la hora de calcular la popularidad).
Tenemos 7 variables categóricas, entre las cuales se encuentra el género que es nuestra variable objetivo.
Las variables track_id y track_name tienen tantos valores como canciones. Algo similar ocurre con artist_name. Las variables key, mode y time_signature son variables que realmente definen a una canción; y podemos ver que están distribuidas de una manera bastante proporcionada, salvo ésta última.
Podemos ver que las distribuciones son bastante asimétricas. Nuestro objetivo es llegar predecir el género de una canción, por lo que esta asímetría podría ayudarnos. Luego entraremos más en detalle en algunas de estas variables.
¿Nos faltan datos o hay información incompleta?
Lo más relevante es que hay 94 observaciones para las que no tenemos información en 14 variables. A 99 observaciones les falta el dato de time_signature, y 1 no contiene el nombre de la canción.
Se trata de porcentajes muy bajos con respecto al dataset total, por lo que voy a optar por quitarlos directamente:
¿Tenemos valores duplicados? Para hacer esta comprobación, me voy a fijar en el ID de la canción y en el nombre del artista.
1. Canciones:
#cuántas veces aparece cada track_id?
n_apariciones<-data.frame(table(SpotifyData$track_id))
#seleccionos solo los que aparecen más de 1 vez
repeticiones<-n_apariciones[n_apariciones$Freq > 1,]
table(repeticiones$Freq)##
## 2 3 4
## 7787 296 2
Como podemos ver, hay 7.787 canciones que aparecen 2 veces, 296 aparecen 3 y 2 aparecen 4 veces. ¿A qué es debido esto? A que una canción tiene tantos registros en el dataset como número de géneros a los que pertenece. Por lo tanto, no son valores repetidos como tal, sino que pertenecen a más de una clase de nuestra variable target. Más adelante veremos cómo lidiar con este “problema”.
2.Artistas:
#cuántas veces aparece cada artista?
n_apariciones_artist<-data.frame(table(SpotifyData$artist_name))
#seleccionos solo los que aparecen más de 1 vez y veo los artistas con mayor frecuencia
repeticiones_artist<-n_apariciones_artist[n_apariciones_artist$Freq > 1,]
head(repeticiones_artist[order(repeticiones_artist[,2],decreasing=TRUE),],10)## Var1 Freq
## 2760 Giuseppe Verdi 1389
## 2718 Giacomo Puccini 1137
## 6029 Richard Wagner 803
## 8004 Wolfgang Amadeus Mozart 793
## 2695 Georges Bizet 693
## 3603 Johann Sebastian Bach 632
## 4525 Ludwig van Beethoven 595
## 2736 Gioachino Rossini 491
## 2551 Frédéric Chopin 436
## 2613 Gaetano Donizetti 374
Vemos que el top 10 de artistas con más apariciones está ocupado en su totalidad por la música Clásica y la Ópera. Probablemente la explicación sea que, en la búsqueda de un dataset con varios géneros, no se han podido encontrar muchos autores de estos estilos, por lo que se ha optado por escoger varias canciones suyas. Esto no debería suponer un problema para nuestro objetivo final.
Por la propia naturaleza del conjunto de datos, sólo es posible encontar outliers en ciertas variables, ya que muchas de ellas están autocontenidas en un rango determinado (0-1 ó 0-100). Podríamos encontrar valores atípicos extremos en las variables que no tienen valores limitados: Tempo, Loudness y Duration.
Tempo
El tempo nos indica los beats por minuto que tiene la canción. Vamos a ver su distribución:
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 34.15 93.01 115.49 118.34 140.00 239.85
Vemos que el rango intercuantílico es IQR=Q3-Q1 (140-93.01=46.99). Si definimos valor atípico extremo como aquel que dista 3 veces el rango intercantílico por debajo de Q1 o por encima de Q3 (en este caso sólo tendríamos por arriba):
Q3 + 3 x IQR= 280,97 (umbral por encima). Como el max es 239.85, no tenemos valores atípicos.
Loudness
Hacemos lo mismo que antes
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## -47.599 -12.215 -7.333 -9.831 -5.205 1.585
En este caso podríamos tener outliers por la parte baja: IQR=7.01
Q1 - 3 x IQR= -33,245 (umbral por debajo)
##
## Classical Jazz Opera Rock
## 557 8 156 2
Podemos ver que lo que parecen outliers en realidad son canciones de un mismo estilo (Classical y Opera sobretodo). Si observamos la distribución de cada género por separado, vemos que en realidad no son valores atípicos para este tipo de música.
Duration
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 15509 186480 221600 242541 268970 5488000
Vamos analizar esos outliers por la parte alta:
IQR=82490 Q3 + 3 x IQR= 516.440 (umbral por arriba)
##
## Classical Jazz Opera Rock
## 557 8 156 2
Al igual que ocurre con loudness parece que esa duración tan larga es propia de ciertos géneros.
Concluimos por tanto, que el dataset no muestra outliers relevantes o que puedan venir dados de errores en mediciones, que puedan afectar a nuestro análisis.
Vamos a convertir algunas variables para poder trabajar mejor con ellas:
TIME SIGNATURE
Esta variable nos indica el compás en el que está escrita la canción. Como hemos visto anteriormente, la variable está reconocida como caracter, y presenta el formato 1/4, 2/4… La mayoría de las canciones están escritas en compás 4/4 (“cuatro por cuatro”).
Para convertirlo a numérico, voy a asignar un número creciente secuencial:
#identifico los distintos nveles
levels_time_signature <- c('0/4', '1/4', '2/4','3/4','4/4','5/4')
#asigno los número empezando por el 1
SpotifyData$time_signature <- match(SpotifyData$time_signature, levels_time_signature)
#resto 1 para que los niveles empiecen por cero
SpotifyData$time_signature <- SpotifyData$time_signature - 1KEY
Esta variable informa de la escala en la que está la canción y utiliza la nomenclatura anglosajona, en la que la escala empieza en la nota A (Nuestro La). El símbolo # representa el sostenido. A nivel analítico, trataremos cada nota y su variación sostenida, como distintos niveles:
#identifico los distintos nveles
levels_key <- c('A', 'A#', 'B','C','C#','D','D#','E','F','F#','G','G#')
SpotifyData$key <- match(SpotifyData$key, levels_key)MODE
¿Qué es el modo de la canción? Nos indica si está en una escala mayor o menor. Es una variable que suele ir ligada a la variable anterior (KEY). En este caso, transformamos la variable a 1 si es mayor y 0 si es menor:
DURATION
La variable duration_ms indica la duración de la canción en milisegundos. Para que sea más fácil de interpretar, voy a convertir este campo a minutos:
IDIOMA
No contamos con mucha información que no sean variables “musicales”, pero sí que tenemos los nombres de cada canción. ¿Puede estar relacionado el idioma del título de la canción con su género? A simple vista se me ocurre que muchas Operas tendrán títulos en italiano, y seguro que muchas canciones de Reggaeton están en español…
Para intentar ganar exactitud en la detección del idioma, voy a limitar los mismos a 5.
#selecciono los idiomas más comunes
my.profiles <- TC_byte_profiles[names(TC_byte_profiles) %in% c("english", "french","spanish","italian","german")]
SpotifyData$idioma=textcat(SpotifyData$track_name, p=my.profiles)
SpotifyData$idioma[is.na(SpotifyData$idioma)]<-"desconocido"
SpotifyData$idioma<-as.factor(SpotifyData$idioma)
table(SpotifyData$idioma,SpotifyData$genre)##
## Classical Country Electronic Jazz Opera Pop Rap Reggaeton Rock
## desconocido 9 22 64 27 5 51 44 20 23
## english 2808 4232 3176 3890 1148 3391 3205 853 4110
## french 1018 631 1071 929 1881 760 756 880 710
## german 3319 3159 3974 3186 1934 4023 4274 1882 3469
## italian 1068 171 326 410 2288 296 291 862 231
## spanish 1019 446 763 986 991 863 661 4429 708
##
## Ska
## desconocido 29
## english 3665
## french 674
## german 2992
## italian 265
## spanish 1247
¿SE TRATA DE UNA COLABORACIÓN?
De manera similar a lo que hemos hecho con el Idioma, vamos a intentar detectar las canciones en las que hay una colaboración. Voy a asumir que esta colaboración se suele indicar en el nombre de la canción, incluyendo la palabra feat. (featuring) o “&”.
SpotifyData$colaboracion=grepl("(feat|&)",SpotifyData$track_name)
#La función me devuelve los valores "TRUE" y "FALSE", que paso a 1 y 0
SpotifyData<- SpotifyData %>%
mutate(colaboracion= ifelse(colaboracion == "TRUE",1,0))
table(SpotifyData$colaboracion,SpotifyData$genre)##
## Classical Country Electronic Jazz Opera Pop Rap Reggaeton Rock Ska
## 0 9008 8340 8479 9095 8166 8235 7587 7747 9046 8761
## 1 233 321 895 333 81 1149 1644 1179 205 111
KEY + MODE
Como he comentado anteriormente, estas dos variables suelen ir unidas. No quiere decir que la propia relación entre ellas sea indicador de algo, pero nos podría ayudar a la hora de encontrar agrupadores.Seguro que hemos escuchado alguna vez “…sonata en Do Mayor para piano…” El “Do” sería nuestra variable “key” y el “Mayor” nuestro “Mode”.
SpotifyData$key_mode<-paste0(SpotifyData$key, SpotifyData$mode)
SpotifyData$key_mode<-as.factor(SpotifyData$key_mode)BILLBOARD
Billboard es una de las revistas especializadas en música más importantes del mundo. Mantiene varias listas músicales reconocidas internacionalmente.
He encontrado un dataset de Kaggle [https://www.kaggle.com/danield2255/data-on-songs-from-billboard-19992019/] que contiene los artistas que han aparecido en Billboard entre Julio de 1999 y Julio de 2019. Por lo que haciendo un cruce con este listado, podemos identificar a estos grupos/cantantes. No voy a unir los dos datasets, simplemente voy a cruzar el nombre del artista, de manera que si ha estado en esta lista, obtendré el valor 1 y si no, un 0.
#cargo el dataset de Billboard
Billboard <- read_xlsx("C:/Users/Usuario/Desktop/TFM/datasets_538220_983653_BillboardFromLast20_artistDf.xlsx")
###está el artista en el top billboard de los últimos 20 años?
SpotifyData$Billboard <- as.integer(SpotifyData$artist_name %in% Billboard$artist_name)
table(SpotifyData$Billboard,SpotifyData$genre)##
## Classical Country Electronic Jazz Opera Pop Rap Reggaeton Rock Ska
## 0 9053 5131 8992 9037 8247 6179 5880 8228 6220 8872
## 1 188 3530 382 391 0 3205 3351 698 3031 0
¿ES UNA BANDA?
Seguimos haciendo uso de las variables no numéricas para intentar enriquecer el dataset.
¿Cómo podríamos saber si el artista es un grupo o si se trata de un solista? Sabiendo de antemano que este criterio es una aproximación, podríamos asumir que los artistas que lleven la palabra “The” o “Los/Las” incluida en el nombre, probablemente sean una banda (The Beatles, Los Fabulosos Cadillacs…).
Esto no quiere decir que los que no lo lleven sean solistas, pero podría ser una ayuda de cara a identificar algún patrón común en ciertos estilos de música. Espero no encontrar, por ejemplo, bandas musicales en un género como la Opera. En cambio, seguro que el Rock concentra parte de estos casos.
Para ello, uso la función grepl, delimitando los “boundaries” de esas palabras, para que la coincidencia sea exacta.
SpotifyData$group = grepl("(\\bThe\\b|\\bLos\\b|\\bLas\\b)",SpotifyData$artist_name)
#La función me devuelve los valores "TRUE" y "FALSE", que paso a 1 y 0
SpotifyData<- SpotifyData %>%
mutate(group= ifelse(group == "TRUE",1,0))
table(SpotifyData$group,SpotifyData$genre)##
## Classical Country Electronic Jazz Opera Pop Rap Reggaeton Rock Ska
## 0 9232 8354 8855 8951 8247 8771 8835 8779 7808 6262
## 1 9 307 519 477 0 613 396 147 1443 2610
ARTIST NAME
En el punto (2.2.5 Valores repetidos) Hemos sacado el listado de artistas únicos del dataset, obteniendo como resultado 5.174 artistas.
## [1] 8174 2
Voy a crear una varible que recoja la frecuencia de cada uno de estos nombres.
Esta variable está muy orientada a la creación de un modelo predictivo, por lo que no la tendré en cuenta en el análisis descriptivo, ya que no me aporta información sobre la canción.
Llegado este punto, vamos a entrar a analizar un poco más en detalle las variables y la relación que existe entre ellas. Me voy apoyar en la clasificación que he comentado inicialmente:
Variables calculadas por el algoritmo de Spotify, y por tanto sujetas a cierta subjetividad: acousticness, danceability, energy, instrumentalness, liveness, speechiness y valence.
#identifico estas variables
var_subjetivas <- c("acousticness", "danceability", "energy", "instrumentalness", "liveness","speechiness","valence")
SpotifyData %>%
select(c('genre', var_subjetivas)) %>% pivot_longer(cols = var_subjetivas) %>% ggplot(aes(x = value)) + geom_density(aes(color = genre)) + theme_minimal() + facet_wrap(~name, ncol = 3, scales = 'free')Podemos ver detalles interesantes:
-La variable instrumentalness está totalmente concetrada en valores cercanos o iguales a cero.
-Las variables speechiness y liveness presentan distribuciones muy similares para todos los géneros.
-Las variables danceability, energy y valence presentan distribuciones bastante bien diferenciadas para cada tipo de música. Curiosamente, estas 3 variables están relacionas con el “estado de ánimo” de la canción.
-La variable acousticness acumula muchas observaciones en los extremos del rango.
INSTRUMENTALNESS
Para analizar más en detalle esta variable, voy a hacer un gráfico que represente el valor mínimo, el máximo y la mediana para cada género:
ggplot(SpotifyData,aes_string(x='instrumentalness', y='genre', color='genre')) +
stat_summary(mapping = aes(x = instrumentalness , y = reorder(genre,instrumentalness,fun=median)),fun.min =min,fun.max = max,fun=median) +theme_minimal() + labs(x="instrumentalness", y ="genre")+theme(legend.position="none")Una vez más, al ver el detalle por género, comprobamos que puede ser una variable relevante para definir ciertos estilos, mientras que no nos ayudará mucho en otros.
SPEECHINESS y LIVENESS
Speechiness nos da información sobre la presencia de partes habladas en una canción y Liveness detecta la presencia de público en la grabación.
ggplot(data=SpotifyData, aes(x =genre,y=speechiness))+geom_boxplot(aes(color=genre))+theme_minimal()Por lo general, speechiness presenta valores bajos, pero como era de esperar, las canciones de Rap y Reggaeton son las que tienen más partes habladas.
Veamos qué sucede con la variable liveness:
Los valores por lo general son bajos y muy similares. Según Spotify, un valor superior a 0,8 proporciona una gran probabilidad de que la pista se haya grabado en vivo.
Podríamos decir que esta variable no está directamente relacionada con la canción, sino con las circunstancias en la que ha sido grabada. Podríamos plantearnos no tener en cuenta la misma (lo analizamos más adelante).
DANCEABILITY, ENERGY y VALENCE
Estas son variables que podríamos asociar al “estado de ánimo”. Realmente, parece complicado definir la “bailabilidad” de una canción. Veamos la distribución para cada estilo musical:
library(ggridges)
ggplot(SpotifyData, aes(x = danceability, y = genre,fill=stat(x))) + geom_density_ridges_gradient(scale = 2, rel_min_height = 0.01, gradient_lwd = 1.) + scale_x_continuous(expand = c(0, 0)) + scale_y_discrete(expand = expansion(mult = c(0.01, 0.25))) + scale_fill_viridis_c(name = "danceability")+theme_ridges(font_size = 13, grid = TRUE) +
theme(axis.title.y = element_blank())Vemos que el Reggateon es el estilo que presenta más canciones en niveles altos de bailabilidad, mientras que la Opera y la música Clásica, están claramente representadas en valores bajos.
Veamos que sucede con energy y valence:
ggplot(SpotifyData, aes(x = energy, y = genre,fill=stat(x))) + geom_density_ridges_gradient(scale = 2, rel_min_height = 0.01, gradient_lwd = 1.) + scale_x_continuous(expand = c(0, 0)) + scale_y_discrete(expand = expansion(mult = c(0.01, 0.25))) + scale_fill_viridis_c(name = "energy")+theme_ridges(font_size = 13, grid = TRUE) +
theme(axis.title.y = element_blank())ggplot(SpotifyData, aes(x = valence, y = genre,fill=stat(x))) + geom_density_ridges_gradient(scale = 2, rel_min_height = 0.01, gradient_lwd = 1.) + scale_x_continuous(expand = c(0, 0)) + scale_y_discrete(expand = expansion(mult = c(0.01, 0.25))) + scale_fill_viridis_c(name = "valence")+theme_ridges(font_size = 13, grid = TRUE) +
theme(axis.title.y = element_blank())Parece que la variable valence tiene una distribución más homogénea que la de energy, salvo para la Opera y la música clásica (una vez más). Es curioso que en el caso del Jazz, ambas variables presentan distribuciones similares.
ACOUSTICNESS
Esta variable nos mide el nivel de sonido acústico de una canción, por lo que valores cercanos a 1 indicarán canciones acústicas:
Una vez más, la Opera y la música Clásica aparecen como géneros destacados. Si vemos la distribución de cada género:
El Jazz tiene canciones que abarcan todos los niveles de acousticness, con observaciones concentradas en mínimos y máximos del rango. Hay que tener en cuenta que el Jazz es un estilo musical en el que está muy presente la improvisación, y no tiene una estructura musical tan definida como el Pop o el Rock. Por ese motivo podemos encontrar canciones de Jazz en las que cierto instrumento tiene un peso muy alto en la canción, como por ejemplo, solos muy largos de batería o trompeta.
Esto ocurre con menor frecuencia en la música Clásica o la Opera, géneros que suelen presentar una base acústica similar en todas las canciones.
En este grupo tenemos variables que no necesitan ser interpretadas por un algoritmo para su cálculo: duration_ms, tempo, loudness, key o mode
DURATION y TEMPO
Ya hemos analizado previamente estas dos variables en el apartado de Outliers, por lo que vamos a ver el detalle de las mismas por tipo de género. Voy a quitar los valores extremos de la variable duration, para poder tener una mejor visión:
duration <- subset(SpotifyData,duration_ms < 510000)
ggplot(data=duration, aes(x =genre,y=duration_ms))+geom_boxplot(aes(color=genre))+theme_minimal()Aunque no hay diferencias muy grandes, sí que se puede observar que la música Ska es la que tiende a tener canciones más cortas, al contrario que la Clásica, la Electrónica o el Jazz.
En cuanto al Tempo, parece que el Ska presenta canciones más rápidas, teniendo en el otro extremo a la Opera.
LOUDNESS
Esta variable nos indica el volumen general de una canción en dB y se calcula promediando el valor de toda la pista.
Una vez más, vemos patrones similares en la música Clásica y la Opera. La música electrónica presenta los valores más altos y el Reggaeton una mayor concentración de canciones en valores de dB altos.
KEY y MODE
Estas dos variables son categóricas y nos indican:
-la escala de la canción
-si ésta es mayor o menor
Como hemos visto anteriormente, he crado una variable que unifica estas dos:
Recordamos la codificación de estas variables:
Las combinaciones que más se repiten son el Do Mayor, Re Mayor y Sol Mayor; y las que menos el Re Sostenido Menor y el Sol Sostenido Menor. Por lo general, es menos común encontrar canciones en modo Menor.
Como curiosidad, apuntar que la música escrita en Re Sostenido Menor se considera excesívamente difícl de leer, lo cual favorece que no sea muy común.
En cualquier caso, vemos que todos los estilos tienen mayor porcentaje de canciones en modo mayor que menor:
SpotifyData$mode_factor=as.factor(SpotifyData$mode)
ggplot(SpotifyData,aes(x=genre,fill=mode_factor)) + stat_count(aes(color = mode_factor)) + theme_minimal()+coord_flip()POPULARIDAD
Esta variable tiene en cuenta el número de reproducciones, dando mayor importancia a las reproducciones cercanas en el tiempo. Esto va a favorecer que los géneros más escuchados en los últimos meses puedan presentar niveles de popularidad mayores que ciertos temas clásicos, aunque a lo largo del tiempo, estos clásicos hayan sido más populares:
Esta ponderación tiene sentido, ya que el momento, las modas o circunstancias, son algo intrínseco a esta variable. Una canción es popular en un determinado momento, y además, esta popularidad no sólo depende de las propias características de la canción.
Para intentar demostrarlo, voy a seleccionar las 6 canciones más populares de cada género y voy a ver la relación que existe entre todas ellas.
Para que sea más fácil interpretarlo visualmente, voy a mostrar el nombre el artista de esas canciones y sólo estarán unidos si tienen un coeficiente de correlación superior a 0.7
#creo el subset de las 60 canciones más populares
TOP60 <-SpotifyData %>% dplyr::group_by(genre) %>% dplyr::arrange(desc(popularity)) %>% dplyr::slice(1:6)
#agrupo por artista y género. Podrían aparecer artistas repetidos.
avg_song_matrix <- TOP60 %>% group_by(artist_name,genre) %>% summarise_if(is.numeric, median, na.rm = TRUE) %>%ungroup()
#creo la correlation matrix
avg_song_cor <- select(avg_song_matrix, -artist_name, -genre) %>%
scale() %>%
t() %>%
as.matrix() %>%
cor()
colnames(avg_song_cor) <- avg_song_matrix$artist_name
row.names(avg_song_cor) <- avg_song_matrix$artist_name
#sólo voy a unir los artistas que tengan una correlación superior a 0.7
avg_song_cor[avg_song_cor<0.7] <- 0
library(igraph)
network <- graph_from_adjacency_matrix( avg_song_cor, weighted=T, mode="undirected", diag=F)
#color palette
library(RColorBrewer)
color <- brewer.pal(nlevels(as.factor(avg_song_matrix$genre)),"Paired")
#mapeo los colores a cada género
my_color <- color[as.factor((avg_song_matrix$genre))]
#gráfico
par(bg="grey13", mar=c(0,0,0,0))
set.seed(4)
plot(network,
vertex.size=9,
vertex.color=my_color,
vertex.label.cex=1,
vertex.label.color="white",
vertex.frame.color="transparent")
#título y leyenda
legend("bottomleft",
legend=paste( levels(as.factor(avg_song_matrix$genre)), sep=""),
col = color ,
bty = "n", pch=20 , pt.cex = 2, cex = 1,
text.col="white" , horiz = F)Según vemos en el gráfico, parece difícil encontrar “clusters” entre las canciones más populares, aún incluso siendo canciones del mismo género.
Sí que podemos ver algunos clusters interesantes:
- Se confirma el binomio Opera-Clasica que veníamos viendo en otras variables, con un grupo de hasta 8 canciones relacionadas. Si bien hemos seleccionado las 6 canciones más populares de cada género, hay que decir que la canción más popular de música clásica sólo tiene un 69/100
- Artistas como Ozuna o Post Malone están unidos con hasta 3 géneros distintos. Estamos hablando de popularidades cercanas a 100. ¿Quizás la clave del éxito sea combinar atributos de Rap, Reggaeton y Pop?
En cualquier caso, podemos concluir que la popularidad de una canción no siempre está relacionada con sus atributos musicales, sino que influyen otros factores “externos”.
IDIOMA
Hemos obtenido el idioma partiendo del nombre de la canción, pero al no contar con mucho contexto, quizás la exactitud no ha sido muy alta:
Parece que hay algo que no cuadra, ya que ha detectado que la mayoría de las canciones están en Alemán. Probamos a dejar sólo 4 idiomas:
#quito el alemán
my.profiles2 <- TC_byte_profiles[names(TC_byte_profiles) %in% c("english","french","spanish","italian")]
SpotifyData$idioma2=textcat(SpotifyData$track_name, p=my.profiles2)
SpotifyData$idioma2[is.na(SpotifyData$idioma2)]<-"desconocido"
SpotifyData$idioma2<-as.factor(SpotifyData$idioma2)
cro(SpotifyData$idioma, SpotifyData$idioma2)| SpotifyData$idioma2 | |||||
|---|---|---|---|---|---|
| desconocido | english | french | italian | spanish | |
| SpotifyData$idioma | |||||
| desconocido | 246 | 42 | 2 | 3 | 1 |
| english | 30478 | ||||
| french | 9310 | ||||
| german | 65 | 19744 | 6330 | 2203 | 3870 |
| italian | 6208 | ||||
| spanish | 12113 | ||||
| #Total cases | 311 | 50264 | 15642 | 8414 | 15984 |
La mayoría de las canciones clasificadas inicialmente como Alemán, pasan a clasificare como Inglés o Francés. Más tarde veremos si esto mejora la predicción del modelo.
BILLBOARD
Esta variable nos indicaba si el artista había estado en la lista de Billboard de los últimos 20 años. ¿Tendrá algo que ver con la popularidad de los mismos?
bill<- filter(SpotifyData, Billboard == 1)
library(hrbrthemes)
ggplot(data=bill, aes(x=popularity, group=genre, fill=genre)) +
geom_density(adjust=1.8, alpha=.4) +
theme_ipsum() + scale_colour_brewer(palette="Paired")No parece que exista una relación entre la inclusión de un artista en la lista Billboard y la popularidad de sus canciones.
Voy a crear una matriz que muestre la correlación ente las distintas variables numéricas del dataset.
#Creo la matriz de correlacion variables
correlation_data <- SpotifyData %>% select(-genre,-artist_name,-track_name,-track_id, -idioma, -key_mode, -mode_factor, -duration_ms,-idioma2)
correlation_data=scale(correlation_data)
cor_spoti=cor(correlation_data)
col <- colorRampPalette(c("#BB4444", "#EE9988", "#FFFFFF", "#77AADD", "#4477AA"))
p.mat <- cor.mtest(correlation_data)$p
corrplot(cor_spoti, method = "color", col = col(200),
type = "upper", order = "hclust", number.cex = .7,
addCoef.col = "black", # Add coefficient of correlation
tl.col = "black", tl.srt = 90, # Text label color and rotation
# Combine with significance
p.mat = p.mat, sig.level = 0.01, insig = "blank",
# hide correlation coefficient on the principal diagonal
diag = FALSE)Si bien no hay muchas variables con una correlación muy alta, sí que podemos destacar la relación entre:
energy y loudness: Presentan una alta correlación positiva.
Recordemos que energy es una medida perceptiva de la intensidad. Las características que contribuyen al cálculo de este valor incluyen rango dinámico, volumen percibido, timbre, frecuencia de inicio y entropía general. Por lo que ciertas características que afectan al cálculo de esta variable, también formarán parte del cálculo de loudness, que es el volumen general de un canción.
ggplot(SpotifyData, aes(energy, loudness)) +
geom_point(shape = 16, size = 1, show.legend = FALSE, alpha = .3, aes(color=loudness)) +
theme_minimal() + scale_color_gradient(low = "#0091ff", high = "#f0650e") + facet_grid(genre~.)acousticness y energy: Tienen una alta correlación negativa. Si una canción tiene un nivel de acousticness alto, tendrá menos probabilidad de contener sonidos no acústicos y por lo tanto el timbre o el rango dinámico estará más limitado. Esto afectará a los niveles de energy, que serán inferiores.
acousticness y loudness: Explicado por extensión de la relación positiva entre energy-loudness y negativa entre acousticness-energy.
Podemos plantearnos no tener en cuenta la variable energy.
Relación entre géneros
Hemos visto la matriz de correlación entre variables. Ahora voy a hacer una representación similar pero en función de los géneros. Se trata de una aproximación con la intención de llegar a encontrar géneros que pudieran formar parte de clusters.
Para ello, voy a calcular una matriz de correlación tomando la mediana de cada variable para cada género:
#Creo la matriz de correlacion géneros
cor_data=SpotifyData %>% select(-artist_name,-track_id,-track_name,-time_signature,-idioma, -key_mode,-colaboracion,-Billboard, -mode_factor, -group, -mode)
avg_genre_matrix <- cor_data %>% group_by(genre) %>%
summarise_if(is.numeric, median, na.rm = TRUE) %>% ungroup()
featured_names<-names(avg_genre_matrix)[2:13]
avg_genre_cor <- avg_genre_matrix %>% select(featured_names) %>% scale() %>%
t() %>% as.matrix() %>% cor()
colnames(avg_genre_cor) <- avg_genre_matrix$genre
row.names(avg_genre_cor) <- avg_genre_matrix$genre
avg_genre_cor %>% corrplot::corrplot(method = 'color',
order = 'hclust',
type = 'upper',
tl.col = 'black',
diag = FALSE,
addCoef.col = "black",
number.cex = 0.75,
mar = c(2,2,2,2),
main = 'Correlación entre valores medianos de cada género',
family = 'Avenir')La música Clásica y la Opera tienen una correlación negativa con prácticamente todos los géneros, excepto entre ellas mismas, por lo que probablemente sean los estilos musicales más fáciles de diferenciar del resto. En el otro lado tenemos al Pop, Rock y Country, que parecen los géneros más similares.
En el siguiente gráfico podemos ver una comparación entre los distintos géneros, basada en el valor de la mediana de cada variable (al igual que en la matriz de correlaciones) seleccionando los géneros que queremos comparar:
datos_polar <- avg_genre_matrix %>% select(featured_names) %>% scale()
row.names(datos_polar) <- avg_genre_matrix$genre
#nombres de las variables
names<-colnames(datos_polar)
fig2 <- plot_ly(
type = 'scatterpolar',
fill = 'toself',
visible = 'legendonly'
)
fig2 <- fig2 %>%
add_trace(
r = datos_polar[1,],
theta = names,
name = 'Classical'
)
fig2 <- fig2 %>%
add_trace(
r = datos_polar[2,],
theta = names,
name = 'Country'
)
fig2 <- fig2 %>%
add_trace(
r = datos_polar[3,],
theta = names,
name = 'Electronic'
)
fig2 <- fig2 %>%
add_trace(
r = datos_polar[4,],
theta = names,
name = 'Jazz'
)
fig2 <- fig2 %>%
add_trace(
r = datos_polar[5,],
theta = names,
name = 'Opera'
)
fig2 <- fig2 %>%
add_trace(
r = datos_polar[6,],
theta = names,
name = 'Pop'
)
fig2 <- fig2 %>%
add_trace(
r = datos_polar[7,],
theta = names,
name = 'Rap'
)
fig2 <- fig2 %>%
add_trace(
r = datos_polar[8,],
theta = names,
name = 'Reggaeton'
)
fig2 <- fig2 %>%
add_trace(
r = datos_polar[9,],
theta = names,
name = 'Rock'
)
fig2 <- fig2 %>%
add_trace(
r = datos_polar[10,],
theta = names,
name = 'Ska'
)
fig2 <- fig2 %>%
layout(
polar = list(
radialaxis = list(
visible = T,
range = c(-2,3)
)
)
)
fig2Queda patente la similitud entre el Pop, Rock y el Country; o la diferencia que hay entre la Opera o la música Clásica, con el resto de estilos.
Voy a utilizar el método de PCA como parte de este estudio descriptivo. De cara a simplificar la visualización, no voy a tener en cuenta todas las variables que he creado, ya éstas no dan información cuantitativa de la canción, si no simplemente nos indican si pertenecen a grupos (banda/solista, colaboración/no colaboración….)
#Quito las variables que he creado nuevas, ya que no representan un valor propio de la canción.
cols<-c(5:18)
SpotifyData_PCA<-SpotifyData[,cols]
spoti_pca=prcomp(SpotifyData_PCA, center = TRUE,scale = TRUE)
#creo un dataframe con los resultados para su visualización posterior
scores = as.data.frame(spoti_pca$x)
#Importancia de cada componente
summary(spoti_pca)## Importance of components:
## PC1 PC2 PC3 PC4 PC5 PC6 PC7
## Standard deviation 2.0586 1.08403 1.04913 1.02311 0.99587 0.97521 0.95361
## Proportion of Variance 0.3027 0.08394 0.07862 0.07477 0.07084 0.06793 0.06496
## Cumulative Proportion 0.3027 0.38663 0.46525 0.54002 0.61086 0.67879 0.74375
## PC8 PC9 PC10 PC11 PC12 PC13 PC14
## Standard deviation 0.92553 0.87875 0.80788 0.79431 0.60688 0.46702 0.29786
## Proportion of Variance 0.06119 0.05516 0.04662 0.04507 0.02631 0.01558 0.00634
## Cumulative Proportion 0.80493 0.86009 0.90671 0.95178 0.97808 0.99366 1.00000
Necesitaría 9 componentes principales para empezar arecoger un nivel aceptable de variabilidad de los datos (86%).
En el siguiente gráfico vemos la contribución de cada variable a las 2 primeras componentes principales:
fviz_pca_var(spoti_pca, col.var = "contrib",
gradient.cols = c("white", "blue", "red"),
repel = TRUE)Las variables loudness, energy, acousticness o danceability tienen una contribución alta, mientras que otras como duration o speechiness apenas aportan a la varianza.
Voy a representar las 3 primeras componentes, para ver si, aún contando con un nivel bajo de representatividad podemos adivinar ciertos clusters entorno al género de la canción.
Seleccionar los estilos músicales a mostar en el gráfico:
Parece complicado llegar identificar grupos bien definidos por estilo musical, aunque, una vez más comprobamos que hay géneros bien diferenciados de otros.
Continuando con este análisis exploratorio y sin perder de vista nuestro objetivo final (identificación de 10 géneros musicales), voy a identificar clusters en el dataset. ¿cuántos clusters puedo detectar con claridad? ¿Coincidirán con la clasificación en función de los estilos musicales?
Qué número de clusters es el óptimo?
Para calcular el número de clusters óptimo voy a usar el método del “codo”. En él se calcula la suma de cuadrados en cada número de clusters y tenemos que detectar el cambio en la pendiente para determinar el número óptimo de agrupaciones.
En el gráfico he incluido hasta 10 clusters, ya que son 10 los géneros que queremos detectar (realmente es casi imposible detectar un cluster por cada género):
spoti_k<-SpotifyData[,cols]
spoti_scale <- scale(spoti_k)
#identifico el nº de clusters óptimo
wss <- function(k) {
kmeans(spoti_scale, k, nstart = 10 )$tot.withinss
}
k.values <- 1:10
wss_values <- map_dbl(k.values, wss)
plot(k.values, wss_values,
type="b", pch = 20, frame = TRUE,
xlab="Number of clusters K",
ylab="Total within-clusters sum of squares")Aunque no se aprecia con mucha facilidad, podríamos determinar que el número de agrupaciones óptimo sería 4.
Vamos a ver cómo quedaría el dataset con 4 clusters delimitados.
Seleccionar los clusters a mostrar en el gráfico:
spoti_clusters<-SpotifyData[, cols]
set.seed(5)
spoti_kmeans<-kmeans(spoti_clusters, centers = 4 , nstart = 50)
#añado la información del cluster al que pertenece cada observación, al dataset original:
Spotifydata_cluster<-cbind(SpotifyData,spoti_kmeans[1])
#Calculo PCA
SpotifyData_PCA2<-Spotifydata_cluster[,cols]
spoti_pca2=prcomp(SpotifyData_PCA2, center = TRUE,scale = TRUE)
#creo un dataframe con los resultados para su visualización posterior
scores2 = as.data.frame(spoti_pca2$x)
Spotifydata_cluster$cluster<-as.factor(Spotifydata_cluster$cluster)
plot_ly(scores2, x = ~PC1, y = ~PC2, z = ~PC3, color = Spotifydata_cluster$cluster, visible = 'legendonly', size=0.01)Parece que no hay una separación entre grupos bien definida, además el cluster 2 contiene muy pocas observaciones. Voy a reprentarlo de nuevo, con sólo 3 clusters.
Seleccionar los clusters a mostrar en el gráfico:
spoti_clusters<-SpotifyData[, cols]
set.seed(5)
spoti_kmeans2<-kmeans(spoti_clusters, centers = 3, nstart = 50)
#añado la información del cluster al que pertenece cada observación, al dataset original:
Spotifydata_cluster2<-cbind(SpotifyData,spoti_kmeans2[1])
#Calculo PCA
SpotifyData_PCA3<-Spotifydata_cluster2[,cols]
spoti_pca3=prcomp(SpotifyData_PCA3, center = TRUE,scale = TRUE)
#creo un dataframe con los resultados para su visualización posterior
scores3 = as.data.frame(spoti_pca3$x)
Spotifydata_cluster2$cluster<-as.factor(Spotifydata_cluster2$cluster)
plot_ly(scores3, x = ~PC1, y = ~PC2, z = ~PC3, color = Spotifydata_cluster2$cluster, visible = 'legendonly', size=0.01)En cualquier caso, parece que va a ser complicado llegar a identificar 10 estilos musicales con un porcentaje alto de probabilidad, ya que los géneros aparecen muy entremezclados:
Antes de probar los diferentes modelos, voy a crear 3 datasets distintos, en base a la comentado en el punto “2.2.5 Valores duplicados”.
En este apartado explico que hay ciertas canciones que se repiten varias veces. Esto es debido a que pertenecen a varios géneros, y por lo tanto presentan tantos registros como estilos musicales a los que pertenecen. Esto va a dificultar aún más la predicción, ya que si la misma canción aparece en el set de entrenamiento del modelo con un género y en el set de test con otro, muy probablemente, el modelo falle en su predicción.
Para lidiar con este problema, voy a usar 3 datasets:
-DataModel1: El original, con “repeticiones” para las canciones que pertenecen a varios géneros.
-DataModel2: Registros únicos, añadiendo una nueva categoría (“varios”) a la variable target.
Inconvenientes:
- Pasamos a tener 11 clases a predecir, en lugar de 10.
- La categoría “varios” es un compendio de distintos estilos que no tienen por qué estar relacionados, simplemente son canciones que pertenencen a más de un género, pero éstos pueden ser: Opera+Clasica, Rap+Pop, Pop+Rock…
-DataModel3: Registros únicos, sin añadir una nueva categoría. Las canciones que tengan varios géneros quedarán con el primer género que tengan en el dataset original.
DataModel1
Este será el conjunto original:
Datamodel1<-copy(SpotifyData)
#Convierto la variable target a tipo factor
Datamodel1$genre<-as.factor(Datamodel1$genre)DataModel2
Identifico las canciones que pertenecen a varios géneros, quedándome sólo con un registro y asignándole la categroría “varios”.
Para dificultar aún más las cosas, veo que hay canciones que pertenecen a varios géneros, y presentan una popularidad distinta cuando aparecen clasificados como uno u otro estilo. Esto, a priori, no tiene mucho sentido, ya que el track_id es el mismo. Pero teniendo en cuenta que la popularidad se calcula en base al nº de reproducciones, quizás éstas se contabilicen de distinta manera si el usuario ha llegado hasta la canción seleccionando un estilo u otro.
Para solventarlo, voy a crear un dataframe con los track_id y su popularidad y posteriormente me quedaré con registros únicos de tracK_id (no tiene impacto quedarme con una popularidadu otra para la misma canción, ya que casi no varía):
popularidad<-SpotifyData[,4:5]
popularidad<-popularidad%>% group_by(track_id) %>% filter(row_number() == 1)#Creo un nuevo dataset para modelizar
Datamodel2<-copy(SpotifyData)
Datamodel2$popularity<-NULL
#combino en la variable genre los géneros a los que pertenece la cancion
Datamodel2 <- as.data.table(aggregate(Datamodel2[1],Datamodel2[-1],unique))
Datamodel2 <- Datamodel2[ , genre2 := paste0(unlist(genre), collapse = "_"), by = .(1:nrow(Datamodel2))]
#limpio espacios, guiones... de este nuevo campo genre2
Datamodel2[ , genre2 := str_replace_all(genre2, "-|’| ", "")]
#si tiene _ es que son varios generos
Datamodel2$genre2 = ifelse(grepl("(_)",Datamodel2$genre2),"varios",Datamodel2$genre2)
#quito la variable de genre original y convierto la nueva a factor
Datamodel2$genre<-NULL
Datamodel2$genre2<-as.factor(Datamodel2$genre2)
#añado de nuevo la variable popularity
Datamodel2<-merge(Datamodel2,popularidad[,c("track_id","popularity")],by.x="track_id",by.y="track_id")
Datamodel2<-Datamodel2[!duplicated(Datamodel2), ]
#convierto la variable target a tipo factor
Datamodel2$genre2<-as.factor(Datamodel2$genre2)DataModel3
Solo registros únicos del dataset original. Si una canción aparece 3 veces porque pertenece a 3 estilos, me quedaré con el primero que aparezca en el dataset. Aquí entra cierta aleatoriedad a la hora de seleccionar la muestra:
#Creo un nuevo dataset para modelizar
Datamodel3<-copy(SpotifyData)
Datamodel3<-Datamodel3%>% group_by(track_id) %>% filter(row_number() == 1)%>%ungroup
#convierto la variable target a tipo factor
Datamodel3$genre<-as.factor(Datamodel3$genre)Veo cómo se distribuye la variable target en cada conjunto:
genre_1 <- round( prop.table(table(Datamodel1$genre))*100, 2)
genre_2 <- round( prop.table(table(Datamodel2$genre2))*100, 2)
genre_3 <- round( prop.table(table(Datamodel3$genre))*100, 2)
kable(
list(genre_1, genre_2, genre_3),
booktabs = TRUE
)
|
|
|
En el segundo Dataset el Rock y el Pop pierden mucha representatividad, en favor de “varios”.
En el tercer Daataset, también bajan las observaciones de Pop y sobretodo de Rock. Veremos cómo afecta al entrenamiento de cada modelo.
Selección de variables
Para cada dataset definido anteriormente, dejo sólo las variables iniciales, para poder comparar resultados posteriormente, incluyendo variables nuevas.
#variables originales
Datamodel1_clean<-Datamodel1 %>% select(-artist_name,-track_id,-track_name,-duration_min,-idioma, -key_mode,-colaboracion,-Billboard, -mode_factor, -group,-idioma2,-name_freq)
Datamodel2_clean<-Datamodel2%>% select(-artist_name,-track_id,-track_name,-duration_min,-idioma, -key_mode,-colaboracion,-Billboard, -mode_factor, -group,-idioma2,-name_freq)
Datamodel3_clean<-Datamodel3%>% select(-artist_name,-track_id,-track_name,-duration_min,-idioma, -key_mode,-colaboracion,-Billboard, -mode_factor, -group,-idioma2,-name_freq)Creación de grupos de entrenamiento y test
Para cada dataset, generamos los conjuntos de entrenamiento y test:
set.seed(5)
index1 <- createDataPartition(Datamodel1_clean$genre, p = 0.75, list = FALSE)
spoti_train1 <- Datamodel1_clean[ index1,]
spoti_test1 <- Datamodel1_clean[-index1,]
set.seed(5)
index2 <- createDataPartition(Datamodel2_clean$genre2, p = 0.75, list = FALSE)
spoti_train2 <- Datamodel2_clean[ index2,]
spoti_test2 <- Datamodel2_clean[-index2,]
set.seed(5)
index3 <- createDataPartition(Datamodel3_clean$genre, p = 0.75, list = FALSE)
spoti_train3 <- Datamodel3_clean[ index3,]
spoti_test3 <- Datamodel3_clean[-index3,]Voy a probar este modelo con los 3 datasets y las variables originales.
Construcción del modelo
set.seed(34)
fit1<- ranger( as.factor(genre) ~. ,
data = spoti_train1,
num.trees = 1000,
importance = 'impurity',
write.forest = TRUE,
min.node.size = 1,
splitrule = "gini",
verbose = TRUE,
classification = TRUE
)## Growing trees.. Progress: 41%. Estimated remaining time: 44 seconds.
## Growing trees.. Progress: 81%. Estimated remaining time: 14 seconds.
set.seed(34)
fit2<- ranger( as.factor(genre2) ~. ,
data = spoti_train2,
num.trees = 1000,
importance = 'impurity',
write.forest = TRUE,
min.node.size = 1,
splitrule = "gini",
verbose = TRUE,
classification = TRUE
)## Growing trees.. Progress: 42%. Estimated remaining time: 42 seconds.
## Growing trees.. Progress: 85%. Estimated remaining time: 11 seconds.
set.seed(34)
fit3<- ranger( as.factor(genre) ~. ,
data = spoti_train3,
num.trees = 1000,
importance = 'impurity',
write.forest = TRUE,
min.node.size = 1,
splitrule = "gini",
verbose = TRUE,
classification = TRUE
)## Growing trees.. Progress: 46%. Estimated remaining time: 36 seconds.
## Growing trees.. Progress: 93%. Estimated remaining time: 5 seconds.
Prueba sobre el conjunto de test
RF_result1 <- predict(fit1, spoti_test1)
RF_result2 <- predict(fit2, spoti_test2)
RF_result3 <- predict(fit3, spoti_test3)Contraste de resultados
RF1<-Accuracy(y_pred=RF_result1$predictions, y_true=spoti_test1$genre)
RF2<-Accuracy(y_pred=RF_result2$predictions, y_true=spoti_test2$genre2)
RF3<-Accuracy(y_pred=RF_result3$predictions, y_true=spoti_test3$genre)
RF1## [1] 0.6452097
## [1] 0.7030604
## [1] 0.7365733
Utilizando el Datamodel3 tenemos una tasa de aciertos mucho más alta. Este dataset sólo recoge una observación en los casos en los que la canción pertenezca a varios géneros. Por lo tanto, “sacrificamos” datos de entrenamiento, pero las canciones tienen géneros unívocos.
Voy a seleccionar el Datamodel3 pero incluyendo las variables que había creado inicialmente [idioma / idioma2, colaboracion , Billboard, freq_name y group]:
Datamodel3_clean2<-Datamodel3%>% select(-artist_name,-track_id,-track_name,-duration_min, -key_mode, -mode_factor,-idioma)
set.seed(34)
index3_2 <- createDataPartition(Datamodel3_clean2$genre, p = 0.75, list = FALSE)
spoti_train3_2 <- Datamodel3_clean2[ index3_2,]
spoti_test3_2 <- Datamodel3_clean2[-index3_2,]
fit3_2<- ranger( as.factor(genre) ~. ,
data = spoti_train3_2,
num.trees = 1000,
importance = 'impurity',
write.forest = TRUE,
min.node.size = 1,
splitrule = "gini",
verbose = TRUE,
classification = TRUE
)## Growing trees.. Progress: 44%. Estimated remaining time: 38 seconds.
## Growing trees.. Progress: 90%. Estimated remaining time: 6 seconds.
RF_result3_2 <- predict(fit3_2, spoti_test3_2)
RF3_2<-Accuracy(y_pred=RF_result3_2$predictions, y_true=spoti_test3_2$genre)
RF3_2## [1] 0.7927612
El resultado mejora considerablemente. Antes de llegar a este resultado, he probado el modelo con las siguientes variaciones:
-Sin añadir la variable freq_name:
Con idioma (5 idiomas) = 0.7584647 Con idioma2(4 idiomas) = 0.7586593
El verdadero salto, se da al incluir la variable con la frecuencia de los nombres de los artistas, llegando a una accuracy de 0.7927.
En el análisis descriptivo previo, veíamos que la variable liveness parecía no aportar mucho a la distinción de géneros, ya que sólo informa de la presencia de público en la grabación. Si probamos a excluir la misma:
Datamodel3_clean2.2<-Datamodel3%>% select(-artist_name,-track_id,-track_name,-duration_min, -key_mode, -mode_factor,-idioma,-liveness)
set.seed(34)
index3_2.2 <- createDataPartition(Datamodel3_clean2.2$genre, p = 0.75, list = FALSE)
spoti_train3_2.2 <- Datamodel3_clean2.2[ index3_2.2,]
spoti_test3_2.2 <- Datamodel3_clean2.2[-index3_2.2,]
fit3_2.2<- ranger( as.factor(genre) ~. ,
data = spoti_train3_2.2,
num.trees = 1000,
importance = 'impurity',
write.forest = TRUE,
min.node.size = 1,
splitrule = "gini",
verbose = TRUE,
classification = TRUE
)## Growing trees.. Progress: 46%. Estimated remaining time: 36 seconds.
## Growing trees.. Progress: 92%. Estimated remaining time: 5 seconds.
RF_result3_2.2 <- predict(fit3_2.2, spoti_test3_2.2)
RF3_2.2<-Accuracy(y_pred=RF_result3_2.2$predictions, y_true=spoti_test3_2.2$genre)
RF3_2.2## [1] 0.7934423
La precisión mejora ligeramente, con un ratio de 0.7934.
Veamos la importancia de las variables:
RF3_imp <- fit3_2.2$variable.importance
RF3_imp <- as.data.frame(RF3_imp)
RF3_imp $myvar <- rownames(RF3_imp)
RF3_imp <- as.data.table(RF3_imp)
setorder(RF3_imp , -RF3_imp)
ggbarplot(RF3_imp[1:15],
x = "myvar", y = "RF3_imp",
#fill = 'myvar',
color = "green",
palette = "paired",
sort.val = "asc",
sort.by.groups = FALSE,
x.text.angle = 90,
ylab = "Importancia",
xlab = 'Variable',
rotate = TRUE,
ggtheme = theme_minimal()
)Y la Matriz de confusión:
confusion_matrix <- as.data.frame(table(spoti_test3_2.2$genre, RF_result3_2.2$predictions))
names(confusion_matrix) = c("Actual","Predicted","Freq")
ggplot(data = confusion_matrix,
mapping = aes(x = Actual,
y = Predicted)) +
geom_tile(aes(fill = Freq)) +
geom_text(aes(label = sprintf("%1.0f", Freq)), vjust = 1) +
scale_fill_gradient(low = "blue",
high = "red",
trans = "log")Como ya adelantábamos durante el análisis descriptivo, el género que aparentemente es más sencillo de diferenciar es la Opera.
Creo una tabla con los resultados para posteriormente comparar este modelo:
Aplicamos el modelo de árboles de decisión, sobre el datamodel3, que es el que mejor resultados nos había dado inicialmente. De la misma forma que antes, utilizo inicialmente las variables originales:
set.seed(34)
dec_tree <- rpart(genre ~ ., data = spoti_train3)
rpart.plot(dec_tree,
type = 5,
extra = 104,
box.palette = 'Blue',
leaf.round = 0,
fallen.leaves = FALSE,
branch = 0.3,
under = TRUE,
under.col = 'grey40',
family = 'Avenir',
main = 'Genre Decision Tree',
tweak = 1.2)Aplico el modelo a los datos de test:
dec_tree_result<- predict(object=dec_tree,spoti_test3 ,type="class")
t<-table(spoti_test3$genre,dec_tree_result)
confusionMatrix(t)## Confusion Matrix and Statistics
##
## dec_tree_result
## Classical Country Electronic Jazz Opera Pop Rap Reggaeton Rock
## Classical 1624 20 79 170 211 2 0 50 18
## Country 63 929 59 67 0 86 6 385 296
## Electronic 45 79 1185 376 1 58 41 301 147
## Jazz 565 164 324 653 6 23 28 241 120
## Opera 283 1 0 75 1672 0 0 0 0
## Pop 91 2 3 0 0 1556 33 4 225
## Rap 10 10 6 4 0 638 704 140 368
## Reggaeton 2 104 65 51 0 130 122 1446 123
## Rock 63 53 36 10 0 465 72 43 879
## Ska 14 97 407 144 7 17 14 560 63
## dec_tree_result
## Ska
## Classical 39
## Country 40
## Electronic 110
## Jazz 46
## Opera 30
## Pop 1
## Rap 0
## Reggaeton 164
## Rock 4
## Ska 888
##
## Overall Statistics
##
## Accuracy : 0.5612
## 95% CI : (0.5544, 0.568)
## No Information Rate : 0.1542
## P-Value [Acc > NIR] : < 2.2e-16
##
## Kappa : 0.5122
##
## Mcnemar's Test P-Value : NA
##
## Statistics by Class:
##
## Class: Classical Class: Country Class: Electronic
## Sensitivity 0.5884 0.63674 0.54760
## Specificity 0.9669 0.94753 0.93704
## Pos Pred Value 0.7338 0.48110 0.50576
## Neg Pred Value 0.9381 0.97154 0.94625
## Prevalence 0.1343 0.07098 0.10527
## Detection Rate 0.0790 0.04519 0.05765
## Detection Prevalence 0.1077 0.09394 0.11398
## Balanced Accuracy 0.7777 0.79213 0.74232
## Class: Jazz Class: Opera Class: Pop Class: Rap
## Sensitivity 0.42129 0.88139 0.52303 0.69020
## Specificity 0.92018 0.97915 0.97958 0.93980
## Pos Pred Value 0.30092 0.81126 0.81253 0.37447
## Neg Pred Value 0.95121 0.98783 0.92388 0.98308
## Prevalence 0.07540 0.09228 0.14473 0.04962
## Detection Rate 0.03177 0.08134 0.07570 0.03425
## Detection Prevalence 0.10557 0.10026 0.09316 0.09146
## Balanced Accuracy 0.67074 0.93027 0.75130 0.81500
## Class: Reggaeton Class: Rock Class: Ska
## Sensitivity 0.45615 0.39259 0.67171
## Specificity 0.95623 0.95927 0.93122
## Pos Pred Value 0.65519 0.54092 0.40163
## Neg Pred Value 0.90604 0.92816 0.97634
## Prevalence 0.15421 0.10892 0.06431
## Detection Rate 0.07034 0.04276 0.04320
## Detection Prevalence 0.10737 0.07905 0.10756
## Balanced Accuracy 0.70619 0.67593 0.80146
El ratio de aciertos general es de 0.5612
Probamos con las nuevas variables:
Datamodel3_clean2I<-Datamodel3%>% select(-artist_name,-track_id,-track_name,-duration_min, -mode_factor,-idioma,-liveness,-tempo, -duration_ms, -loudness, -valence, -name_freq)
set.seed(34)
index3_2I <- createDataPartition(Datamodel3_clean2I$genre, p = 0.75, list = FALSE)
spoti_train3_2I <- Datamodel3_clean2I[ index3_2I,]
spoti_test3_2I <- Datamodel3_clean2I[-index3_2I,]
set.seed(34)
dec_tree2I <- rpart(genre ~ ., data = spoti_train3_2I)
rpart.plot(dec_tree2I,
type = 5,
extra = 104,
box.palette = 'Blue',
leaf.round = 0,
fallen.leaves = FALSE,
branch = 0.3,
under = TRUE,
under.col = 'grey40',
family = 'Avenir',
main = 'Genre Decision Tree',
tweak = 1.2)Aplico el modelo a los datos de test:
dec_tree_result2I<- predict(object=dec_tree2I,spoti_test3_2I ,type="class")
t2I<-table(spoti_test3_2I $genre,dec_tree_result2I)
confusionMatrix(t2I)## Confusion Matrix and Statistics
##
## dec_tree_result2I
## Classical Country Electronic Jazz Opera Pop Rap Reggaeton Rock
## Classical 1646 31 57 121 252 1 0 57 11
## Country 101 892 36 74 0 105 10 383 293
## Electronic 26 72 1168 450 3 34 39 308 150
## Jazz 593 155 268 683 5 19 31 263 109
## Opera 243 0 0 62 1739 0 0 0 0
## Pop 85 1 2 0 0 1528 40 6 252
## Rap 22 6 5 5 0 610 732 132 367
## Reggaeton 2 101 66 58 2 139 94 1512 124
## Rock 64 41 21 16 0 465 78 33 900
## Ska 13 110 408 146 12 12 6 613 57
## dec_tree_result2I
## Ska
## Classical 37
## Country 37
## Electronic 93
## Jazz 44
## Opera 17
## Pop 1
## Rap 1
## Reggaeton 109
## Rock 7
## Ska 834
##
## Overall Statistics
##
## Accuracy : 0.566
## 95% CI : (0.5592, 0.5728)
## No Information Rate : 0.1609
## P-Value [Acc > NIR] : < 2.2e-16
##
## Kappa : 0.5175
##
## Mcnemar's Test P-Value : NA
##
## Statistics by Class:
##
## Class: Classical Class: Country Class: Electronic
## Sensitivity 0.58891 0.63307 0.57509
## Specificity 0.96808 0.94574 0.93657
## Pos Pred Value 0.74379 0.46194 0.49851
## Neg Pred Value 0.93736 0.97224 0.95262
## Prevalence 0.13597 0.06854 0.09880
## Detection Rate 0.08007 0.04339 0.05682
## Detection Prevalence 0.10766 0.09394 0.11398
## Balanced Accuracy 0.77849 0.78940 0.75583
## Class: Jazz Class: Opera Class: Pop Class: Rap
## Sensitivity 0.42291 0.86388 0.52455 0.71068
## Specificity 0.92149 0.98263 0.97806 0.94121
## Pos Pred Value 0.31475 0.84377 0.79791 0.38936
## Neg Pred Value 0.94931 0.98519 0.92570 0.98404
## Prevalence 0.07857 0.09793 0.14171 0.05011
## Detection Rate 0.03323 0.08460 0.07433 0.03561
## Detection Prevalence 0.10557 0.10026 0.09316 0.09146
## Balanced Accuracy 0.67220 0.92326 0.75131 0.82594
## Class: Reggaeton Class: Rock Class: Ska
## Sensitivity 0.45721 0.39770 0.70678
## Specificity 0.95971 0.96037 0.92893
## Pos Pred Value 0.68509 0.55385 0.37720
## Neg Pred Value 0.90217 0.92800 0.98114
## Prevalence 0.16088 0.11009 0.05740
## Detection Rate 0.07356 0.04378 0.04057
## Detection Prevalence 0.10737 0.07905 0.10756
## Balanced Accuracy 0.70846 0.67903 0.81786
El resultado obtenido es prácticamente el mismo, aunque, como vemos en el gráfico siguiente con la importancia de cada variable, las nuevas variables han aportado a la hora de crear los árboles de decisión:
dec_tree_imp <- dec_tree2I$variable.importance
dec_tree_imp<- as.data.frame(dec_tree_imp )
dec_tree_imp $myvar <- rownames(dec_tree_imp)
dec_tree_imp <- as.data.table(dec_tree_imp)
setorder(dec_tree_imp , -dec_tree_imp )
ggbarplot(dec_tree_imp[1:10],
x = "myvar", y = "dec_tree_imp",
#fill = 'myvar',
color = "green",
palette = "paired",
sort.val = "asc",
sort.by.groups = FALSE,
x.text.angle = 90,
ylab = "Importancia",
xlab = 'Variable',
rotate = TRUE,
ggtheme = theme_minimal()
)Creo una tabla con los resultados para posteriormente comparar este modelo:
Probamos el modelo con el Datamodel 3 y las variables originales:
#datos de entrenamiento
x_train=spoti_train3[,-1]
y_train=spoti_train3$genre
#datos de test
x_test=spoti_test3[,-1]
y_test=spoti_test3$genre
detach(package:class)
library(class)
set.seed(34)
knn_pred <- knn(train = scale(x_train),
test = scale(x_test),
cl = y_train,
k = 20,
prob = TRUE)Confusion matrix:
## Confusion Matrix and Statistics
##
## Reference
## Prediction Classical Country Electronic Jazz Opera Pop Rap Reggaeton Rock
## Classical 1521 4 33 168 234 5 0 2 13
## Country 35 1347 180 247 8 62 48 137 319
## Electronic 72 45 1359 266 0 10 27 98 59
## Jazz 102 90 227 1100 20 31 20 33 85
## Opera 442 7 9 53 1797 0 0 0 1
## Pop 3 87 47 27 0 1182 345 75 338
## Rap 2 29 90 68 1 326 1192 197 53
## Reggaeton 2 101 221 154 1 40 93 1544 32
## Rock 11 146 65 43 0 257 148 16 702
## Ska 23 75 112 44 0 2 7 105 23
## Reference
## Prediction Ska
## Classical 4
## Country 207
## Electronic 104
## Jazz 76
## Opera 4
## Pop 13
## Rap 17
## Reggaeton 238
## Rock 51
## Ska 1497
##
## Overall Statistics
##
## Accuracy : 0.6441
## 95% CI : (0.6376, 0.6507)
## No Information Rate : 0.114
## P-Value [Acc > NIR] : < 2.2e-16
##
## Kappa : 0.6044
##
## Mcnemar's Test P-Value : NA
##
## Statistics by Class:
##
## Class: Classical Class: Country Class: Electronic
## Sensitivity 0.68730 0.69757 0.58003
## Specificity 0.97476 0.93326 0.96261
## Pos Pred Value 0.76663 0.52008 0.66618
## Neg Pred Value 0.96274 0.96749 0.94686
## Prevalence 0.10766 0.09394 0.11398
## Detection Rate 0.07399 0.06553 0.06611
## Detection Prevalence 0.09652 0.12600 0.09924
## Balanced Accuracy 0.83103 0.81541 0.77132
## Class: Jazz Class: Opera Class: Pop Class: Rap
## Sensitivity 0.50691 0.87191 0.61723 0.63404
## Specificity 0.96280 0.97210 0.94984 0.95807
## Pos Pred Value 0.61659 0.77691 0.55834 0.60354
## Neg Pred Value 0.94300 0.98553 0.96025 0.96297
## Prevalence 0.10557 0.10026 0.09316 0.09146
## Detection Rate 0.05351 0.08742 0.05750 0.05799
## Detection Prevalence 0.08679 0.11252 0.10299 0.09608
## Balanced Accuracy 0.73486 0.92200 0.78354 0.79606
## Class: Reggaeton Class: Rock Class: Ska
## Sensitivity 0.69959 0.43200 0.67707
## Specificity 0.95193 0.96107 0.97869
## Pos Pred Value 0.63644 0.48784 0.79290
## Neg Pred Value 0.96343 0.95172 0.96175
## Prevalence 0.10737 0.07905 0.10756
## Detection Rate 0.07511 0.03415 0.07283
## Detection Prevalence 0.11802 0.07000 0.09185
## Balanced Accuracy 0.82576 0.69653 0.82788
Probamos con las nuevas variables:
Este modelo no acepta variables categóricas, así que creo una variable dummy para el idioma:
idioma2_dummy <- as.data.frame(dummy.code(Datamodel3_clean2$idioma2))
#combino la variable dummy con el dataset
Datamodel3_clean2_dummy <- cbind(Datamodel3_clean2, idioma2_dummy)
Datamodel3_clean2_dummy$idioma2 <-NULL Genero los sets de entrenamiento y test:
set.seed(5)
index3_dummy <- createDataPartition(Datamodel3_clean2_dummy$genre, p = 0.75, list = FALSE)
spoti_train3_2_dummy <- Datamodel3_clean2_dummy[ index3_dummy,]
spoti_test3_2_dummy <- Datamodel3_clean2_dummy[-index3_dummy,]
#datos de entrenamiento
x_train2=spoti_train3_2_dummy[,-1]
y_train2=spoti_train3_2_dummy$genre
#datos de test
x_test2=spoti_test3_2_dummy[,-1]
y_test2=spoti_test3_2_dummy$genreEntreno el modelo:
set.seed(34)
detach(package:class)
library(class)
knn_pred2 <- knn(train = scale(x_train2),
test = scale(x_test2),
cl = y_train2,
k = 15,
prob = TRUE)Confusion matrix:
## Confusion Matrix and Statistics
##
## Reference
## Prediction Classical Country Electronic Jazz Opera Pop Rap Reggaeton Rock
## Classical 1619 5 34 150 256 5 0 1 18
## Country 37 1305 139 213 4 105 85 97 318
## Electronic 87 71 1409 284 0 23 51 103 73
## Jazz 133 108 233 1131 20 27 29 40 80
## Opera 306 5 7 38 1778 0 0 1 2
## Pop 1 84 43 24 0 1061 334 54 309
## Rap 3 55 85 75 0 350 1103 130 61
## Reggaeton 4 90 188 150 2 64 135 1707 36
## Rock 12 142 72 41 1 268 131 9 681
## Ska 11 66 133 64 0 12 12 65 47
## Reference
## Prediction Ska
## Classical 1
## Country 81
## Electronic 114
## Jazz 75
## Opera 1
## Pop 7
## Rap 11
## Reggaeton 176
## Rock 34
## Ska 1711
##
## Overall Statistics
##
## Accuracy : 0.657
## 95% CI : (0.6505, 0.6635)
## No Information Rate : 0.114
## P-Value [Acc > NIR] : < 2.2e-16
##
## Kappa : 0.6185
##
## Mcnemar's Test P-Value : NA
##
## Statistics by Class:
##
## Class: Classical Class: Country Class: Electronic
## Sensitivity 0.73159 0.67582 0.60137
## Specificity 0.97438 0.94207 0.95575
## Pos Pred Value 0.77501 0.54740 0.63612
## Neg Pred Value 0.96783 0.96555 0.94908
## Prevalence 0.10766 0.09394 0.11398
## Detection Rate 0.07876 0.06349 0.06854
## Detection Prevalence 0.10162 0.11598 0.10775
## Balanced Accuracy 0.85298 0.80894 0.77856
## Class: Jazz Class: Opera Class: Pop Class: Rap
## Sensitivity 0.52120 0.8627 0.55405 0.58670
## Specificity 0.95948 0.9805 0.95408 0.95877
## Pos Pred Value 0.60288 0.8316 0.55347 0.58889
## Neg Pred Value 0.94438 0.9846 0.95418 0.95841
## Prevalence 0.10557 0.1003 0.09316 0.09146
## Detection Rate 0.05502 0.0865 0.05162 0.05366
## Detection Prevalence 0.09126 0.1040 0.09326 0.09112
## Balanced Accuracy 0.74034 0.9216 0.75406 0.77274
## Class: Reggaeton Class: Rock Class: Ska
## Sensitivity 0.77345 0.41908 0.77386
## Specificity 0.95395 0.96250 0.97765
## Pos Pred Value 0.66889 0.48958 0.80669
## Neg Pred Value 0.97223 0.95074 0.97288
## Prevalence 0.10737 0.07905 0.10756
## Detection Rate 0.08304 0.03313 0.08324
## Detection Prevalence 0.12415 0.06767 0.10318
## Balanced Accuracy 0.86370 0.69079 0.87575
El resultado mejora ligeramente (pasa de una accuracy de 0.6441 a 0.657).
Un exceso de variables, podría dificultar la clasificación de este algoritmo, y más aún si tienen una correlación alta.
En la primera parte del análisis habíamos visto que las variables energy y loudness estaban altamente correlacionadas. Voy a probar a excluirlas del modelo:
#datos de entrenamiento
x_train_ex=spoti_train3_2_dummy[,-1]
y_train_ex=spoti_train3_2_dummy$genre
#datos de test
x_test_ex=spoti_test3_2_dummy[,-1]
y_test_ex=spoti_test3_2_dummy$genre
x_train_ex$energy<-NULL
x_test_ex$energy<-NULL
set.seed(34)
detach(package:class)
library(class)
knn_pred2_ex <- knn(train = scale(x_train_ex),
test = scale(x_test_ex),
cl = y_train_ex,
k = 15,
prob = TRUE)
confusionMatrix(knn_pred2_ex ,y_test_ex)## Confusion Matrix and Statistics
##
## Reference
## Prediction Classical Country Electronic Jazz Opera Pop Rap Reggaeton Rock
## Classical 1611 2 33 147 261 5 0 1 17
## Country 41 1299 149 203 2 90 91 95 291
## Electronic 87 76 1395 317 0 23 62 127 69
## Jazz 133 98 222 1099 21 24 25 36 89
## Opera 305 5 9 34 1775 0 0 1 2
## Pop 0 89 49 28 0 1056 324 53 324
## Rap 2 53 100 72 0 358 1104 126 70
## Reggaeton 4 90 179 153 2 69 129 1690 39
## Rock 15 140 74 44 0 276 133 7 669
## Ska 15 79 133 73 0 14 12 71 55
## Reference
## Prediction Ska
## Classical 2
## Country 83
## Electronic 111
## Jazz 69
## Opera 2
## Pop 7
## Rap 10
## Reggaeton 188
## Rock 34
## Ska 1705
##
## Overall Statistics
##
## Accuracy : 0.652
## 95% CI : (0.6455, 0.6585)
## No Information Rate : 0.114
## P-Value [Acc > NIR] : < 2.2e-16
##
## Kappa : 0.6129
##
## Mcnemar's Test P-Value : NA
##
## Statistics by Class:
##
## Class: Classical Class: Country Class: Electronic
## Sensitivity 0.72797 0.67271 0.59539
## Specificity 0.97449 0.94389 0.95212
## Pos Pred Value 0.77489 0.55418 0.61535
## Neg Pred Value 0.96742 0.96530 0.94817
## Prevalence 0.10766 0.09394 0.11398
## Detection Rate 0.07837 0.06319 0.06786
## Detection Prevalence 0.10114 0.11403 0.11028
## Balanced Accuracy 0.85123 0.80830 0.77376
## Class: Jazz Class: Opera Class: Pop Class: Rap
## Sensitivity 0.50645 0.86123 0.55144 0.58723
## Specificity 0.96100 0.98064 0.95311 0.95765
## Pos Pred Value 0.60518 0.83216 0.54715 0.58259
## Neg Pred Value 0.94285 0.98448 0.95388 0.95842
## Prevalence 0.10557 0.10026 0.09316 0.09146
## Detection Rate 0.05346 0.08635 0.05137 0.05371
## Detection Prevalence 0.08834 0.10377 0.09389 0.09219
## Balanced Accuracy 0.73373 0.92094 0.75228 0.77244
## Class: Reggaeton Class: Rock Class: Ska
## Sensitivity 0.76575 0.41169 0.77114
## Specificity 0.95351 0.96181 0.97536
## Pos Pred Value 0.66457 0.48060 0.79045
## Neg Pred Value 0.97130 0.95011 0.97250
## Prevalence 0.10737 0.07905 0.10756
## Detection Rate 0.08221 0.03255 0.08294
## Detection Prevalence 0.12371 0.06772 0.10493
## Balanced Accuracy 0.85963 0.68675 0.87325
Al excluir la variable loudness la precisión se reduce ligeramente: 0.652 y al excluir energy 0.6511. Como habíamos visto anteriormente, estas eran variables importantes al aplicar PCA.
Pero si excluímos la variable liveness ganamos en precisión:
#datos de entrenamiento
x_train_ex=spoti_train3_2_dummy[,-1]
y_train_ex=spoti_train3_2_dummy$genre
#datos de test
x_test_ex=spoti_test3_2_dummy[,-1]
y_test_ex=spoti_test3_2_dummy$genre
x_train_ex$liveness<-NULL
x_test_ex$liveness<-NULL
set.seed(34)
detach(package:class)
library(class)
knn_pred2_ex <- knn(train = scale(x_train_ex),
test = scale(x_test_ex),
cl = y_train_ex,
k = 15,
prob = TRUE)
confusionMatrix(knn_pred2_ex ,y_test_ex)## Confusion Matrix and Statistics
##
## Reference
## Prediction Classical Country Electronic Jazz Opera Pop Rap Reggaeton Rock
## Classical 1622 8 34 157 238 5 0 1 15
## Country 33 1323 138 210 3 90 71 84 296
## Electronic 84 65 1439 290 0 20 43 101 64
## Jazz 123 107 228 1126 21 30 28 40 84
## Opera 321 3 8 36 1798 0 0 0 2
## Pop 1 79 44 24 0 1083 330 44 302
## Rap 3 53 86 73 0 356 1131 136 60
## Reggaeton 3 90 191 142 1 64 117 1723 40
## Rock 11 144 59 45 0 254 149 10 710
## Ska 12 59 116 67 0 13 11 68 52
## Reference
## Prediction Ska
## Classical 2
## Country 84
## Electronic 113
## Jazz 75
## Opera 0
## Pop 6
## Rap 12
## Reggaeton 169
## Rock 34
## Ska 1716
##
## Overall Statistics
##
## Accuracy : 0.6651
## 95% CI : (0.6586, 0.6715)
## No Information Rate : 0.114
## P-Value [Acc > NIR] : < 2.2e-16
##
## Kappa : 0.6275
##
## Mcnemar's Test P-Value : NA
##
## Statistics by Class:
##
## Class: Classical Class: Country Class: Electronic
## Sensitivity 0.73294 0.68514 0.6142
## Specificity 0.97492 0.94583 0.9572
## Pos Pred Value 0.77906 0.56732 0.6485
## Neg Pred Value 0.96801 0.96664 0.9507
## Prevalence 0.10766 0.09394 0.1140
## Detection Rate 0.07891 0.06436 0.0700
## Detection Prevalence 0.10128 0.11345 0.1079
## Balanced Accuracy 0.85393 0.81548 0.7857
## Class: Jazz Class: Opera Class: Pop Class: Rap
## Sensitivity 0.51889 0.87239 0.56554 0.60160
## Specificity 0.95997 0.97999 0.95547 0.95829
## Pos Pred Value 0.60473 0.82934 0.56613 0.59215
## Neg Pred Value 0.94415 0.98570 0.95537 0.95983
## Prevalence 0.10557 0.10026 0.09316 0.09146
## Detection Rate 0.05478 0.08747 0.05269 0.05502
## Detection Prevalence 0.09058 0.10547 0.09306 0.09292
## Balanced Accuracy 0.73943 0.92619 0.76050 0.77994
## Class: Reggaeton Class: Rock Class: Ska
## Sensitivity 0.78070 0.43692 0.77612
## Specificity 0.95547 0.96271 0.97830
## Pos Pred Value 0.67835 0.50141 0.81173
## Neg Pred Value 0.97313 0.95219 0.97316
## Prevalence 0.10737 0.07905 0.10756
## Detection Rate 0.08382 0.03454 0.08348
## Detection Prevalence 0.12356 0.06888 0.10284
## Balanced Accuracy 0.86809 0.69981 0.87721
Creo una tabla con los mejores resultados para posteriormente comparar este modelo:
Probamos un modelo de XGBoost. Como en los casos anteriores, primero con las variables originales y posteriormente incluyendo las nuevas.
Vamos a seleccionar un valor de nrounds=50.
matrix_train_gb <- xgb.DMatrix(as.matrix(sapply(spoti_train3[,-1], as.numeric)), label=spoti_train3$genre)
matrix_test_gb <- xgb.DMatrix(as.matrix(sapply(spoti_test3[,-1], as.numeric)), label=spoti_test3$genre)
set.seed(34)
model_gb <- xgboost(data = matrix_train_gb,
nrounds = 50,
verbose = FALSE,
params = list(objective = "multi:softmax",
num_class = 10+1))
predict_gb <- predict(model_gb, matrix_test_gb)
predict_gb <- levels(as.factor(spoti_test3$genre))[predict_gb]
#creo funcion para obtener la accuracy
detach(package:tcR)
detach(package:dplyr)
library(dplyr)
model_accuracy_calc <- function(df, model_name) {
df %>%
mutate(match = ifelse(true_value == predicted_value, TRUE, FALSE)) %>%
count(match) %>%
mutate(accuracy = n/sum(n),
model = model_name)}
compare_gb <- data.frame(true_value = spoti_test3$genre,
predicted_value = predict_gb,
model = 'xgboost',
stringsAsFactors = FALSE)
accuracy_gb <- model_accuracy_calc(df = compare_gb, model_name = 'xgboost')
print(accuracy_gb)## # A tibble: 2 x 4
## match n accuracy model
## <lgl> <int> <dbl> <chr>
## 1 FALSE 5459 0.266 xgboost
## 2 TRUE 15097 0.734 xgboost
Probamos añadiendo las nuevas variables y excluyendo la variable ‘liveness’:
spoti_train3_2_dummy$liveness<-NULL
spoti_test3_2_dummy$liveness<-NULL
matrix_train_gb2 <- xgb.DMatrix(as.matrix(sapply(spoti_train3_2_dummy[,-1], as.numeric)), label=spoti_train3_2_dummy$genre)
matrix_test_gb2 <- xgb.DMatrix(as.matrix(sapply(spoti_test3_2_dummy[,-1], as.numeric)), label=spoti_test3_2_dummy$genre)
set.seed(34)
model_gb2 <- xgboost(data = matrix_train_gb2,
nrounds = 50,
verbose = FALSE,
params = list(objective = "multi:softmax",
num_class = 10 + 1))
predict_gb2 <- predict(model_gb2, matrix_test_gb2)
predict_gb2 <- levels(as.factor(spoti_test3_2_dummy$genre))[predict_gb2]
#creo un dataframe con los resultados
compare_gb2 <- data.frame(true_value = spoti_test3_2_dummy$genre,
predicted_value = predict_gb2,
model = 'XGBoost',
stringsAsFactors = FALSE)
accuracy_gb2 <- model_accuracy_calc(df = compare_gb2, model_name = 'XGBoost')
print(accuracy_gb2)## # A tibble: 2 x 4
## match n accuracy model
## <lgl> <int> <dbl> <chr>
## 1 FALSE 3722 0.181 XGBoost
## 2 TRUE 16834 0.819 XGBoost
Obtenemos el ratio de acierto más alto: 0.8189.
Creo una tabla con los mejores resultados para posteriormente comparar este modelo:
#ya había creado antes el dataframe con los resultados de la predicción:
compare_XGBoost <- compare_gb2 Importancia de las variables
importance_XGBoost<-xgb.importance(model=model_gb2)
ggbarplot(importance_XGBoost[1:15],
x = "Feature", y = "Gain",
#fill = 'myvar',
color = "green",
palette = "paired",
sort.val = "asc",
sort.by.groups = FALSE,
x.text.angle = 90,
ylab = "Importancia",
xlab = 'Variable',
rotate = TRUE,
ggtheme = theme_minimal()
)Probamos el modelo Naive Bayes. Al igual que los casos anteriores, seleccionamos el Datamodel3 y las variables iniciales:
set.seed(34)
naive_model <- naiveBayes(as.factor(genre) ~. , data=spoti_train3)
test_naive=predict(naive_model, spoti_test3)
testTable=table(spoti_test3$genre, test_naive)
confusionMatrix(testTable)## Confusion Matrix and Statistics
##
## test_naive
## Classical Country Electronic Jazz Opera Pop Rap Reggaeton Rock
## Classical 1439 40 75 103 491 0 2 10 18
## Country 27 1337 10 77 3 56 15 270 43
## Electronic 52 224 954 255 2 25 101 443 59
## Jazz 346 330 370 758 30 14 44 211 20
## Opera 240 9 4 37 1768 0 2 0 0
## Pop 38 219 9 18 0 1008 330 164 123
## Rap 0 164 15 30 0 249 1005 349 66
## Reggaeton 5 166 26 35 0 56 118 1734 1
## Rock 53 573 70 69 0 246 67 136 381
## Ska 5 255 121 66 1 4 15 512 21
## test_naive
## Ska
## Classical 35
## Country 93
## Electronic 228
## Jazz 47
## Opera 1
## Pop 6
## Rap 2
## Reggaeton 66
## Rock 30
## Ska 1211
##
## Overall Statistics
##
## Accuracy : 0.5641
## 95% CI : (0.5573, 0.5709)
## No Information Rate : 0.1863
## P-Value [Acc > NIR] : < 2.2e-16
##
## Kappa : 0.5149
##
## Mcnemar's Test P-Value : NA
##
## Statistics by Class:
##
## Class: Classical Class: Country Class: Electronic
## Sensitivity 0.6526 0.40308 0.57678
## Specificity 0.9578 0.96554 0.92652
## Pos Pred Value 0.6502 0.69239 0.40717
## Neg Pred Value 0.9582 0.89369 0.96157
## Prevalence 0.1073 0.16136 0.08046
## Detection Rate 0.0700 0.06504 0.04641
## Detection Prevalence 0.1077 0.09394 0.11398
## Balanced Accuracy 0.8052 0.68431 0.75165
## Class: Jazz Class: Opera Class: Pop Class: Rap
## Sensitivity 0.52348 0.77037 0.60796 0.59152
## Specificity 0.92610 0.98395 0.95201 0.95360
## Pos Pred Value 0.34931 0.85784 0.52637 0.53457
## Neg Pred Value 0.96247 0.97151 0.96513 0.96284
## Prevalence 0.07044 0.11165 0.08066 0.08265
## Detection Rate 0.03687 0.08601 0.04904 0.04889
## Detection Prevalence 0.10557 0.10026 0.09316 0.09146
## Balanced Accuracy 0.72479 0.87716 0.77998 0.77256
## Class: Reggaeton Class: Rock Class: Ska
## Sensitivity 0.45286 0.52049 0.70448
## Specificity 0.97172 0.93725 0.94691
## Pos Pred Value 0.78568 0.23446 0.54772
## Neg Pred Value 0.88582 0.98146 0.97231
## Prevalence 0.18627 0.03561 0.08363
## Detection Rate 0.08435 0.01853 0.05891
## Detection Prevalence 0.10737 0.07905 0.10756
## Balanced Accuracy 0.71229 0.72887 0.82570
El resultado no es muy bueno, comparado con el resto de modelos. Hay que tener en cuenta este modelo asume que la distribución de las variables numéricas es normal. Vamos a probar a excluir variables tempo y energy, ya que presentan distribuciones lejos de ser normales. Además, la variable energy, estaba altamente correlada con loudness:
#creo una copia del data model
spoti_train3_naive<-copy(spoti_train3)
spoti_test3_naive<-copy(spoti_test3)
#quito las variables que no quiero incluir en el modelo:
spoti_train3_naive$energy<-NULL
spoti_test3_naive$energy<-NULL
set.seed(34)
naive_model_2 <- naiveBayes(as.factor(genre) ~. , data=spoti_train3_naive)
test_naive2=predict(naive_model_2, spoti_test3_naive)
testTable2=table(spoti_test3_naive$genre, test_naive2)
confusionMatrix(testTable2)## Confusion Matrix and Statistics
##
## test_naive2
## Classical Country Electronic Jazz Opera Pop Rap Reggaeton Rock
## Classical 1497 45 79 109 424 0 2 13 19
## Country 24 1427 12 49 4 60 13 220 45
## Electronic 61 262 980 225 1 26 117 425 59
## Jazz 315 363 368 738 19 14 36 235 32
## Opera 265 10 4 36 1742 0 2 0 0
## Pop 28 217 10 11 0 1069 335 109 133
## Rap 0 176 21 24 0 256 995 339 67
## Reggaeton 5 210 25 27 0 62 112 1701 1
## Rock 45 599 71 54 0 248 68 121 398
## Ska 7 300 122 60 1 4 11 540 22
## test_naive2
## Ska
## Classical 25
## Country 77
## Electronic 187
## Jazz 50
## Opera 2
## Pop 3
## Rap 2
## Reggaeton 64
## Rock 21
## Ska 1144
##
## Overall Statistics
##
## Accuracy : 0.5687
## 95% CI : (0.5619, 0.5755)
## No Information Rate : 0.1801
## P-Value [Acc > NIR] : < 2.2e-16
##
## Kappa : 0.5202
##
## Mcnemar's Test P-Value : NA
##
## Statistics by Class:
##
## Class: Classical Class: Country Class: Electronic
## Sensitivity 0.66622 0.39540 0.57920
## Specificity 0.96089 0.97026 0.92775
## Pos Pred Value 0.67646 0.73900 0.41827
## Neg Pred Value 0.95911 0.88285 0.96091
## Prevalence 0.10931 0.17557 0.08231
## Detection Rate 0.07283 0.06942 0.04767
## Detection Prevalence 0.10766 0.09394 0.11398
## Balanced Accuracy 0.81356 0.68283 0.75347
## Class: Jazz Class: Opera Class: Pop Class: Rap
## Sensitivity 0.55364 0.79507 0.61472 0.58841
## Specificity 0.92551 0.98263 0.95504 0.95309
## Pos Pred Value 0.34009 0.84522 0.55822 0.52926
## Neg Pred Value 0.96764 0.97572 0.96406 0.96273
## Prevalence 0.06485 0.10659 0.08460 0.08226
## Detection Rate 0.03590 0.08474 0.05200 0.04840
## Detection Prevalence 0.10557 0.10026 0.09316 0.09146
## Balanced Accuracy 0.73957 0.88885 0.78488 0.77075
## Class: Reggaeton Class: Rock Class: Ska
## Sensitivity 0.45936 0.51289 0.72635
## Specificity 0.96998 0.93797 0.94379
## Pos Pred Value 0.77073 0.24492 0.51741
## Neg Pred Value 0.89089 0.98003 0.97651
## Prevalence 0.18014 0.03775 0.07662
## Detection Rate 0.08275 0.01936 0.05565
## Detection Prevalence 0.10737 0.07905 0.10756
## Balanced Accuracy 0.71467 0.72543 0.83507
-Excluyendo las variables tempo y energy, la tasa de aciertos es 0.5671.
-Excluyendo las variables liveness y energy, la tasa de aciertos es 0.5682.
-Excluyendo la variable energy, la tasa de aciertos es 0.5687.
-Excluyendo la variable tempo, la tasa de aciertos es 0.5624.
Incluimos en el modelo las nuevas variables que habíamos creado:
#creo una copia del data model
spoti_train3_naive2<-copy(spoti_train3_2)
spoti_test3_naive2<-copy(spoti_test3_2)
#quito las variables que no quiero incluir en el modelo:
spoti_train3_naive2$energy<-NULL
spoti_test3_naive2$energy<-NULL
set.seed(34)
naive_model_3 <- naiveBayes(as.factor(genre) ~. , data=spoti_train3_naive2)
test_naive3=predict(naive_model_3, spoti_test3_naive2)
testTable3=table(spoti_test3_naive2$genre, test_naive3)
confusionMatrix(testTable3)## Confusion Matrix and Statistics
##
## test_naive3
## Classical Country Electronic Jazz Opera Pop Rap Reggaeton Rock
## Classical 228 35 33 43 1770 1 4 1 2
## Country 10 991 11 56 27 34 39 69 32
## Electronic 59 41 589 246 24 9 70 158 21
## Jazz 107 192 176 748 346 6 24 121 21
## Opera 31 0 3 3 2022 0 1 0 0
## Pop 30 201 7 26 0 712 386 149 192
## Rap 2 141 11 44 0 156 944 239 53
## Reggaeton 1 48 22 23 3 23 78 1062 2
## Rock 28 330 24 70 4 144 56 49 366
## Ska 15 8 43 32 6 0 3 44 2
## test_naive3
## Ska
## Classical 96
## Country 662
## Electronic 1126
## Jazz 429
## Opera 1
## Pop 212
## Rap 290
## Reggaeton 945
## Rock 554
## Ska 2058
##
## Overall Statistics
##
## Accuracy : 0.4729
## 95% CI : (0.466, 0.4797)
## No Information Rate : 0.31
## P-Value [Acc > NIR] : < 2.2e-16
##
## Kappa : 0.413
##
## Mcnemar's Test P-Value : NA
##
## Statistics by Class:
##
## Class: Classical Class: Country Class: Electronic
## Sensitivity 0.44618 0.49874 0.64091
## Specificity 0.90097 0.94938 0.91068
## Pos Pred Value 0.10303 0.51321 0.25139
## Neg Pred Value 0.98457 0.94652 0.98188
## Prevalence 0.02486 0.09666 0.04471
## Detection Rate 0.01109 0.04821 0.02865
## Detection Prevalence 0.10766 0.09394 0.11398
## Balanced Accuracy 0.67358 0.72406 0.77580
## Class: Jazz Class: Opera Class: Pop Class: Rap
## Sensitivity 0.57940 0.48120 0.65622 0.58816
## Specificity 0.92619 0.99762 0.93822 0.95061
## Pos Pred Value 0.34470 0.98108 0.37180 0.50213
## Neg Pred Value 0.97047 0.88213 0.97999 0.96461
## Prevalence 0.06280 0.20442 0.05278 0.07808
## Detection Rate 0.03639 0.09837 0.03464 0.04592
## Detection Prevalence 0.10557 0.10026 0.09316 0.09146
## Balanced Accuracy 0.75279 0.73941 0.79722 0.76939
## Class: Reggaeton Class: Rock Class: Ska
## Sensitivity 0.56131 0.52967 0.3229
## Specificity 0.93865 0.93662 0.9892
## Pos Pred Value 0.48120 0.22523 0.9308
## Neg Pred Value 0.95477 0.98283 0.7648
## Prevalence 0.09204 0.03362 0.3100
## Detection Rate 0.05166 0.01781 0.1001
## Detection Prevalence 0.10737 0.07905 0.1076
## Balanced Accuracy 0.74998 0.73314 0.6561
El resultado empeora considerablemente. Las nuevas variables están representadas como numéricas, pese a tomar valores 0/1, por lo que vamos a pasar las variables a tipo factor:
#creo una copia del data model
spoti_train3_naive3<-copy(spoti_train3_2)
spoti_test3_naive3<-copy(spoti_test3_2)
#paso a factor las variables:
spoti_train3_naive3$Billboard <- as.factor(spoti_train3_naive3$Billboard)
spoti_test3_naive3$Billboard <- as.factor(spoti_test3_naive3$Billboard)
spoti_train3_naive3$colaboracion <- as.factor (spoti_train3_naive3$colaboracion)
spoti_test3_naive3$colaboracion <- as.factor (spoti_test3_naive3$colaboracion)
spoti_train3_naive3$group <- as.factor (spoti_train3_naive3$group)
spoti_test3_naive3$group <- as.factor (spoti_test3_naive3$group)
#quito las variables que no quiero incluir en el modelo:
spoti_train3_naive3$energy<-NULL
spoti_test3_naive3$energy<-NULL
set.seed(34)
naive_model_4 <- naiveBayes(as.factor(genre) ~. , data=spoti_train3_naive3)
test_naive4=predict(naive_model_4, spoti_test3_naive3)
testTable4=table(spoti_test3_naive3$genre, test_naive4)
confusionMatrix(testTable4)## Confusion Matrix and Statistics
##
## test_naive4
## Classical Country Electronic Jazz Opera Pop Rap Reggaeton Rock
## Classical 1518 80 67 123 394 1 2 7 14
## Country 13 1449 15 67 3 64 37 182 64
## Electronic 59 220 1069 240 0 16 111 379 59
## Jazz 175 334 283 1031 2 9 32 226 36
## Opera 262 5 0 38 1753 0 1 0 0
## Pop 17 162 9 34 0 1014 370 96 212
## Rap 2 166 18 36 0 208 1130 238 79
## Reggaeton 4 120 33 38 0 41 72 1859 3
## Rock 20 504 35 73 0 229 96 82 565
## Ska 14 177 121 68 1 2 15 370 15
## test_naive4
## Ska
## Classical 7
## Country 37
## Electronic 190
## Jazz 42
## Opera 2
## Pop 1
## Rap 3
## Reggaeton 37
## Rock 21
## Ska 1428
##
## Overall Statistics
##
## Accuracy : 0.6235
## 95% CI : (0.6168, 0.6301)
## No Information Rate : 0.1673
## P-Value [Acc > NIR] : < 2.2e-16
##
## Kappa : 0.5812
##
## Mcnemar's Test P-Value : NA
##
## Statistics by Class:
##
## Class: Classical Class: Country Class: Electronic
## Sensitivity 0.72841 0.45042 0.64788
## Specificity 0.96238 0.97220 0.93261
## Pos Pred Value 0.68595 0.75039 0.45625
## Neg Pred Value 0.96914 0.90507 0.96810
## Prevalence 0.10138 0.15650 0.08027
## Detection Rate 0.07385 0.07049 0.05200
## Detection Prevalence 0.10766 0.09394 0.11398
## Balanced Accuracy 0.84539 0.71131 0.79025
## Class: Jazz Class: Opera Class: Pop Class: Rap
## Sensitivity 0.58982 0.81421 0.64015 0.60557
## Specificity 0.93944 0.98326 0.95251 0.95987
## Pos Pred Value 0.47512 0.85056 0.52950 0.60106
## Neg Pred Value 0.96100 0.97837 0.96942 0.96059
## Prevalence 0.08504 0.10474 0.07706 0.09078
## Detection Rate 0.05016 0.08528 0.04933 0.05497
## Detection Prevalence 0.10557 0.10026 0.09316 0.09146
## Balanced Accuracy 0.76463 0.89874 0.79633 0.78272
## Class: Reggaeton Class: Rock Class: Ska
## Sensitivity 0.54056 0.53964 0.80769
## Specificity 0.97967 0.94567 0.95832
## Pos Pred Value 0.84232 0.34769 0.64586
## Neg Pred Value 0.91389 0.97454 0.98147
## Prevalence 0.16730 0.05093 0.08601
## Detection Rate 0.09044 0.02749 0.06947
## Detection Prevalence 0.10737 0.07905 0.10756
## Balanced Accuracy 0.76012 0.74265 0.88301
Alcanzamos el mejor resultado para este modelo: 0.6235
Creo una tabla con los mejores resultados para posteriormente comparar este modelo:
Comparamos los aciertos para cada género y cada modelo:
compare_rf %>%
rbind(compare_naive) %>%
rbind(compare_dt) %>%
rbind(compare_XGBoost) %>%
rbind(compare_knn)%>%
count(true_value, predicted_value, model) %>%
mutate(match = ifelse(true_value == predicted_value, TRUE, FALSE)) %>%
group_by(true_value, model) %>%
mutate(pct = n/sum(n)) %>%
ungroup() %>%
mutate(label = ifelse(match == TRUE,
paste0(round(pct * 100,1),'%'),
"")) %>%
ggplot(aes(x = true_value,
y = pct,
fill = predicted_value,
label = label)) +
geom_col(position = 'dodge') +
geom_text(position = position_dodge(width = 0.9),
cex = 2.75,
hjust = -0.1) +
facet_wrap( ~ model, ncol = 1) +
coord_flip() +
labs(title = 'Genre Accuracy by Model',
subtitle = 'Accuracy denoted as a percent label',
y = 'Percent classified') +
ylim(c(0,1)) + theme(panel.spacing = unit(2, "lines")) +
theme(panel.grid.major.y = element_blank()) + theme_minimal() + scale_x_discrete(expand = c(0,1)) + theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank(),
panel.background = element_blank(), axis.line = element_line(colour = "black"))Voy a intentar crear un “recomendador” basado en la distancia euclídea de los Componentes principales de una canción:
Selección de las canciones sobre las que voy a aplicar el recomendador:
#Selecciono solo canciones de Rock
rock <- filter(SpotifyData, genre == "Rock")
#Selecciono una muestra de 100 canciones de Rock
recomendador <- rock[sample(nrow(rock), 1000), ]
#Quito las variables que he creado nuevas, ya que no representan un valor propio de la canción.
feat_names<-names(recomendador)[5:18]
recomendadorII <- recomendador %>% select(feat_names)Calculo de PC y matriz de distancia:
#Calculo principal components de esta mestra
recomendador_pca=prcomp(recomendadorII, center = TRUE,scale = TRUE)
#Calculo la distancia eclídea entre componentes principales de cada canción
library(tcR)
distancia<-pca2euclid(recomendador_pca)
colnames(distancia) <- recomendador$track_name
row.names(distancia) <- recomendador$track_nameBusco canciones similares a la canción “Bad Moon Rising”, de The Creedence Clearwater Revival:
## Bad Moon Rising Only the Good Die Young
## 0.00000000 0.04360699
## Call Me I Think We're Alone Now
## 0.05012275 0.06275129
## Been Caught Stealing Little Miss Can't Be Wrong
## 0.10560624 0.10909655
## Hypocrite Back on the Chain Gang - 2007 Remaster
## 0.10992172 0.15960859
## Sweet Child O' Mine Free And Easy (Down The Road I Go)
## 0.18874529 0.20312027
## Angel in Blue Jeans
## 0.21602182