Uma das minhas músicas favoritas nos últimos tempos tem sido Weird Fishes/Arpeggi do Radiohead. Ela tem sido tão favorita que estou usando as recomendações do Deezer para encontrar outras canções parecidas, mas sem sucesso. O software sugere muita coisa, mas nada que tenha chegado perto de um resultado que me agrade.
Pensando nisso, baixei um conjunto de dados do Kaggle com 1,2 milhão de canções retiradas do Spotify, a fim de encontrar aquelas mais parecidas com Weird Fishes/Arpeggi.
library(janitor)
##
## Attaching package: 'janitor'
## The following objects are masked from 'package:stats':
##
## chisq.test, fisher.test
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.4 ✔ readr 2.1.5
## ✔ forcats 1.0.0 ✔ stringr 1.5.1
## ✔ ggplot2 3.5.0 ✔ tibble 3.2.1
## ✔ lubridate 1.9.3 ✔ tidyr 1.3.1
## ✔ purrr 1.0.2
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
theme_set(theme_bw())
spotify <- read_csv(file = "dados/tracks_features.csv")
## Rows: 1204025 Columns: 24
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (7): id, name, album, album_id, artists, artist_ids, release_date
## dbl (16): track_number, disc_number, danceability, energy, key, loudness, mo...
## lgl (1): explicit
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
glimpse(spotify)
## Rows: 1,204,025
## Columns: 24
## $ id <chr> "7lmeHLHBe4nmXzuXc0HDjk", "1wsRitfRRtWyEapl0q22o8", "…
## $ name <chr> "Testify", "Guerrilla Radio", "Calm Like a Bomb", "Mi…
## $ album <chr> "The Battle Of Los Angeles", "The Battle Of Los Angel…
## $ album_id <chr> "2eia0myWFgoHuttJytCxgX", "2eia0myWFgoHuttJytCxgX", "…
## $ artists <chr> "['Rage Against The Machine']", "['Rage Against The M…
## $ artist_ids <chr> "['2d0hyoQ5ynDBnkvAbJKORj']", "['2d0hyoQ5ynDBnkvAbJKO…
## $ track_number <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5,…
## $ disc_number <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,…
## $ explicit <lgl> FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE,…
## $ danceability <dbl> 0.470, 0.599, 0.315, 0.440, 0.426, 0.298, 0.417, 0.27…
## $ energy <dbl> 0.978, 0.957, 0.970, 0.967, 0.929, 0.848, 0.976, 0.87…
## $ key <dbl> 7, 11, 7, 11, 2, 2, 9, 11, 7, 9, 7, 6, 4, 7, 1, 7, 4,…
## $ loudness <dbl> -5.399, -5.764, -5.424, -5.830, -6.729, -5.947, -6.03…
## $ mode <dbl> 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1,…
## $ speechiness <dbl> 0.0727, 0.1880, 0.4830, 0.2370, 0.0701, 0.0727, 0.175…
## $ acousticness <dbl> 0.026100, 0.012900, 0.023400, 0.163000, 0.001620, 0.0…
## $ instrumentalness <dbl> 1.09e-05, 7.06e-05, 2.03e-06, 3.64e-06, 1.05e-01, 1.5…
## $ liveness <dbl> 0.3560, 0.1550, 0.1220, 0.1210, 0.0789, 0.2010, 0.107…
## $ valence <dbl> 0.503, 0.489, 0.370, 0.574, 0.539, 0.194, 0.483, 0.61…
## $ tempo <dbl> 117.906, 103.680, 149.749, 96.752, 127.059, 148.282, …
## $ duration_ms <dbl> 210133, 206200, 298893, 213640, 205600, 280960, 20204…
## $ time_signature <dbl> 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,…
## $ year <dbl> 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999,…
## $ release_date <chr> "1999-11-02", "1999-11-02", "1999-11-02", "1999-11-02…
# limpar nome dos artistas
spotify <-
spotify |>
mutate(artists = gsub("\\['|'\\]", "", artists))
Minha providência foi encontrar o que acho que deixa esta música tão especial. Para quem não a conhece, ela possui um número elevado de batidas por minuto (caraterística de canções alegres), mas é tocada com acordes menores (caraterística de canções tristes).
spotify |>
filter(name == "Weird Fishes/ Arpeggi") |>
select(name, artists, tempo, mode, key)
## # A tibble: 1 × 5
## name artists tempo mode key
## <chr> <chr> <dbl> <dbl> <dbl>
## 1 Weird Fishes/ Arpeggi Radiohead 153. 0 11
Pensando nisso, eu não simplesmente procurei a esmo as canções mais parecidas com Weird Fishes/Arpeggi baseando-me em todas as suas características. Eu primeiro filtrei o conjunto de dados, mantendo apenas
- canções com batidas por minuto entre 153 ± 5%
- em tom menor
- com a mesma nota principal
Guardei o resultado em um novo conjunto de dados chamado candidatas
.
p <- 0.05
tempo <- 153
candidatas <-
spotify |>
mutate(song = paste(artists, name, sep = " - ")) |>
filter(mode == 0) |>
filter(key == 11) |>
filter(tempo >= (1-p)*tempo) |>
filter(tempo <= (1+p)*tempo) |>
select(song | where(is.numeric)) |>
select(-track_number, -disc_number, -year, -mode, -key) |>
distinct(song, .keep_all = TRUE) |>
as.data.frame()
row.names(candidatas) <- candidatas$song
candidatas <-
candidatas |>
select(-song) |>
scale()
str(candidatas)
## num [1:45196, 1:11] -0.4316 -1.2674 1.1118 1.2605 0.0606 ...
## - attr(*, "dimnames")=List of 2
## ..$ : chr [1:45196] "Rage Against The Machine - Mic Check" "Rage Against The Machine - Maria" "Will Young - Your Game" "Will Young - Dance The Night Away" ...
## ..$ : chr [1:11] "danceability" "energy" "loudness" "speechiness" ...
## - attr(*, "scaled:center")= Named num [1:11] 0.524 0.595 -10.463 0.102 0.323 ...
## ..- attr(*, "names")= chr [1:11] "danceability" "energy" "loudness" "speechiness" ...
## - attr(*, "scaled:scale")= Named num [1:11] 0.195 0.282 6.327 0.125 0.362 ...
## ..- attr(*, "names")= chr [1:11] "danceability" "energy" "loudness" "speechiness" ...
candidatas <- as.data.frame(candidatas)
Em seguida, calculei a distiancia de Manhattan de todas as 45196 músicas que satisfazem estes critérios em relação a Weird Fishes/Arpeggi, guardando estas informações em um novo data frame chamado resultado
.
weird_fishes <- candidatas[which(row.names(candidatas) %in% "Radiohead - Weird Fishes/ Arpeggi"), ]
distancia <- function(x){
d <- dist(matrix(c(weird_fishes, x), nrow = 2, byrow = TRUE),
method = "manhattan")
return(d)
}
candidatas_distancia <- apply(candidatas, 1, distancia)
resultado <-
candidatas_distancia |>
bind_cols(str_split_fixed(row.names(candidatas), pattern = " - ", 2)) |>
clean_names() |>
select(artista = x2,
cancao = x3,
distancia = x1)
## New names:
## • `` -> `...1`
## • `` -> `...2`
## • `` -> `...3`
Portanto, estas são, de acordo com a minha análise, as 20 canções mais parecidas com Weird Fishes/Arpeggi:
Artista | Música | Distância |
---|---|---|
Moby | Falling Rain And Light | 1.813872 |
Perfume Monster | Castles Fall | 2.425636 |
Cone of Confusion | Pattern of Ignorance | 2.580750 |
Matteo Palmer | Southern Hemisphere | 2.623682 |
Steffen Shackinger | Perfect Waves | 2.635186 |
Travis Larson Band | Room To Breathe | 2.764531 |
Cody Lee | New Orleans | 2.766308 |
Joel Jerome | Totally Confused | 2.774482 |
Key Of Dreams | Gatito | 2.775946 |
Alex Seifert | What's Mine (Orchestral Version) | 2.791823 |
Trans-Siberian Orchestra | A Mad Russian's Christmas - Instrumental | 2.826837 |
Mors Syphilitica | Galatea | 2.841396 |
Bryan Carrigan | Seventh Stone | 2.843053 |
Sufjan Stevens', 'Helado Negro | All of Me Wants All of You - Helado Negro Remix | 2.859638 |
Roman Leykam | The World as a Canvas | 2.873808 |
Rameses B | Never Forget | 2.947742 |
Rooftops | Year as Lift | 2.963960 |
Lovespirals | One of Those Days | 2.967784 |
Bella Morte | Silver Crosses | 3.000960 |
Tigerforest | Longing For The Sun | 3.010751 |
Infelizmente, o arquivo que baixei não informa o gênero das canções. Uma informação como esta seria interessante para realizar mais uma pré-filtragem nos dados, mantendo apenas canções de gêneros como alternative, indie, electronica ou outros nos quais o Radiohead se encaixaria.
O resultado final pode ser ouvido encontrado na minha playlist weirder_fishes, no Deezer.