library(tidyverse)
library(spotifyr)
library(highcharter)
library(ggridges)
library(DT)
No dia 8 de maio o mundo da música ficou mais careta: Rita Lee, a rainha do rock brasileiro, padroeira da liberdade, a santa rita de sampa, nos deixou, mas não sem antes deixar um grande legado de obras, letras, frases.
O intuito desse artigo é homenagear Rita Lee a partir de uma análise de sua obra usando R. Das R-Ladies de São Paulo para a mais icônica “Mina de Sampa”.
Esse post contém alguns trocadilhos e referências musicais. Dá o play na playlist abaixo e chega mais, chega mais
Análise das músicas com spotifyR
Esse tal de R
Para começar, é preciso carregar alguns pacotes:
Primeiro, devemos configurar os dados da API do Spotify. Aqui tem um tutorialzinho explicando como obter seu Client ID e o token de acesso. No blog da Curso-R também mostra como criar seus tokens de acesso na API do Spotify (com o pacote Rspotify, que é diferente do que vamos usar aqui, mas o início é o mesmo!).
Com o ambiente configurado, podemos requisitar as características de áudio das faixas qualquer artista presente na plataforma! Isso é feito com a função get_artist_audio_features
do pacote spotifyR
.
Nessa análise, vamos coletar os atributos das faixas da Rita Lee e analisar alguns deles.
<- read_csv("data-raw/rita_features.csv")
rita_features glimpse(rita_features)
## Rows: 411
## Columns: 39
## $ artist_name <chr> "Rita Lee", "Rita Lee", "Rita Lee", "Rita…
## $ artist_id <chr> "7dnT2FUXhjirperXaH22IJ", "7dnT2FUXhjirpe…
## $ album_id <chr> "0rZu9DYrrdzdOUQ7Nm2QB8", "0rZu9DYrrdzdOU…
## $ album_type <chr> "album", "album", "album", "album", "albu…
## $ album_images <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ album_release_date <chr> "2021-06-25", "2021-06-25", "2021-06-25",…
## $ album_release_year <dbl> 2021, 2021, 2021, 2021, 2021, 2021, 2021,…
## $ album_release_date_precision <chr> "day", "day", "day", "day", "day", "day",…
## $ danceability <dbl> 0.735, 0.802, 0.755, 0.821, 0.852, 0.656,…
## $ energy <dbl> 0.513, 0.638, 0.712, 0.769, 0.715, 0.734,…
## $ key <dbl> 2, 2, 5, 9, 0, 4, 2, 2, 5, 0, 0, 2, 6, 11…
## $ loudness <dbl> -9.369, -7.968, -7.696, -9.791, -8.402, -…
## $ mode <dbl> 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0,…
## $ speechiness <dbl> 0.0299, 0.0319, 0.0397, 0.0707, 0.0427, 0…
## $ acousticness <dbl> 0.031100, 0.005490, 0.000466, 0.001490, 0…
## $ instrumentalness <dbl> 0.414000, 0.023400, 0.258000, 0.726000, 0…
## $ liveness <dbl> 0.1160, 0.2160, 0.2210, 0.0469, 0.0426, 0…
## $ valence <dbl> 0.5470, 0.6850, 0.8350, 0.4380, 0.6770, 0…
## $ tempo <dbl> 119.978, 127.982, 127.986, 125.000, 123.0…
## $ track_id <chr> "4CFOKrbwh1FuR5xBvOWG2R", "1VM1W9cMkrHmrX…
## $ analysis_url <chr> "https://api.spotify.com/v1/audio-analysi…
## $ time_signature <dbl> 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,…
## $ artists <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ available_markets <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ disc_number <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,…
## $ duration_ms <dbl> 356886, 216585, 369843, 524638, 320488, 2…
## $ explicit <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,…
## $ track_href <chr> "https://api.spotify.com/v1/tracks/4CFOKr…
## $ is_local <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,…
## $ track_name <chr> "Só De Você - Coppola Remix", "Pega Rapaz…
## $ track_preview_url <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ track_number <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13…
## $ type <chr> "track", "track", "track", "track", "trac…
## $ track_uri <chr> "spotify:track:4CFOKrbwh1FuR5xBvOWG2R", "…
## $ external_urls.spotify <chr> "https://open.spotify.com/track/4CFOKrbwh…
## $ album_name <chr> "Rita Lee & Roberto - Classix Remix Vol. …
## $ key_name <chr> "D", "D", "F", "A", "C", "E", "D", "D", "…
## $ mode_name <chr> "major", "major", "major", "major", "majo…
## $ key_mode <chr> "D major", "D major", "F major", "A major…
Aqui já conseguimos ver que o dataframe resultante dessa requisição tem 39 colunas e 411 linhas, que correspondem a cada faixa da cantora. Há uma coluna com o formato errado, a album_release_date
, que é a data de lançamento do álbum (não usaremos esse atributo na análise, mas é bom saber :D)
Alguns álbuns são homônimos (ou seja, têm o mesmo nome), são coletâneas e/ou remixes. Tem que considerar isso na análise!
Quantas faixas cada álbum tem? Há algumas maneiras de fazer isso, mas vou utilizar o “GSA” (as funções group_by
,summarise
e arrange
do pacote dplyr
):
|>
rita_features group_by(album_name, album_release_year) |>
summarise(faixas=n()) |>
arrange(desc(faixas)) |>
datatable(extensions = c('FixedColumns',"FixedHeader"),
options = list(
scrollX = FALSE,
paging=TRUE,
fixedHeader=TRUE,
class = (c('compact', 'row-border', "hover", "nowrap" ))
),caption="Tabela 1 - Quantidade de faixas por álbum")
O “Em Bossa ’N Roll (Edição Comemorativa - 25 Anos) - Ao Vivo” é o álbum com mais faixas entre todos os discos listados no Spotify.
Será que isso significa que o “Em Bossa ’N Roll (Edição Comemorativa - 25 Anos) - Ao Vivo” é o álbum mais longo? Para responder a essa pergunta, podemos somar a duração em milissegundos de cada álbum, medida pelo atributo duration_ms
.
|>
rita_features group_by(album_name, album_release_year) |>
summarise("duracao" = sum(duration_ms)) |>
arrange(desc(duracao)) |>
datatable(extensions = c('FixedColumns',"FixedHeader"),
options = list(scrollX = FALSE, paging=TRUE, fixedHeader=TRUE,
class = (c('compact', 'row-border', "hover", "nowrap" ))),
caption="Tabela 2 - Duração de cada álbum da Rita Lee, em milissegundos")
Quase! É de se imaginar que os álbuns ao vivo, coletâneas e remixes apareçam com mais faixas e maior duração (no ao vivo tem o bis, a interação com o público…)
Para evitar problemas com álbuns homônimos, criei uma nova coluna incluindo “nome do álbum - ano de lançamento”.
<- rita_features |>
rita_features mutate(album = paste(album_name, "-", album_release_year))
Para as próximas análises, vamos considerar apenas os álbuns da discografia em estúdio da Rita Lee, de acordo com Wikipedia. Criei um novo objeto, o rita_filtrado
só com essa lista.
<- subset(rita_features, album_name %in% c('Build Up', 'Hoje É O Primeiro Dia Do Resto Da Sua Vida', 'Atrás Do Porto Tem Uma Cidade', 'Fruto Proibido', 'Entradas E Bandeiras', 'Babilônia', 'Rita Lee', 'Rita Lee', 'Saúde', 'Flagra', 'Bombom', 'Rita E Roberto', 'Flerte Fatal', 'Zona Zen', 'Rita Lee E Roberto De Carvalho', 'Rita Lee', 'Santa Rita De Sampa', 'Rita Lee 3001', 'Aqui, Ali, Em Qualquer Lugar','Balacobaco','Reza')) rita_filtrado
“Mas louco é quem me diz e não é feliz”
A positividade de uma canção pode ser medida com a valência dela. De acordo com a documentação da API do Spotify, valência “é uma medida de 0.0 a 1.0 que descreve a positividade musical transmitida por uma faixa. Faixas com alta valência soam mais positivas (por exemplo, felizes, alegres, eufóricas), enquanto faixas com baixa valência soam mais negativas (por exemplo, tristes, deprimidas, irritadas)”.
Então quais são as músicas mais e menos positivas da Rita?
# Mais positiva
$track_name[which.max(rita_filtrado$valence)]
rita_filtrado## [1] "Tatibitati"
# Menos positiva
$track_name[which.min(rita_filtrado$valence)]
rita_filtrado## [1] "Eu e Mim"
A música “Tabitati”, do álbum “Saúde”, é a mais positiva entre todas as faixas que estamos analisando. Por outro lado, a menos positiva é a “Eu e Mim”.
Quero ver qual é o álbum mais positivo. Para isso, escolhi a mediana da valência de cada álbum para representar esse atributo:
|>
rita_filtrado group_by(album) |>
summarise_at(vars(valence), list(mediana=median)) |>
arrange(desc(mediana)) |>
datatable(extensions = c('FixedColumns',"FixedHeader"),
options = list(scrollX = FALSE, paging=TRUE, fixedHeader=TRUE,
class = (c('compact', 'row-border', "hover", "nowrap" ))),
caption="Tabela 3 - Mediana da valência de cada álbum da Rita Lee")
Podemos representar a distribuição do atributo valência em cada álbum visualmente:
|>
rita_filtrado arrange(album_release_year) |>
mutate(album=factor(album, levels = rev(unique(rita_filtrado$album)))) |>
ggplot(aes(x= valence, y= album, fill= album)) +
geom_density_ridges(from = 0, to = 1, alpha = 1, size = 0.1) +
labs(
x = "Escala de valência",
y = "",
title = "Distribuição da postividade das músicas de Rita Lee por álbum",
subtitle = "Valência: Faixas com maiores níveis de valência soam mais positivas e vice-versa",
caption = "por Bianca Muniz (@biancamuniz__) | Fonte: Spotify's Web API"
+
) scale_fill_cyclical(
values = c("#9A373F")
+
) theme_ridges() +
theme(
plot.title = element_text(hjust = 0.5, size = 12, color = "#000000", face = "bold", family = "Bitter"),
plot.title.position = "plot",
plot.subtitle = element_text(face = "italic", hjust = 0.5, size = 10, family = "Bitter"),
axis.text.y = element_text(size = 10, family = "Raleway"),
axis.title.x = element_text(hjust = 0.5, size = 11, family = "Raleway"),
plot.caption = element_text(size = 9, family = "Bitter"))
O gráfico mostra “uma onda” por álbum da Rita. Os picos são os valores com mais faixas naquele ponto da escala de valência.
Olhando o gráfico, dá para perceber que os anos de 1981 e 1983 foram mais positivos musicalmente, de acordo com os atributos dos álbuns lançados nesses anos.
“Pra pedir silêncio eu berro, pra fazer barulho eu mesma faço”
Vamos analisar a energia das músicas da rainha do rock. Diferentemente da energia espiritual e cósmica que uma padroeira da liberdade poderia exalar, a API descreve esse atributo como “uma escala de 0 a 1 e representa uma medida de intensidade e atividade. Normalmente, as faixas energéticas parecem rápidas, altas e barulhentas”. Isso significa que uma faixa bem pesada, como um death metal, teria alta energia, enquanto uma baladinha suave pontua baixo na escala.
|>
rita_filtrado arrange(album_release_year) |>
mutate(album=factor(album, levels = rev(unique(rita_filtrado$album)))) |>
ggplot(aes(x= energy, y= album, fill= album))+
geom_density_ridges(from = 0, to = 1, alpha = 1, size = 0.1) +
labs(x = "Escala de energia", y = "",
title = "Distribuição da energia das músicas de Rita Lee por álbum",
subtitle = "Energia: Faixas com maiores níveis de energia são mais rápidas e altas e vice-versa",
caption = "por Bianca Muniz (@biancamuniz__) | Fonte: Spotify's Web API") +
scale_fill_cyclical(values = c("#9A373F")) +
theme_ridges()+
theme(
plot.title = element_text(hjust = 0.5, size = 12, color = "#000000", face = "bold", family = "Bitter"),
plot.title.position = "plot",
plot.subtitle = element_text(face = "italic", hjust = 0.5, size = 10, family = "Bitter"),
axis.text.y = element_text(size = 10, family = "Raleway"),
axis.title.x = element_text(hjust = 0.5, size = 11, family = "Raleway"),
plot.caption = element_text(size = 9, family = "Bitter"))
Olhando a representação visual, o “Aqui, Ali, em Qualquer Lugar” aparece como o álbum menos energético, com mais faixas próximas do 0. Lançado em 2006, ele tá cheio de versões de músicas dos Beatles.
Aprofundando na análise, o próximo passo é ver quais as músicas com mais energia:
|>
rita_filtrado select(track_name, album_name, energy) |>
top_n(5) |>
arrange(-energy) |>
datatable(extensions = c('FixedColumns',"FixedHeader"),
options = list(scrollX = FALSE, paging=TRUE, fixedHeader=TRUE,
class = (c('compact', 'row-border', "hover", "nowrap" ))),
caption='Tabela 3 - Músicas mais energéticas')
Aqui temos um empate entre “Erva Venenosa” e “Tratos À Bola”. Vou trapacear na minha própria análise e colocar o link de “Orra Meu”, minha faixa favorita entre o top 5 de músicas mais energéticas. Rita me perdoaria, eu acho.
“Baila comigo”
O atributo dançabilidade (“danceability” em inglês - confesso não saber de uma palavra em português pra isso) descreve o quão adequada uma faixa é para dançar, baseado em uma combinação de elementos musicais incluindo tempo, estabilidade rítmica, força do beat e regularidade geral. Um valor de 0 é o menos dançável e 1 é o mais dançável.
Desse modo, vamos ver qual faixa tem o valor máximo de danceabilidade:
$track_name[which.max(rita_filtrado$danceability)]
rita_filtrado## [1] "A Fulana"
“A Fulana” é a mais adequada para dançar “lá no meu esconderijo”. Concorda?
Veja como a dançabilidade se mostra nos álbuns de Rita: primeiro, com a mediana do atributo por álbum e depois com o gráfico:
|>
rita_filtrado group_by(album) |>
summarise_at(vars(danceability), list(danceabilidade=median)) |>
arrange(desc(danceabilidade)) |>
datatable(extensions = c('FixedColumns',"FixedHeader"),
options = list(scrollX = FALSE, paging=TRUE,fixedHeader=TRUE,
class = (c('compact', 'row-border', "hover", "nowrap" ))),
caption="Tabela 4 - Mediana da dançabilidade de cada álbum da Rita Lee")
|>
rita_filtrado arrange(album_release_year) |>
mutate(album=factor(album, levels = rev(unique(rita_filtrado$album)))) |>
ggplot(aes(x= danceability, y= album, fill= album)
+
)geom_density_ridges(from = 0, to = 1, alpha = 1, size = 0.1) +
labs(
x = "Escala de dançabilidade",
y = "",
title = "Distribuição da dançabilidade das músicas de Rita Lee por álbum",
subtitle = "Essa escala mede as músicas mais ou menos dançáveis",
caption = "por Bianca Muniz (@biancamuniz__) | Fonte: Spotify's Web API"
+
) scale_fill_cyclical(values = c("#9A373F")) +
theme_ridges()+
theme(
plot.title = element_text(hjust = 0.5, size = 12, color = "#000000", face = "bold", family = "Bitter"),
plot.title.position = "plot",
plot.subtitle = element_text(face = "italic", hjust = 0.5, size = 10, family = "Bitter"),
axis.text.y = element_text(size = 10, family = "Raleway"),
axis.title.x = element_text(hjust = 0.5, size = 11, family = "Raleway"),
plot.caption = element_text(size = 9, family = "Bitter"))
“E falo baixo nos meus decibéis”
Os atributos fala (speechiness) e instrumentalidade (instrumentalness) podem se complementar. Enquanto o primeiro trata da detecção da presença de palavras faladas em uma faixa musical, o segundo prevê se uma faixa não contém vocais.
De acordo com o Spotify, “Quanto mais exclusivamente falado for o registro (por exemplo, programa de entrevista, audiolivro, poesia), mais próximo de 1.0 será o valor do atributo [fala]. Já na instrumentalidade,”sons de”ooh” e “aah” são tratados como instrumentais nesse contexto. Quanto mais próximo o valor de instrumentalidade for de 1.0, maior a probabilidade de a faixa não conter conteúdo vocal. Valores acima de 0.5 pretendem representar faixas instrumentais, mas a confiança é maior à medida que o valor se aproxima de 1.0”
Olhando as músicas mais/menos faladas e as mais/menos instrumentais:
# Mais falada
$track_name[which.max(rita_filtrado$speechiness)]
rita_filtrado## [1] "Pistis Sophia"
# Menos falada
$track_name[which.min(rita_filtrado$speechiness)]
rita_filtrado## [1] "Pirarucu"
# Mais instrumental
$track_name[which.max(rita_filtrado$instrumentalness)]
rita_filtrado## [1] "De Novo Aqui Meu Bom José"
# Menos instrumental
$track_name[which.min(rita_filtrado$instrumentalness)]
rita_filtrado## [1] "Pistis Sophia"
É interessante observar que nesse caso os atributos se complementam quando observamos a música “Pistis Sophia”, cuja letra é uma oração à Nossa Senhora Aparecida. Ela é a menos instrumental e a com maior presença vocal.
Análise das letras
Raspagem com uma cobra cascavel
Antes de passar para o R, vamos fazer a raspagem do site letras.mus.br com Python (aquela linguagem do logo de cobrinha). Para isso, é preciso importar algumas bibliotecas:
import requests
from bs4 import BeautifulSoup
import pandas as pd
A url utilizada será a da página da Rita no site:
= 'https://www.letras.mus.br/rita-lee/' url
O bloco de código abaixo explica a raspagem da página: quero que encontre o link para cada música na página da Rita Lee e a letra correspondente a cada canção. O código forma uma tabela, com as colunas “nome” (nome da música) e “verso”. Cada linha é um verso das músicas coletadas.
No final, salva o resultado em um arquivo CSV pra analisarmos com R!
# Realiza a requisição HTTP para obter o HTML da página
= requests.get(url)
response
# Faz o parsing do HTML com o BeautifulSoup
= BeautifulSoup(response.text, 'html.parser')
soup
# Encontra todos os links das músicas
= soup.find_all('a', class_='song-name')
links
# Cria um DataFrame vazio com as colunas 'nome' e 'verso'
= pd.DataFrame(columns=['nome', 'verso'])
df
for link in links:
= link['href']
song_url = "https://www.letras.mus.br"+ song_url
song_url2 = link.text.strip()
song_title = requests.get(song_url2)
song_response = BeautifulSoup(song_response.text, 'html.parser')
song_soup = song_soup.find('div', class_='cnt-letra')
lyrics_div if lyrics_div is not None:
# Extrai cada verso da letra e adiciona como uma nova linha no DataFrame
= list(lyrics_div.stripped_strings)
verses = {'nome': [song_title] * len(verses), 'verso': verses}
data = pd.concat([df, pd.DataFrame(data)], ignore_index=True)
df else:
print(f"Não foi possível encontrar a letra da música '{song_title}'.")
# Salva o DataFrame em um arquivo CSV
#df.to_csv('data-raw/rita-letras.csv')
Voltando para o R (tenho mais facilidade com ele) vamos carregar mais alguns pacotes úteis para o trabalho com textos e importar o arquivo com as letras:
library(stopwords)
library(tm)
library(tidytext)
library(wordcloud2)
<- read.csv("data-raw/rita-letras.csv")
letras glimpse(letras)
## Rows: 7,718
## Columns: 3
## $ X <int> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18…
## $ nome <chr> "2001", "2001", "2001", "2001", "2001", "2001", "2001", "2001", …
## $ verso <chr> "Astronauta libertado", "Minha vida me ultrapassa", "Em qualquer…
O dataframe tem três colunas e mais de 7 mil linhas! Vamos olhar qual a música com mais versos:
|>
letras group_by(nome) |>
summarise(qtd_de_versos=n()) |>
arrange(desc(qtd_de_versos)) |>
datatable(extensions = c('FixedColumns',"FixedHeader"),
options = list(scrollX = FALSE, paging=TRUE, fixedHeader=TRUE,
class = (c('compact', 'row-border', "hover", "nowrap" ))),
caption="Tabela 5 - Quantidade de versos por música na página da Rita Lee no site letras.mus.br")
A página lista covers feito pela artista, ao vivo ou em álbuns. Considerar isso nas análises!
A música com mais versos no site é “Bad”, originalmente cantada por Michael Jackson. Rita Lee já homenageou o astro do pop ao vivo com um cover. Desconsiderando “Bad”, a música da Rita com mais versos é “Mutante”. “Ai de mim que sou romântica”…
“O som das nuvens” (de palavras)
Antes de analisar o texto, é preciso fazer uma série de ajustes, um pré-processamento. Aqui vamos deixar as letras em minúscula, faremos a remoção de algumas palavras (as stopwords) e o processo de tokenização.
Convertendo todo o texto da coluna “verso” para letras minúsculas:
<- letras |>
letras mutate(verso = tolower(verso))
Stopwords são palavras comuns que geralmente são consideradas irrelevantes para a análise de conteúdo e podem incluir pronomes, artigos, preposições e conjunções, etc. O objetivo de filtrar essas palavras é diminuir o conteúdo a ser analisado e melhorar sua eficiência.
No dataframe com as letras há músicas em português e em inglês. O código abaixo cria um vetor chamado “my_stopwords” que contém stopwords nos dois idiomas, que a gente vai remover mais pra frente:
<- c(stopwords("en"), stopwords("pt")) my_stopwords
A tokenização é um processo de dividir um texto em unidades menores, chamadas tokens. Esses tokens podem ser palavras únicas, frases, duplas de palavras, partes de palavras, dependendo do nível de granularidade desejado. O código abaixo divide cada verso em um token (que neste caso, é a palavra):
<- letras
tidy_letras <- tidy_letras |>
tidy_letras unnest_tokens(palavra, verso)
|>
tidy_letrasdatatable(extensions = c('FixedColumns',"FixedHeader"),
options = list(scrollX = FALSE, paging=TRUE,fixedHeader=TRUE,
class = (c('compact', 'row-border', "hover", "nowrap" ))),
caption="Tabela 6 - Músicas de Rita Lee, tokenizadas por palavra")