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.