Uma jornada pelo tidyverse
1 - Introdução
 O tidyverseé um conjunto de pacotes do R desenvolvido
por Hadley Wickham que buscam suprir todas as ferramentas necessárias
para um fluxo de trabalho completo em ciências de dados.
 Os pacotes do tidyverse seguem a filosofia
tidy. Suas funções devem obedecer os quatro princÃpios
desta filosofia:
- Reutilizar estruturas de dados existentes;
- Unir funções simples a partir do uso do
pipe %>%; - Abraçar a programação funcional;
- As funções devem ser pensadas para uso por seres humanos.
É comum que os nomes de funções dos pacotes do
tidyversesejam simples e diretos como, por exemplo:
filter, select e arrange.
 Entre os pacotes que fazem parte do
tidyverseestão:
- readr
- dplyr
- tidyr
- tibble
- lubridate
- stringr
- forcats
- magrittr
- ggplot2
- broom
Instalando o tidyverse
 O tidyversepode ser instalado pelo menu de instalação
de pacotes disponÃvel no R Studio ou pela seguinte linha de comando.
install.packages("tidyverse") Este comando instalará os seguintes pacotes e suas respectivas dependências: ggplot2, dplyr, tidyr, readr, purr, tibble, stringr e forcats.
Carregando o tidyverse
 De forma parecida a da instalação é possÃvel carregar uma parte dos
pacotes do tidyversede uma só vez. Ao carregar os pacotes
pela função library(tidyverse) o R mostrará uma mensagem
indicando quais pacotes foram carregados, suas versões e possÃveis
conflitos de nomes de funções com outros pacotes.
library(tidyverse)## -- Attaching packages --------------------------------------- tidyverse 1.3.1 --
## v ggplot2 3.3.5 v purrr 0.3.4
## v tibble 3.1.5 v dplyr 1.0.7
## v tidyr 1.1.4 v stringr 1.4.0
## v readr 2.0.2 v forcats 0.5.1
## Warning: package 'ggplot2' was built under R version 4.1.2
## Warning: package 'tibble' was built under R version 4.1.1
## Warning: package 'tidyr' was built under R version 4.1.2
## Warning: package 'readr' was built under R version 4.1.1
## Warning: package 'dplyr' was built under R version 4.1.2
## -- Conflicts ------------------------------------------ tidyverse_conflicts() --
## x dplyr::filter() masks stats::filter()
## x dplyr::lag() masks stats::lag()
2 - O pipe: %>%
O pipe %>% é um operador implementado no pacote
magrittr que busca melhorar a legibilidade e o tempo de
implementação dos códigos. Ele é uma estrutura fundamental dentro do
tidyverse para inteligar as demais funções dos pacotes.
No R Studio o atalho para o pipe é o seguinte:
É fácil notar as vantagens no uso do pipe a partir de um exemplo simples: Vamos gerar uma amostra, então calcular sua média e por fim arredondar para ter três casas decimais.
Primeiro, sem usar o pipe:
round(mean(runif(100)), 3)## [1] 0.544
Agora com o pipe:
runif(100) %>%
mean() %>%
round(3)## [1] 0.464
É bem mais simples de entender o que está acontecendo no código no segundo exemplo.
O pipe é tão popular que na versão 4.1 do R foi implementada uma
versão nativa do pipe denotada por: |>. Ainda que suas
funções sejam essencialmente as mesmas, existem algumas diferenças nas
possibilidades do %>% e do |>,
incluindo:
- Por ser implementado nativamente, o
|>é mais rápido do que o%>%; - Até o momento o
|>não possui um marcador para a base de dados, o%>%suporta o uso do.dentro do fluxo de funções (mais sobre isso daqui a algumas seções). Uma solução momentânea é utilizar as novas funções anônimas implementadas no R 4.1; - Por ser uma implementação recente, nem todos as funções de pacotes
de fora do
tidyverseque funcionam com o%>%funcionam da mesma maneira com o|>.
Por enquanto vamos manter a utilização do %>% neste
minicurso.
3 - A missão de hoje: uma amostra da base de músicas do Spotify
Antes de mostrar os pacotes do tidyverseiremos conhecer
a base de dados que servirá como material de trabalho. O sistema de
recomendação de faixas do Spotify utiliza diversas variáveis
para caracterizar as músicas de modo a sugerir quais são mais similares
as que um determinado usuário escuta.
Existem variáveis que indicam a dançabilidade da faixa (é sério) e sua positividade a outras caracterÃsticas mais esperadas como a nota musical ou o tempo em milisegundos. Algumas das variáveis da base indicam a probabilidade da música possuir alguma caracterÃstica, usualmente estas variáveis são interpretadas como o nÃvel de confiança de que a música possui a caracterÃstica.
A base é composta por 176.249 faixas e 17 variáveis, sendo elas:
- genre: Gênero musical da faixa;
- artist_name: Nome do artista;
- track_name: Nome da faixa;
- track_id: Número identificador da faixa;
- popularity: Popularidade da faixa. Varia de 0 a 100 e valores maiores indicam maior popularidade;
- acousticness: Indica se a faixa é acústica. Varia de 0 a 1 e valores mais elevados indicam maior confiança;
- danceability: Indica o quão dançante é a faixa. Varia de 0 a 1 e valores mais elevados indicam maior dançabilidade;
- duration_ms: Duração da faixa em milissegundos;
- energy: Medida de percepção da intensidade da faixa. Varia de 0 a 1 e valores mais elevados indicam maior energia;
- intrumentalness: Indica se uma faixa é instrumental. Varia de 0 a 1, valores acima de 0.5 indicam que a música é instrumental;
- key: Nota música da faixa;
- liveness: Indica a presença de audiência. Varia de 0 a 1 e valores mais elevados indicam maior probabilidade de audiência;
- loudness: Volume médio da faixa em decibéis;
- mode: Indica a tonalidade da faixa (maior ou menor);
- speechiness: Indica a presença de voz na faixa. Varia de 0 a 1, valores acima de 0.66 indicam que há apenas voz na faixa, valores entre 0.33 e 0.66 indicam faixas com seções de música e voz (ex: rap) e valores abaixo de 0.33 sugerem música;
- tempo: Ritmo da faixa em BPM (batidas por minuto);
- valence: Indica a positividade da faixa (avaliada pela letra). Varia de 0 a 1 e valores mais elevados indicam maior positividade.
Mais informações sobre essas variáveis e a API do Spotify estão disponÃveis neste link.
Agora que conhecemos a base de dados, mãos a obra!
4 - Importando dados com o readr
O pacote básico do R possui funções para importação dos formatos mais
comuns de dados. Essas funções costumam possuir nomes como
read.csv(), read.csv2(),
read.delim(), entre outros e em grande parte do tempo
conseguem fazer o necessário. Entretanto, seu uso se torna
consideravelmente menos eficiente para bases de dados muito grandes.
Uma alternativa para estes cenários é o pacote readr,
suas funções são mais eficientes que as do pacote básico, possuem mais
argumentos para controlar a importação e também mostram uma barra de
carregamento enquanto a importação é feita.
A base do Spotify foi dividida em três arquivos diferentes.
Os arquivos que precisamos importar estão nas bases
spotify_db_01.csv, spotify_db_02.csv e
spotify_db_03.csv.
Para realizar a importação utilizaremos a função
read_csv2. Ela possui os seguintes argumentos:
read_csv2(
file, # O endereço do arquivo que será importado.
col_names = TRUE, # Se TRUE, usará a primeira linha do arquivo. Se FALSE, usará nomes genéricos. Também aceita um vetor de nomes.
col_types = NULL, # Se NULL irá imputar o tipo de dado na coluna utilizando as primeiras 1000 linhas. Se a imputação falhar é necessário indicar o tipo de cada coluna por strings utilizando este argumento.
locale = default_locale(), # Tipo de encoding utilizado no sistema.
na = c("", "NA"), # Vetor de strings que indica o que deve ser considerado um NA.
quoted_na = TRUE, # Indica se valores faltantes dentro de citação devem ser considerados NA ou não.
quote = "\"", # Caractere usado para indicar citação.
comment = "", # String utilizada para indicar comentários no arquivo, o que vier depois do comentário será ignorado na importação.
trim_ws = TRUE, # Indica se os espaços a frente e a atrás dos valores nas células devem ser removidos.
skip = 0, # Número de linhas a serem puladas no começo do arquivo no momento da importação.
n_max = Inf, # Número máximo de linhas a serem importadas.
guess_max = min(1000, n_max), # Número máximo de linhas a serem utilizadas na imputação do tipo de coluna.
progress = show_progress(), # Indica se a barra de carregamento deve ser exibida.
skip_empty_rows = TRUE # Indica se as linhas vazias devem ser ignoradas.
)Para essas bases não precisaremos manipular a maior parte desses argumentos, portanto iremos direto a importação:
base1 = read_csv2('https://raw.githubusercontent.com/ricardo-js1/SEMEST-tidyverse/main/spotify_db_01.csv')## i Using "','" as decimal and "'.'" as grouping mark. Use `read_delim()` for more control.
## Rows: 175084 Columns: 6-- Column specification --------------------------------------------------------
## Delimiter: ";"
## chr (4): track_id, track_name, artist_name, genre
## dbl (2): popularity, duration_ms
## i Use `spec()` to retrieve the full column specification for this data.
## i Specify the column types or set `show_col_types = FALSE` to quiet this message.
base2 = read_csv2('https://raw.githubusercontent.com/ricardo-js1/SEMEST-tidyverse/main/spotify_db_02.csv')## i Using "','" as decimal and "'.'" as grouping mark. Use `read_delim()` for more control.
## Rows: 175084 Columns: 4-- Column specification --------------------------------------------------------
## Delimiter: ";"
## chr (4): artist_name, track_name, key, mode
## i Use `spec()` to retrieve the full column specification for this data.
## i Specify the column types or set `show_col_types = FALSE` to quiet this message.
base3 = read_csv2('https://raw.githubusercontent.com/ricardo-js1/SEMEST-tidyverse/main/spotify_db_03.csv')## i Using "','" as decimal and "'.'" as grouping mark. Use `read_delim()` for more control.
## Rows: 175084 Columns: 10-- Column specification --------------------------------------------------------
## Delimiter: ";"
## chr (1): track_id
## dbl (9): acousticness, danceability, energy, speechiness, instrumentalness, ...
## i Use `spec()` to retrieve the full column specification for this data.
## i Specify the column types or set `show_col_types = FALSE` to quiet this message.
Ao importar as bases a função retorna algumas informações sobre o
separador utilizado para os decimais e os tipos de colunas que foram
imputados. Para verificar a base importada podemos utilizar as funções
str(), glimpse()e print(), cada
uma delas apresenta a base junto de algumas informações sobre a
mesma.
base1 %>%
str()## spec_tbl_df [175,084 x 6] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
## $ track_id : chr [1:175084] "0BRjO6ga9RKCKjfDqeFgWV" "0BjC1NfoEOOusryehmNudP" "0CoSDzoNIKCRs124s9uTVy" "0Gc6TVm52BwZD07Ki6tIvf" ...
## $ track_name : chr [1:175084] "C'est beau de faire un Show" "Perdu d'avance (par Gad Elmaleh)" "Don't Let Me Be Lonely Tonight" "Dis-moi Monsieur Gordon Cooper" ...
## $ artist_name: chr [1:175084] "Henri Salvador" "Martin & les f<e9>es" "Joseph Williams" "Henri Salvador" ...
## $ genre : chr [1:175084] "Movie" "Movie" "Movie" "Movie" ...
## $ popularity : num [1:175084] 0 1 3 0 4 0 2 15 0 10 ...
## $ duration_ms: num [1:175084] 99373 137373 170267 152427 82625 ...
## - attr(*, "spec")=
## .. cols(
## .. track_id = col_character(),
## .. track_name = col_character(),
## .. artist_name = col_character(),
## .. genre = col_character(),
## .. popularity = col_double(),
## .. duration_ms = col_double()
## .. )
## - attr(*, "problems")=<externalptr>
base2 %>%
glimpse()## Rows: 175,084
## Columns: 4
## $ artist_name <chr> "Henri Salvador", "Martin & les f\xe9es", "Joseph Williams~
## $ track_name <chr> "C'est beau de faire un Show", "Perdu d'avance (par Gad El~
## $ key <chr> "C#", "F#", "C", "C#", "F", "C#", "C#", "F#", "C", "G", "E~
## $ mode <chr> "Major", "Minor", "Minor", "Major", "Major", "Major", "Maj~
base3 %>%
print()## # A tibble: 175,084 x 10
## track_id acousticness danceability energy speechiness instrumentalness
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 0BRjO6ga9RKCKjfDqeFgWV 0.611 0.389 0.91 0.0525 0
## 2 0BjC1NfoEOOusryehmNudP 0.246 0.59 0.737 0.0868 0
## 3 0CoSDzoNIKCRs124s9uTVy 0.952 0.663 0.131 0.0362 0
## 4 0Gc6TVm52BwZD07Ki6tIvf 0.703 0.24 0.326 0.0395 0
## 5 0IuslXpMROHdEPvSl1fTQK 0.95 0.331 0.225 0.0456 0.123
## 6 0Mf1jKa8eNAf1a4PwTbizj 0.749 0.578 0.0948 0.143 0
## 7 0NUiKYRd6jt1LKMYGkUdnZ 0.344 0.703 0.27 0.953 0
## 8 0PbIF9YVD505GutwotpB5C 0.939 0.416 0.269 0.0286 0
## 9 0ST6uPfvaPpJLtQwhE6KfC 0.00104 0.734 0.481 0.046 0.00086
## 10 0VSqZ3KStsjcfERGdcWpFO 0.319 0.598 0.705 0.0281 0.00125
## # ... with 175,074 more rows, and 4 more variables: liveness <dbl>,
## # loudness <dbl>, tempo <dbl>, valence <dbl>
5 - Organizando os dados com o dplyr e tidyr
No momento as variáveis sobre as faixas estão divididas em três bases
diferentes e isso não é nada prático para se trabalhar. Seria razoável
unificar as três bases em uma base final, mas como podemos fazer isso?
Com o pacote dplyr.
União de bases com o dplyr
O dplyr é um pacote com funções de manipulação de dados.
Ele possui funções muito úteis como mutate(),
select(), filter(), summarise() e
arrange(), mas também diversas funções de união de bases
como left_join(), right_join(),
inner_join() e outras que também terminam com o sufixo
_join.Cada um desses joins possui caracterÃsticas próprias
e resultam em bases finais ligeiramente diferentes, então cuidado nessa
parte!
Primeiro, um exemplo mais didático do blog do Garrick Aden:
Por esse exemplo fica fácil de entender que o
left_join() mantém as linhas da base a esquerda (base x) e
todas as colunas presentes nas bases x e y. Esse tipo de join descartará
as colunas da base y que não possuem linhas equivalentes na base x e,
caso uma linha exista apenas na base x ela receberá NA nas
variáveis da base y. Nesse exemplo o join utilizou como chave
as colunas com os números das linhas, vamos precisar identificar nas
bases do Spotify quais variáveis poderemos utilizar como chave
da união.
Ainda considerando a base x na esquerda e a base y a direita, temos os outros tipos de joins:
right_join(): Mantém todas as linhas da base y (a direita) e todas as colunas das bases x e y. Linhas na base y sem alguma variável na base x receberãoNA;inner_join(): Mantém todas as linhas da base x com valores equivalentes na base y, e todas as colunas de x e y;full_join(): Mantém todas as linhas e colunas das bases x e y.NAserão atribuÃdos para as variáveis sem valor em alguma das colunas;semi_join(): Mantém apenas as linhas da base x com equivalentes na base y e apenas as colunas da base x;anti_join(): Mantém apenas as linhas da base x que não possuem equivalentes na base y e apenas as colunas da base x.
Em geral os argumentos dessas funções são iguais, usando a
left_join() como exemplo, teremos:
base_temp = left_join(x, # base a esquerda
y, # base a direita
by = c(), # Nome da coluna que será utilizada para parear as bases
suffix = c(".x", ".y"), # Sufixo que será adicionado ao nome da variável caso existam variáveis nas duas bases com o mesmo nome
keep = FALSE) # Argumento que indica se a variável chave das duas bases devem ser mantidas. O padrão é falso. Precisamos ter apenas um pouco de cuidado com o argumento
by. Aqui podemos alguns cenários diferentes de como
lidaremos com ele, lembrando que o nome da variávela esquerda sempre
será o da base a esquerda e o da direita o da variável na base a
direita:
- As chaves possuem o mesmo nome nas duas bases:
Nesse caso precisamos passar o nome da chave uma vez só:
by = 'nome_chave'; - Preciso utilizar mais de uma chave para unir as bases
corretamente: Então o argumento by receberá um vetor com os
nomes:
by = c('chave1', 'chave2'); - Minha(s) chave(s) tem nomes diferentes entre as
bases: Aqui é parecido com o caso anterior, mas precisamos
indicar o nome da variável nas duas bases:
by = c('chave1_b1' ='chave1_b2'). Para indicar mais variáveis, basta adicionar uma vÃrgula e próximo par de nomes. - Sou indeciso e não quero escolher qual variável será a chave…: Se não definirmos o argumento by então a função fará (ou tentará) o join com todas as variáveis que possuÃrem o mesmo nome entre as duas bases. O R retornará uma mensagem dizendo quais variáveis foram utilizadas, porém isso não é uma boa prática e pode dar errado por muitos motivos.
Voltando as nossas bases, vamos olhar para as variáveis e identificar
quais são os candidatos ideias para nossas chaves. Cada linha dessas
bases são informações sobre uma faixa, seria razoável utilizar chaves
que permitiriam identificar diretamente cada faixa. Nessa lógica, a
primeira variável a chamar atenção é a track_id, como cada
faixa tem sua própria identificação única seria uma chave ideal.
Tudo resolvido? Não, a nossa base2não possui a variável
track_id, apenas artist_name,
track_name, keye mode. Se
tentarmos unir usando como chave apenas artist_name teremos
problemas porque cada artista pode ter mais de uma faixa, acontece
parecido com track_name já que dois artistas diferentes
podem ter lançado faixas com o mesmo nome. A solução nesse caso será
usar tanto artist_name quanto track_name como
chaves, como feito no cenário 2 na lista acima.
Vamos utilizar uma base temporária para guardar a união intermediária.
temp = left_join(base1, base3, by = 'track_id')E então concluÃmos a união com a adição da base2:
base = left_join(temp, base2, by = c('artist_name', 'track_name'))Verificando a base final:
base %>%
glimpse()## Rows: 175,084
## Columns: 17
## $ track_id <chr> "0BRjO6ga9RKCKjfDqeFgWV", "0BjC1NfoEOOusryehmNudP", "~
## $ track_name <chr> "C'est beau de faire un Show", "Perdu d'avance (par G~
## $ artist_name <chr> "Henri Salvador", "Martin & les f\xe9es", "Joseph Wil~
## $ genre <chr> "Movie", "Movie", "Movie", "Movie", "Movie", "Movie",~
## $ popularity <dbl> 0, 1, 3, 0, 4, 0, 2, 15, 0, 10, 0, 2, 4, 3, 0, 0, 0, ~
## $ duration_ms <dbl> 99373, 137373, 170267, 152427, 82625, 160627, 212293,~
## $ acousticness <dbl> 0.61100, 0.24600, 0.95200, 0.70300, 0.95000, 0.74900,~
## $ danceability <dbl> 0.389, 0.590, 0.663, 0.240, 0.331, 0.578, 0.703, 0.41~
## $ energy <dbl> 0.9100, 0.7370, 0.1310, 0.3260, 0.2250, 0.0948, 0.270~
## $ speechiness <dbl> 0.0525, 0.0868, 0.0362, 0.0395, 0.0456, 0.1430, 0.953~
## $ instrumentalness <dbl> 0.00e+00, 0.00e+00, 0.00e+00, 0.00e+00, 1.23e-01, 0.0~
## $ liveness <dbl> 0.3460, 0.1510, 0.1030, 0.0985, 0.2020, 0.1070, 0.105~
## $ loudness <dbl> -1.828, -5.559, -13.879, -12.178, -21.150, -14.970, -~
## $ tempo <dbl> 166.969, 174.003, 99.488, 171.758, 140.576, 87.479, 8~
## $ valence <dbl> 0.8140, 0.8160, 0.3680, 0.2270, 0.3900, 0.3580, 0.533~
## $ key <chr> "C#", "F#", "C", "C#", "F", "C#", "C#", "F#", "C", "G~
## $ mode <chr> "Major", "Minor", "Minor", "Major", "Major", "Major",~
Selecionar, filtrar, agrupar e outros verbos
As demais funções do dplyrcobrem um grande número de
atividades rotineiras como filtrar as linhas da base de dados de acordo
com alguma condição das variáveis, selecionar colunas e calcular
estatÃsticas dentro de grupos. Muitas dessas funções possuem
alternativas no pacote base do R, porém nem todas essas alternativas são
tão simples de se utilizar.
Veremos algumas dessas funções a seguir.
select()
Permite selecionar colunas da base de dados. A função pode receber
apenas o nome das colunas, ou colunas que comecem ou terminem com a
palavra especificada pelas funções starts_with()e
ends_with(). Os operadores ou :, complementar
!, e & e intervalo : também
são aceitos para espeficar a seleção.
Primeiro, um exemplo mais simples em que selecionaremos o nome, o
artista e o tempo da faixa. Utilizaremos a função names()
para ver o nome das colunas resultantes em cada exemplo.
base %>%
select(artist_name, track_id, duration_ms) %>%
names()## [1] "artist_name" "track_id" "duration_ms"
Podemos utilizar o operador complementar ! para excluir
variáveis da base. Vamos utilizá-lo para excluir as variáveis de nota
musical e tom (keye mode).
base %>%
select(!c(key, mode)) %>%
names()## [1] "track_id" "track_name" "artist_name" "genre"
## [5] "popularity" "duration_ms" "acousticness" "danceability"
## [9] "energy" "speechiness" "instrumentalness" "liveness"
## [13] "loudness" "tempo" "valence"
E por último, também podemos utilizar a função
ends_with() para selecionar todas as colunas que terminam
com name.
base %>%
select(ends_with('name')) %>%
names()## [1] "track_name" "artist_name"
filter()
A possibilidade de filtrar a base de dados de acordo com uma
determinada caracterÃstica das variáveis é muito útil. A função
filter() possibilita a utilização de filtros de forma
bastante simples e sendo compatÃvel com os operadores lógicos do R.
Esta função também possui um operador especial denotado por
%in% que permite filtrar uma variável categórica de acordo
com um vetor de categorias. Veremos como utilizá-lo em breve.
Primeiro, vamos começar com um exemplo simples: Filtraremos a base de dados para conter apenas as faixas do gênero Rock.
base %>%
filter(genre == 'Rock')## # A tibble: 2,213 x 17
## track_id track_name artist_name genre popularity duration_ms acousticness
## <chr> <chr> <chr> <chr> <dbl> <dbl> <dbl>
## 1 4kzvAGJi~ We Are The C~ Queen Rock 50 180667 0.23
## 2 144y07Se~ Bennie And T~ Elton John Rock 58 361547 0.244
## 3 49nmsafp~ The Mighty F~ Fall Out B~ Rock 53 212467 0.0612
## 4 5mcWdITs~ One More Day Diamond Rio Rock 55 216040 0.639
## 5 2daZovie~ Dead Inside Muse Rock 61 262947 0.000259
## 6 7ptv3yXd~ (Oh) Pretty ~ Van Halen Rock 55 172671 0.192
## 7 0zjBoMgP~ I Get the Pi~ Mitchell T~ Rock 54 212343 0.00938
## 8 5j6WdDC1~ Rocket Queen Guns N' Ro~ Rock 53 375307 0.0303
## 9 028njLMK~ Hum Halleluj~ Fall Out B~ Rock 57 230507 0.00869
## 10 5VBzIn9c~ How Many Mor~ Led Zeppel~ Rock 54 508000 0.114
## # ... with 2,203 more rows, and 10 more variables: danceability <dbl>,
## # energy <dbl>, speechiness <dbl>, instrumentalness <dbl>, liveness <dbl>,
## # loudness <dbl>, tempo <dbl>, valence <dbl>, key <chr>, mode <chr>
Agora, vamos utilizar o operador %in% para filtrar apenas as faixas dos gêneros Pop, R&B e Eletronic.
base %>%
filter(genre %in% c('Pop', 'R&B', 'Eletronic'))## # A tibble: 7,701 x 17
## track_id track_name artist_name genre popularity duration_ms acousticness
## <chr> <chr> <chr> <chr> <dbl> <dbl> <dbl>
## 1 2YegxR5As~ Be Without ~ Mary J. Bl~ R&B 65 246333 0.083
## 2 6KFaHC9G1~ Desperado Rihanna R&B 63 186467 0.323
## 3 6muW8cSjJ~ Ice On My B~ Yung Bleu R&B 62 199520 0.0675
## 4 7yHqOZfsX~ Heaven Fall~ Surfaces R&B 61 240597 0.36
## 5 4XzgjxGKq~ Love Myself Olivia O'B~ R&B 68 213947 0.596
## 6 7KdRu0h7P~ Needs ELHAE R&B 61 205640 0.661
## 7 21Ft8ME79~ Make It Out~ Nao R&B 64 239147 0.667
## 8 1BViPjTT5~ Seigfried Frank Ocean R&B 66 334570 0.975
## 9 33YFwLJbA~ Roll In Pea~ Layton Gre~ R&B 60 170343 0.72
## 10 47TqCCnEl~ You Make Me~ Usher R&B 69 219120 0.0359
## # ... with 7,691 more rows, and 10 more variables: danceability <dbl>,
## # energy <dbl>, speechiness <dbl>, instrumentalness <dbl>, liveness <dbl>,
## # loudness <dbl>, tempo <dbl>, valence <dbl>, key <chr>, mode <chr>
O operador complementar ! é compatÃvel com o operador
%in%, agora vamos filtrar todas as faixas exceto as com gênero Pop,
R&B e Eletronic.
base %>%
filter(!genre %in% c('Pop', 'R&B', 'Eletronic'))## # A tibble: 167,383 x 17
## track_id track_name artist_name genre popularity duration_ms acousticness
## <chr> <chr> <chr> <chr> <dbl> <dbl> <dbl>
## 1 0BRjO6ga~ "C'est beau ~ "Henri Sal~ Movie 0 99373 0.611
## 2 0BjC1Nfo~ "Perdu d'ava~ "Martin & ~ Movie 1 137373 0.246
## 3 0CoSDzoN~ "Don't Let M~ "Joseph Wi~ Movie 3 170267 0.952
## 4 0Gc6TVm5~ "Dis-moi Mon~ "Henri Sal~ Movie 0 152427 0.703
## 5 0IuslXpM~ "Ouverture" "Fabien Na~ Movie 4 82625 0.95
## 6 0Mf1jKa8~ "Le petit so~ "Henri Sal~ Movie 0 160627 0.749
## 7 0NUiKYRd~ "Premi\xe8re~ "Martin & ~ Movie 2 212293 0.344
## 8 0PbIF9YV~ "Let Me Let ~ "Laura May~ Movie 15 240067 0.939
## 9 0ST6uPfv~ "Helka" "Chorus" Movie 0 226200 0.00104
## 10 0VSqZ3KS~ "Les bisous ~ "Le Club d~ Movie 10 152694 0.319
## # ... with 167,373 more rows, and 10 more variables: danceability <dbl>,
## # energy <dbl>, speechiness <dbl>, instrumentalness <dbl>, liveness <dbl>,
## # loudness <dbl>, tempo <dbl>, valence <dbl>, key <chr>, mode <chr>
Também podemos utilizar filtros compostos. Por exemplo, podemos
filtrar as músicas do gênero Rock e com letras mais negativas (o que é
indicado para valores baixos na variável valence).
base %>%
filter(genre == 'Rock' & valence <= 0.2)## # A tibble: 240 x 17
## track_id track_name artist_name genre popularity duration_ms acousticness
## <chr> <chr> <chr> <chr> <dbl> <dbl> <dbl>
## 1 5jOQ5v49~ "Plans" Oh Wonder Rock 56 235671 0.844
## 2 1ZzehTXQ~ "Pitchfork K~ AJR Rock 53 212760 0.0111
## 3 5VVWgWH8~ "Stockholm S~ Muse Rock 61 297000 0.00187
## 4 6IM45SqA~ "Why Won't T~ Tame Impala Rock 57 286093 0.619
## 5 2qdVHdkL~ "You Were Mi~ Dixie Chic~ Rock 55 217807 0.342
## 6 4q26Viix~ "My Only Swe~ El Ten Ele~ Rock 53 314587 0.109
## 7 00oZhqZI~ "Tomorrow Ne~ The Beatles Rock 58 179547 0.000084
## 8 0xIZPPBf~ "Black Sabba~ Black Sabb~ Rock 50 378227 0.0467
## 9 1jfcwpvd~ "Somewhere B~ Cody Jinks Rock 53 215840 0.00606
## 10 6z1frj1J~ "Are You Wit~ Easton Cor~ Rock 55 220347 0.314
## # ... with 230 more rows, and 10 more variables: danceability <dbl>,
## # energy <dbl>, speechiness <dbl>, instrumentalness <dbl>, liveness <dbl>,
## # loudness <dbl>, tempo <dbl>, valence <dbl>, key <chr>, mode <chr>
group_by(), ungroup(), summarise() e mutate()
Algumas vezes é interessante calcular estatÃsticas descritivas dentro
de categorias de uma variável categórica. A função
group_by()permite utilizar uma ou mais variáveis
categóricas de modo que as funções que venham na sequência sejam
calculadas dentro destes grupos. A função ungroup() deve
sempre ser utilizada ao fim dos cálculos envolvendo os grupos para
evitar que ocorram resultados inesperados.
Sozinhas estas duas funções não fazem muito, seu uso principal
envolve as funções summarise() e mutate(). A
principal diferença entre essas duas funções é que a primeira criará uma
nova base de dados com as informações resumidas, já a segunda adicionará
uma nova variável a base de dados atual.
Para deixar isso mais claro, vamos avaliar o seguinte exemplo: Iremos
utilizar as funções para calcular a valência (valence)
média por gênero musical.
Primeiro, faremos isso com a função mutate():
base %>%
group_by(genre) %>%
mutate(med_val = mean(valence, na.rm = TRUE)) %>%
select(genre, med_val) %>% # selecionando as colunas aqui só para deixar o resultado mais claro
ungroup()## # A tibble: 175,084 x 2
## genre med_val
## <chr> <dbl>
## 1 Movie 0.448
## 2 Movie 0.448
## 3 Movie 0.448
## 4 Movie 0.448
## 5 Movie 0.448
## 6 Movie 0.448
## 7 Movie 0.448
## 8 Movie 0.448
## 9 Movie 0.448
## 10 Movie 0.448
## # ... with 175,074 more rows
A variável med_val representa a valência média em cada
grupo, porém sua construção dentro da função mutate() faz
com que cada linha receba o valor da média de seu respectivo gênero.
Isso é pouco produtivo e gera muita informação repetida dentro da base
de dados (isso será problemático para bases gigantescas ou quando o
computador possui pouca memória disponÃvel).
Agora, vamos repetir esse cálculo utilizando a função
summarise():
base %>%
group_by(genre) %>%
summarise(med_val = mean(valence, na.rm = T)) %>%
ungroup()## # A tibble: 26 x 2
## genre med_val
## <chr> <dbl>
## 1 A Capella 0.329
## 2 Alternative 0.449
## 3 Anime 0.442
## 4 Blues 0.580
## 5 Children's Music 0.676
## 6 Classical 0.216
## 7 Comedy 0.413
## 8 Country 0.535
## 9 Dance 0.518
## 10 Electronic 0.385
## # ... with 16 more rows
Calculando deste jeito o resultado será uma base de dados em que o
número de colunas será o número de variáveis utilizadas para agrupar na
função group_by() mais uma coluna para cada estatÃstica
resumo calculada na função summarise(). O número de linhas
será dado pelo número de combinações entre os grupos, ou pelo número de
grupos na variável caso seja utilizada apenas uma.
Vamos ver como fica um exemplo um pouco mais complexo agora: Obteremos algumas médias de variáveis agrupadas pelo tom da faixa e gênero musical.
base %>%
group_by(genre, mode) %>%
summarise(med_valence = mean(valence, na.rm = T),
med_enegy = mean(energy, na.rm = T),
med_tempo = mean(tempo, na.rm = T)) %>%
ungroup()## `summarise()` has grouped output by 'genre'. You can override using the
## `.groups` argument.
## # A tibble: 52 x 5
## genre mode med_valence med_enegy med_tempo
## <chr> <chr> <dbl> <dbl> <dbl>
## 1 A Capella Major 0.358 0.265 110.
## 2 A Capella Minor 0.250 0.212 116.
## 3 Alternative Major 0.448 0.714 123.
## 4 Alternative Minor 0.451 0.714 121.
## 5 Anime Major 0.453 0.671 128.
## 6 Anime Minor 0.423 0.656 125.
## 7 Blues Major 0.582 0.599 121.
## 8 Blues Minor 0.576 0.613 121.
## 9 Children's Music Major 0.683 0.394 121.
## 10 Children's Music Minor 0.627 0.415 120.
## # ... with 42 more rows
Um uso bem tÃpico para a função summarise()é feito para
contar a quantidade de observações dentro de cada grupo. Isto é feito ao
utilizar a função n() dentro da
summarise()após definir os grupos.
Vamos contar o número de faixas por gênero musical neste exemplo.
base %>%
group_by(genre) %>%
summarize(freq = n())## # A tibble: 26 x 2
## genre freq
## <chr> <int>
## 1 A Capella 119
## 2 Alternative 9090
## 3 Anime 8935
## 4 Blues 8464
## 5 Children's Music 5400
## 6 Classical 8703
## 7 Comedy 9674
## 8 Country 7379
## 9 Dance 7965
## 10 Electronic 9149
## # ... with 16 more rows
Alternativamente, isso pode ser calculado a partir da função
tally(). Esta função também possui a versão
add_tally() que seria equivalente a calcular a frequência
dos grupos utilizando a função mutate().
base %>%
group_by(genre) %>%
tally(name = 'freq')## # A tibble: 26 x 2
## genre freq
## <chr> <int>
## 1 A Capella 119
## 2 Alternative 9090
## 3 Anime 8935
## 4 Blues 8464
## 5 Children's Music 5400
## 6 Classical 8703
## 7 Comedy 9674
## 8 Country 7379
## 9 Dance 7965
## 10 Electronic 9149
## # ... with 16 more rows
A função mutate() possui um uso mais interessante do que
calcular estatÃsticas resumo que se repetirão a cada linha. Para isso
tomaremos como exemplo a variável duration_ms que apresenta
a duração de cada faixa em milissegundos. Um milissegundo é equivalente
a 0,001 segundos, podemos simplificar isso e criar uma variável de
duração em minutos para música utilizando a função
mutate().
base = base %>%
mutate(duration_min = duration_ms / (1000 * 60))
base %>% select(duration_ms, duration_min)## # A tibble: 175,084 x 2
## duration_ms duration_min
## <dbl> <dbl>
## 1 99373 1.66
## 2 137373 2.29
## 3 170267 2.84
## 4 152427 2.54
## 5 82625 1.38
## 6 160627 2.68
## 7 212293 3.54
## 8 240067 4.00
## 9 226200 3.77
## 10 152694 2.54
## # ... with 175,074 more rows
arrange()
Para finalizar sobre o dplyriremos conhecer a função
arrange(). No exemplo em que contamos o número de linhas
por gênero musical a base resultante não possuÃa nenhum ordenamento em
particular. Em algus casos é interessante ordenar os dados de forma
crescente ou decrescente para tornar os resultados mais intuitivos e é
aà que a função arrange()entra.
base %>%
group_by(genre) %>%
summarize(freq = n()) %>%
arrange(freq)## # A tibble: 26 x 2
## genre freq
## <chr> <int>
## 1 A Capella 119
## 2 Rap 1455
## 3 Rock 2213
## 4 Pop 2408
## 5 Indie 3310
## 6 Soul 4395
## 7 R&B 5293
## 8 Children's Music 5400
## 9 Country 7379
## 10 Hip-Hop 7402
## # ... with 16 more rows
Por padrão a função ordenará em ordem crescente, para a ordem decrescente isso é feito da seguinte forma:
base %>%
group_by(genre) %>%
summarize(freq = n()) %>%
arrange(desc(freq))## # A tibble: 26 x 2
## genre freq
## <chr> <int>
## 1 Comedy 9674
## 2 Electronic 9149
## 3 Alternative 9090
## 4 Anime 8935
## 5 Classical 8703
## 6 Reggae 8687
## 7 Reggaeton 8548
## 8 Soundtrack 8478
## 9 Blues 8464
## 10 Opera 8280
## # ... with 16 more rows
Pivoteando com o tidyr
Os pacotes do tidyverse foram feitos para utilizar dados
no formato tidy. Este formato é definido por três regras:
- Cada variável deve ter sua própria coluna;
- Cada observação deve ter sua própria linha;
- Cada valor deve ter sua própria célula.
O tidyr possui funções para arrumar bases de dados
bagunçadas e deixá-las no formato aceito pelo tidyverse.
Geralmente as funções esperam dados no formato wide, em que
cada variável tem sua própria coluna, ou long, em que cada
linha representa uma observação de uma categoria. Para simplificar, o
formato wide é mais horizontal e o long é mais
vertical.
Isso é especialmente importante ao se trabalhar com o
ggplot2, alguns tipos de gráficos dependem que as variáveis
estejam organizadas de um jeito especÃfico.
A base do Spotify já está em formato wide e é
relativamente bem comportada, mas o que farÃamos se fosse uma base mais
bagunçada? O tidyr possui as funções
pivot_longer() e pivot_wider()para pivotear
bases entre os dois tipos de formato.
Essas funções funcionam da seguinte forma:
A função pivot_longer() transforma uma base de dados em
formato wide para o formato long, a função
pivot_wider() faz a transformação oposta. Naturalmente se
aplicamos as duas funções em sequência a uma base de dados teremos a
base final com o mesmo formato da base inicial.
O uso das duas funções é bem parecido: é necessário definir as
colunas que serão pivoteadas por meio dos argumentos ,cols,
names_to e values_topara a
pivot_longer()ou
id_cols,names_from e values_from
para a pivot_wider(). Para a pivot_longer()
estes argumentos indicam o nome da coluna que será criada para guardar
os nomes das variáveis e o valor, respectivamente. Para a
pivot_wider() é parecido, estes argumentos indicam quais
variáveis serão utilizadas para criar as novas colunas e de onde os
valores virão.
Vamos utilizar uma base reduzida para ilustrar o funcionamento destas funções. Utilizaremos Os Beatles e as variáveis valênia e track_name.
Esta base atual será uma base em formato long:
base %>%
filter(artist_name == 'The Beatles') %>%
select(valence, track_name)## # A tibble: 145 x 2
## valence track_name
## <dbl> <chr>
## 1 0.879 Baby It's You - Remastered 2009
## 2 0.516 Things We Said Today - Remastered 2009
## 3 0.632 The Night Before - Remastered 2009
## 4 0.0534 Tomorrow Never Knows - Remastered 2009
## 5 0.664 Hello, Goodbye - Remastered 2015
## 6 0.879 All I've Got To Do - Remastered 2009
## 7 0.49 The Fool On The Hill - Remastered 2009
## 8 0.183 Come Together - Remastered 2015
## 9 0.968 From Me To You - Mono / Remastered
## 10 0.676 All You Need Is Love - Remastered 2015
## # ... with 135 more rows
Agora, utilizaremos a função pivot_wider() para deixá-la
no formato wide:
base %>%
filter(artist_name == 'The Beatles') %>%
select(valence, track_name) %>%
pivot_wider(names_from = track_name, values_from = valence)## # A tibble: 1 x 145
## `Baby It's You - R~ `Things We Said To~ `The Night Before~ `Tomorrow Never Kn~
## <dbl> <dbl> <dbl> <dbl>
## 1 0.879 0.516 0.632 0.0534
## # ... with 141 more variables: Hello, Goodbye - Remastered 2015 <dbl>,
## # All I've Got To Do - Remastered 2009 <dbl>,
## # The Fool On The Hill - Remastered 2009 <dbl>,
## # Come Together - Remastered 2015 <dbl>,
## # From Me To You - Mono / Remastered <dbl>,
## # All You Need Is Love - Remastered 2015 <dbl>,
## # Yellow Submarine - Remastered 2015 <dbl>, ...
Notem que cada faixa virou sua própria coluna com o valor da variável
valência na primeira linha. Por fim, vamos utilizar a função
pivot_longer()para retornar ao formato da base original.
Neste caso o argumento colsreceberá os Ãndices das
colunas.
base %>%
filter(artist_name == 'The Beatles') %>%
select(valence, track_name) %>%
pivot_wider(values_from = valence, names_from = track_name) %>%
pivot_longer(names_to = 'artist_name', values_to = 'valence', cols = 1:145)## # A tibble: 145 x 2
## artist_name valence
## <chr> <dbl>
## 1 Baby It's You - Remastered 2009 0.879
## 2 Things We Said Today - Remastered 2009 0.516
## 3 The Night Before - Remastered 2009 0.632
## 4 Tomorrow Never Knows - Remastered 2009 0.0534
## 5 Hello, Goodbye - Remastered 2015 0.664
## 6 All I've Got To Do - Remastered 2009 0.879
## 7 The Fool On The Hill - Remastered 2009 0.49
## 8 Come Together - Remastered 2015 0.183
## 9 From Me To You - Mono / Remastered 0.968
## 10 All You Need Is Love - Remastered 2015 0.676
## # ... with 135 more rows
É um exemplo bem simples, mas mostra um pouco da utilidade dessas funções. Para bases realmente bagunçadas pode ser necessário usar os dois tipos de pivoteamento seguidos para se obter a base final no formato necessário. Essa página no CRAN apresenta alguns exemplos mais complexos de utilização dessas funções.
6 - Manipulando strings com o stringr
A habilidade de manipular texto pode ser muito útil na hora de organizar bases de dados. É comum encontrar bases que foram preenchidas manualmente por várias pessoas diferentes e as coisas logo começam a ficar complicadas quando você descobrir que João escrevia o local como Rio de Janeiro, Maria como RJ e por aà vai, sem contar as vezes que digitaram o nome errado.
O pacote stringr possui diversas funções de manipulação
de texto, a partir dele é possÃvel converter todas as letras para
maiúsculas ou minúsculas, detectar a presença de padrões de letras
dentro do texto e realizar substituições. Este pacote utiliza as
chamadas regular expressions (regex) para identiicação dos
padrões de palavras e este é um tema complexo o suficiente para ter um
minicurso por si só. Por isso desta vez vamos nos manter com as funções
mais acessÃveis do stringr.
Primeiro, as funções str_to_upper(),
str_to_lower(), str_to_title() e
str_to_sentence()permitem converter o texto para maiúsculo,
minúsculo, apenas com a primeira letra de cada palavra como maiúscula e
com apenas a primeira letra da string em maiúscula.
Vamos testar essas funções com os nomes das músicas.
base %>%
select(track_name) %>%
mutate(
titulo_upper = str_to_upper(track_name),
titulo_lower = str_to_lower(track_name),
titulo_title = str_to_title(track_name),
titulo_sentence = str_to_sentence(track_name)
)## # A tibble: 175,084 x 5
## track_name titulo_upper titulo_lower titulo_title titulo_sentence
## <chr> <chr> <chr> <chr> <chr>
## 1 "C'est beau ~ "C'EST BEAU DE~ "c'est beau d~ "C'est Beau D~ "C'est beau de f~
## 2 "Perdu d'ava~ "PERDU D'AVANC~ "perdu d'avan~ "Perdu D'avan~ "Perdu d'avance ~
## 3 "Don't Let M~ "DON'T LET ME ~ "don't let me~ "Don't Let Me~ "Don't let me be~
## 4 "Dis-moi Mon~ "DIS-MOI MONSI~ "dis-moi mons~ "Dis-Moi Mons~ "Dis-moi monsieu~
## 5 "Ouverture" "OUVERTURE" "ouverture" "Ouverture" "Ouverture"
## 6 "Le petit so~ "LE PETIT SOUP~ "le petit sou~ "Le Petit Sou~ "Le petit souper~
## 7 "Premi\xe8re~ "PREMI\xe8RES ~ "premi\xe8res~ "Premi\xe8Res~ "Premi\xe8res re~
## 8 "Let Me Let ~ "LET ME LET GO" "let me let g~ "Let Me Let G~ "Let me let go"
## 9 "Helka" "HELKA" "helka" "Helka" "Helka"
## 10 "Les bisous ~ "LES BISOUS DE~ "les bisous d~ "Les Bisous D~ "Les bisous des ~
## # ... with 175,074 more rows
Para detectar a presença de um padrão dentro de uma string podemos
utilizar a função str_detect(). Esta função pode ser
utilizada junto com a função filter() do pacote
dplyr para filtrar apenas as linhas que contenham um
determinado padrão nas variáveis de texto.
No próximo exemplo vamos filtrar a base para manter apenas as linhas com love no tÃtulo das faixas.
base %>%
filter(str_detect(track_name, 'love'))## # A tibble: 125 x 18
## track_id track_name artist_name genre popularity duration_ms acousticness
## <chr> <chr> <chr> <chr> <dbl> <dbl> <dbl>
## 1 2Y1TGD05Z~ "Papa loves~ Henri Salv~ Movie 0 201587 0.667
## 2 1To829onY~ "4 Leaf Clo~ Ravyn Lenae Alte~ 55 220101 0.0145
## 3 6ZUdUi3qB~ "Beloved" Jordan Fel~ Alte~ 49 240453 0.00497
## 4 0GIDR7iLJ~ "This Velve~ Red Hot Ch~ Alte~ 54 225200 0.0706
## 5 4qAPq3U8G~ "Goodlove" Justine Sk~ Alte~ 50 249227 0.0894
## 6 2cgf6SSsS~ "Tremble Fo~ Collective~ Alte~ 41 232560 0.000332
## 7 3JeT6Xcv6~ "lovers\x92~ Bibio Dance 60 238640 0.68
## 8 6HcQZfMrw~ "Dreamlover" Mariah Car~ Dance 56 232960 0.18
## 9 6SaeXxdt0~ "Fastlove, ~ George Mic~ Dance 63 324667 0.052
## 10 71JQ4eRRt~ "sea of lov~ Christina ~ Dance 49 215387 0.538
## # ... with 115 more rows, and 11 more variables: danceability <dbl>,
## # energy <dbl>, speechiness <dbl>, instrumentalness <dbl>, liveness <dbl>,
## # loudness <dbl>, tempo <dbl>, valence <dbl>, key <chr>, mode <chr>,
## # duration_min <dbl>
Os operadores do regex ^ e $indicam que a
busca deve ser por trechos que comecem ou terminem com o padrão,
respectivamente.
base %>%
filter(str_detect(track_name, '^love'))## # A tibble: 11 x 18
## track_id track_name artist_name genre popularity duration_ms acousticness
## <chr> <chr> <chr> <chr> <dbl> <dbl> <dbl>
## 1 3JeT6Xcv6~ "lovers\x92~ Bibio Dance 60 238640 0.68
## 2 3XTOmBAnu~ "love (wip)" San Holo Elec~ 53 262687 0.193
## 3 7lY3juj1M~ "love (ain<~ isaac grac~ Folk 56 224813 0.93
## 4 6wcRv0UBv~ "love" DEAN R&B 59 258227 0.00125
## 5 0ltO5bci1~ "love someb~ joan R&B 57 227631 0.0596
## 6 1Znlc3UZA~ "love yours~ XXXTENTACI~ Hip-~ 69 48423 0.674
## 7 2WX8QMtZV~ "love me fo~ Lil Yachty Hip-~ 52 104075 0.0924
## 8 3bjLCKsBN~ "love ride" Christian ~ Indie 62 234192 0.206
## 9 3jjUphDBl~ "love gang ~ Whethan Indie 51 178515 0.122
## 10 7L3r32qd8~ "love n hat~ B0nds Jazz 2 51981 0.288
## 11 0Yqsb1WKT~ "love the r~ Zachary Kn~ Soul 36 304030 0.259
## # ... with 11 more variables: danceability <dbl>, energy <dbl>,
## # speechiness <dbl>, instrumentalness <dbl>, liveness <dbl>, loudness <dbl>,
## # tempo <dbl>, valence <dbl>, key <chr>, mode <chr>, duration_min <dbl>
base %>%
filter(str_detect(track_name, 'love$'))## # A tibble: 19 x 18
## track_id track_name artist_name genre popularity duration_ms acousticness
## <chr> <chr> <chr> <chr> <dbl> <dbl> <dbl>
## 1 0GIDR7iL~ This Velvet ~ Red Hot Ch~ Alte~ 54 225200 0.0706
## 2 4qAPq3U8~ Goodlove Justine Sk~ Alte~ 50 249227 0.0894
## 3 32K03Gjk~ motherlove Bea Miller Dance 54 185747 0.00114
## 4 6qpaTfe6~ Power Glove Knife Party Dance 53 261245 0.000105
## 5 1uTXtV2r~ Superlove Tinashe Dance 52 183792 0.0148
## 6 63PjUXm7~ self-love San Holo Elec~ 62 143775 0.0128
## 7 4GA6Iljg~ Summer love Kobasolo Anime 38 267865 0.331
## 8 1XwChHbX~ Arrow of love GARNiDELiA Anime 23 219680 0.71
## 9 3Bny9WzD~ Koisuruotome~ Kobasolo Anime 18 251520 0.534
## 10 7FzFtbkN~ closest love fripSide Anime 20 275680 0.0138
## 11 5o3Ry6h5~ Caligulove Them Crook~ Blues 36 295280 0.00473
## 12 6wcRv0UB~ love DEAN R&B 59 258227 0.00125
## 13 0kaSpCet~ show me love isaac grac~ Folk 57 197133 0.00785
## 14 6zHyWsqT~ Dontmakemefa~ Cuco Pop 63 207692 0.373
## 15 3H6MaJHf~ A Heartfelt ~ Wyatt Cenac Come~ 23 253777 0.879
## 16 3oP7AI2q~ You'll Go Bl~ Gilbert Go~ Come~ 15 165987 0.828
## 17 74OLXYsv~ bitterlove Ardhito Pr~ Jazz 57 216265 0.393
## 18 0piJU93a~ thelove Jitwam Jazz 34 163928 0.152
## 19 4nWfJBA1~ Wilderlove John Mark ~ World 43 281206 0.00384
## # ... with 11 more variables: danceability <dbl>, energy <dbl>,
## # speechiness <dbl>, instrumentalness <dbl>, liveness <dbl>, loudness <dbl>,
## # tempo <dbl>, valence <dbl>, key <chr>, mode <chr>, duration_min <dbl>
A função str_replace() o padrão especificado por um novo
padrão. Neste exemplo vamos substituir a palavra love no nome
das faixas por LOOOVE.
base %>%
filter(str_detect(track_name, 'love')) %>%
mutate(
track_name = str_replace(track_name, 'love', 'LOOOVE')
) %>%
select(track_name)## # A tibble: 125 x 1
## track_name
## <chr>
## 1 Papa LOOOVEs mambo
## 2 4 Leaf CLOOOVEr (feat. Steve Lacy)
## 3 BeLOOOVEd
## 4 This Velvet GLOOOVE
## 5 GoodLOOOVE
## 6 Tremble For My BeLOOOVEd
## 7 LOOOVErs<U+FFFD> carvings
## 8 DreamLOOOVEr
## 9 FastLOOOVE, Pt. 1
## 10 sea of LOOOVErs
## # ... with 115 more rows
Obviamente o padrão a ser substituÃdo deve ser definido com mais cuidado, mas fica como exemplo das possibilidades do pacote.
A função str_replace()substitui apenas o primeiro padrão
encontrado na string, para os casos em que se deseja substituir todas as
vezes que o padrão aparece deve se utilizar a funçaõ
str_replace_all().
7 - Gráficos com o ggplot2 feat. forcats
O ggplot2 é a opção do tidyverse para
construção de gráficos. Sua principal vantagem é a possibilidade de
construir gráficos complexos com uma sintaxe simples, porém com a
desvantagem de ser ligeiramente mais complexo que a função
plot para construção de gráficos mais simples. O pacote
funciona com uma estrutura de camadas de geometria, os
geom_, em que cada nova camada é adicionada sobre as
camadas anteriores (então a ordem de como definimos os elementos do
gráfico importa!).
O site ggplot2tor
possui uma lista interativa das geometrias do ggplot2,
incluindo os argumentos disponÃveis para cada uma delas e identificando
quais são obrigatórios e quais são opcionais para construção dos
gráficos. A especificação das variáveis utilizadas pela geometria são
especificadas dentro do argumento aes(), de
aesthetic (estética).
Todo gráfico feito com o ggplot2 começa pela função
`ggplot(). As camadas seguintes serão adicionadas pelo
operador +, então para construir um gráfico de barras
simples utilizaremos a função geom_bar(). Caso não
especifiquemos o eixo y a função contará a frequência da
variável especificada em x, também podemos especificar
y como a frequência, mas então precisaremos do argumento
stat = 'identity'. Também utilizaremos a função
coord_flip() para girar o gráfico e melhorar a visualização
dos nomes dos gêneros musicais.
base %>%
group_by(genre) %>%
tally() %>%
ggplot() +
geom_bar(aes(x = genre, y = n), stat = 'identity') +
coord_flip()Agora, utilizaremos os argumentos colore
fillpara definir a cor das bordas das barras e a cor
interna, respectivamente.
base %>%
group_by(genre) %>%
tally() %>%
ggplot() +
geom_bar(aes(x = genre, y = n), stat = 'identity', color = 'black', fill = 'darkred') +
coord_flip()Ainda podemos ir além, agora utilizaremos a função
labs()para definir o tÃtulo dos eixos e do gráfico. Isto é
feito utilizando os argumentos x, y e
labs.
base %>%
group_by(genre) %>%
tally() %>%
ggplot() +
geom_bar(aes(x = genre, y = n), stat = 'identity', color = 'black', fill = 'darkred') +
coord_flip() +
labs(y = 'Frequência', x = 'Gênero musical', title = 'Frequência dos gêneros musicais')Uma adição útil a este tipo de gráfico são etiquetas com a frequência
da categoria. Isto pode ser adicionado pela função
geom_text(), esta função recebe como argumentos dentro do
aes() a variável do eixos x e ye
o valor da etiqueta no argumento label. Também utilizaremos
o argumento nudge_y para aumentar o afastamento entre as
etiquetas e a barra. Notem que os argumentos que não envolvem variáveis
devem ser passados fora do aes().
base %>%
group_by(genre) %>%
tally() %>%
ggplot() +
geom_bar(aes(x = genre, y = n), stat = 'identity', color = 'black', fill = 'darkred') +
geom_text(aes(x = genre, y = n, label = n), nudge_y = 400) +
coord_flip() +
labs(y = 'Frequência', x = 'Gênero musical', title = 'Frequência dos gêneros musicais')Neste tipo de gráfico é util ordenar as barras em ordem decrescente
pela frequência, mas como fazer isso? O pacote
forcatspossui inúmeras funções para lidar com fatores, uma
delas é a fct_reorder()e permite reordenar os grupos de uma
variável categórica de acordo com os valores de uma outra variável.
base %>%
group_by(genre) %>%
tally() %>%
ggplot() +
geom_bar(aes(x = fct_reorder(genre, n), y = n), stat = 'identity', color = 'black', fill = 'darkred') +
geom_text(aes(x = genre, y = n, label = n), nudge_y = 400) +
coord_flip() +
labs(y = 'Frequência', x = 'Gênero musical', title = 'Frequência dos gêneros musicais')
O
ggplot2 também possui diversos temas para os gráficos. Os
temas básicos incorporados com o pacote podem ser vistos neste
link, mas existem pacotes como o ggthemes
e o ggthemr
com opções bastante interessantes. Por ora, vamos adicionar o tema
clássico ao nosso gráfico com a função theme_classic().
base %>%
group_by(genre) %>%
tally() %>%
ggplot() +
geom_bar(aes(x = fct_reorder(genre, n), y = n), stat = 'identity', color = 'black', fill = 'darkred') +
geom_text(aes(x = genre, y = n, label = n), nudge_y = 400) +
coord_flip() +
labs(y = 'Frequência', x = 'Gênero musical', title = 'Frequência dos gêneros musicais') +
theme_classic()Antes de continuar explorando as opções nos temas, vamos começar um segundo gráfico. Desta vez faremos um gráfico de dispersão entre a valência, a positividade medida nas faixas, e a energia. Obviamente temos um número muito grande de faixas, então começaremos apenas com a banda Foo Fighters (ou a banda que quiserem, caso queiram mudar).
O gráfico de dispersão é feito com a função
geom_point(), esta função receberá a variável energia em
x e valência em y.
base %>%
filter(artist_name == 'Foo Fighters') %>%
ggplot() +
geom_point(aes(x = energy, y = valence)) +
theme_light()É um começo, agora vamos deixar as coisas mais interessantes e
adicionar mais artistas. Dessa vez vamos incluir a Katy Perry, os
Beatles e o Justin Timberlake. Dessa vez iremos adicionar o argumento
colordentro da aes() para colorir cada
observação de acordo com sua banda.
base %>%
filter(artist_name %in% c('Foo Fighters', 'The Beatles', 'Katy Perry', 'Justin Timberlake')) %>%
ggplot() +
geom_point(aes(x = energy, y = valence, color = artist_name)) +
theme_light()Já sabemos que podemos utilizar a função labs() para
nomear os eixos e o tÃtulo do gráfico. O ggplot2 tem como
padrão utilizar o nome da variável como nome da legenda, mas podemos
mudar isso também. No nosso caso utilizaremos a função
scale_color_discrete(), mas essa está longe de ser a única
opção disponÃvel no pacote. Este link apresenta uma ferramenta
interativa para selecionar as escalas de acordo com o tipo de variável
utilizado e se utilizamos uma escala para cor (color) ou
preenchimento (fill).
base %>%
filter(artist_name %in% c('Foo Fighters', 'The Beatles', 'Katy Perry', 'Justin Timberlake')) %>%
ggplot() +
geom_point(aes(x = energy, y = valence, color = artist_name)) +
theme_light() +
labs(x = 'Energia', y = 'Valência', title = 'Valência x Energia por Artista') +
scale_color_discrete(name = 'Artista')A função scale_color_discrete() utiliza a escala de cor
padrão do ggplot2, mas também é possÃvel definirmos uma
escala de cor manualmente por meio do argumento values na
função scale_color_manual(). Este argumento receberá um
vetor de cores (nomes ou códigos hexadecimais) com tamanho equivalente
ao número de categorias da variável utilizada no argumento
color.
Vamos aproveitar e também aumentar o tamanho dos pontos por meio do
argumento size na função geom_point(). Como há
pontos se sobrepondo também iremos adicionar transparência nesses pontos
por meio do argumento alpha.
base %>%
filter(artist_name %in% c('Foo Fighters', 'The Beatles', 'Katy Perry', 'Justin Timberlake')) %>%
ggplot() +
geom_point(aes(x = energy, y = valence, color = artist_name), size = 4, alpha = 0.6) +
theme_light() +
labs(x = 'Energia', y = 'Valência', title = 'Valência x Energia por Artista') +
scale_color_manual(name = 'Artista', values = c('#009392', '#9ccb86', '#eeb479', '#cf597e'))Já vimos como adicionar etiquetas aos gráficos, como alterar o nome
dos eixos e da legenda, mas e como farÃamos para alterar o tamanho da
fonte? Todos os temas do ggplot2 são definidos a partir da
função theme() e ela pode ser utilizada para manipular
diretamente qualquer parte de um gráfico feito com o pacote. Este link
apresenta um guia sobre como estes elementos são nomeados e como podem
ser manipulados.
Vamos aproveitar o último gráfico e alterar o tamanho da fonte do
tÃtulo, da legenda e dos eixos. Para isso utilizaremos os argumentos
plot.title, legend.title,
axis.title.x e axis.title.y para indicar quais
elementos queremos manipular. Esses argumentos receberão a função
element_text() que possui argumentos para alterar o
tamanho, cor, ângulo, fonte, entre outros.
Nesta parte é importante lembrar que o ggplot2 funciona
em camadas empilhadas, então se iremos manipular o tema manualmente
essas manipulações devem ser adicionadas como a última camada. Caso
adicionemos um tema pronto após manipular, nossas manipulações serão
sobrepostas.
base %>%
filter(artist_name %in% c('Foo Fighters', 'The Beatles', 'Katy Perry', 'Justin Timberlake')) %>%
ggplot() +
geom_point(aes(x = energy, y = valence, color = artist_name), size = 4, alpha = 0.6) +
theme_light() +
labs(x = 'Energia', y = 'Valência', title = 'Valência x Energia por Artista') +
scale_color_manual(name = 'Artista', values = c('#009392', '#9ccb86', '#eeb479', '#cf597e')) +
theme(
plot.title = element_text(size = 25, face = 'bold'),
axis.title.x = element_text(size = 18, face = 'bold'),
axis.title.y = element_text(size = 18, face = 'bold'),
legend.title = element_text(size = 15, face = 'bold')
)A função theme()também permite manipular a posição da
legenda no gráfico. Isso é feito através do argumento
legend.position, o padrão aqui é right, mas também
aceita bottom, top e left. Vamos colocar
nossa legenda na parte de baixo do gráfico.
base %>%
filter(artist_name %in% c('Foo Fighters', 'The Beatles', 'Katy Perry', 'Justin Timberlake')) %>%
ggplot() +
geom_point(aes(x = energy, y = valence, color = artist_name), size = 4, alpha = 0.6) +
theme_light() +
labs(x = 'Energia', y = 'Valência', title = 'Valência x Energia por Artista') +
scale_color_manual(name = 'Artista', values = c('#009392', '#9ccb86', '#eeb479', '#cf597e')) +
theme(
plot.title = element_text(size = 25, face = 'bold'),
axis.title.x = element_text(size = 18, face = 'bold'),
axis.title.y = element_text(size = 18, face = 'bold'),
legend.title = element_text(size = 15, face = 'bold'),
legend.position = 'bottom'
)Viram só? Com um pouco de cuidado na manipulação dos temas é possÃvel
se afastar bastante desses gráficos básicos do ggplot2. Um
outro pacote muito útil para temas do ggplot2é o bbplot que trás temas
baseados nos gráficos da BBC. Se o pessoal da BBC achou bom fazer isso,
quem sou eu para discordar?
8 - E o que podemos fazer com tudo isso junto?
Como vimos, o tidyversepossui pacotes para cada função
dentro do fluxo de análise de dados. Nós importamos bases de dados, as
organizamos, calculamos estatÃsticas descritivas e fizemos gráficos.
Isso já permite responder muitas questões que possam surgir sobre uma
base de dados.
Nesta seção investigaremos um pouco mais essa base, porém de um jeito mais direto ao ponto.
Quantas vezes cada banda apareceu nessa base de dados e quanto representam no total?
base %>%
group_by(artist_name) %>%
tally(sort = T) %>%
mutate(prop = n/sum(n),
prop = paste0(round(prop * 100, 3),'%')) %>%
rename(Frequência = n,
Proporção = prop,
Artista = artist_name) %>%
ungroup()## # A tibble: 14,443 x 3
## Artista Frequência Proporção
## <chr> <int> <chr>
## 1 Giuseppe Verdi 1312 0.749%
## 2 Giacomo Puccini 1094 0.625%
## 3 Kimbo Children's Music 971 0.555%
## 4 Wolfgang Amadeus Mozart 800 0.457%
## 5 Richard Wagner 778 0.444%
## 6 Nobuo Uematsu 773 0.442%
## 7 Juice Music 684 0.391%
## 8 Georges Bizet 677 0.387%
## 9 Randy Newman 667 0.381%
## 10 Johann Sebastian Bach 632 0.361%
## # ... with 14,433 more rows
Qual é o gênero musical com maior dançabilidade?
base %>%
group_by(genre) %>%
summarise(med_danc = mean(danceability, na.rm = T)) %>%
arrange(desc(med_danc)) %>%
ungroup()## # A tibble: 26 x 2
## genre med_danc
## <chr> <dbl>
## 1 Reggaeton 0.730
## 2 Hip-Hop 0.728
## 3 Rap 0.704
## 4 Reggae 0.699
## 5 Children's Music 0.697
## 6 Dance 0.642
## 7 R&B 0.638
## 8 Electronic 0.619
## 9 Soul 0.616
## 10 Pop 0.605
## # ... with 16 more rows
Mas não poderia resolver isso com um gráfico?
ggplot(base, aes(x = reorder(genre, danceability), y = danceability)) +
geom_boxplot() +
labs(title = 'Dançabilidade por gênero musical', x = 'Gênero musical', y = 'Dançabilidade') +
theme_light() +
coord_flip() +
scale_fill_discrete(name = 'Gênero') +
theme(legend.position = 'bottom')Qual é a música com maior (escolha uma variável numérica aqui) por gênero musical?
base %>%
group_by(genre) %>%
filter(energy == max(energy)) %>%
select(genre, artist_name, track_name, energy) %>%
ungroup()## # A tibble: 47 x 4
## genre artist_name track_name energy
## <chr> <chr> <chr> <dbl>
## 1 A Capella Tonic Sol-Fa Cecilia 0.818
## 2 Country Travis Tritt T-R-O-U-B-L-E - 2006 Remastered V~ 0.991
## 3 Alternative Five Finger Death Punch Fake 0.998
## 4 Alternative Meshuggah Bleed 0.998
## 5 Country Eric Church Drink In My Hand - Live 0.991
## 6 Country Zac Brown Band The Devil Went Down To Georgia - ~ 0.991
## 7 Dance Caramell Caramelldansen 0.999
## 8 Dance 2 Unlimited No Limit 0.999
## 9 Electronic Bear Grillz Wicked 0.999
## 10 Electronic Lil Texas Total Knock Out 0.999
## # ... with 37 more rows
Podemos comparar o volume das faixas de alguns gêneros?
A escala de medida de volume é em decibéis (dB), ela é uma escala log em que o 0 representa o som mais baixo que um humano consegue escutar sem auxÃlio. Valores positivos indicam que o som é algumas vezes acima desse limiar, valores negativos indicam que é algumas vezes abaixo do limiar.
base %>%
filter(genre %in% c('Hip-Hop', 'Soul', 'World', 'Dance', 'Opera')) %>%
ggplot() +
geom_density(aes(x = loudness, fill = genre), alpha = 0.5) +
labs(x = 'dB', y = 'Densidade', title = 'Distribuição do volume por gênero musical') +
scale_fill_discrete(name = 'Gênero') +
theme_light() +
theme(legend.position = 'bottom') Alguém inventou um gênero musical chamado Indie Electronic Country, será que seria popular?
A função fct_collapse() do pacote forcats
permite manipular as categorias de um fator. Isso é especialmente útil
para os momentos em que se precisa reunir categorias com poucas
observações em uma categoria ‘Outros’.
base %>%
mutate(genre = fct_collapse(genre, 'Indie Electronic Country' = c('Indie', 'Electronic', 'Country'))) %>%
group_by(genre) %>%
summarize(media_pop = mean(popularity),
mediana_pop = median(popularity),
min_pop = min(popularity),
max_pop = max(popularity)) %>%
arrange(desc(media_pop))## # A tibble: 24 x 5
## genre media_pop mediana_pop min_pop max_pop
## <fct> <dbl> <dbl> <dbl> <dbl>
## 1 Pop 67.1 66 3 97
## 2 Rap 59.5 56 35 99
## 3 Rock 58.8 57 0 90
## 4 Hip-Hop 58.5 57 17 98
## 5 Dance 57.4 57 0 100
## 6 Alternative 50.3 49 0 83
## 7 Folk 49.7 48 0 84
## 8 R&B 48.4 47 0 80
## 9 Soul 43.5 42 0 85
## 10 Indie Electronic Country 42.7 42 0 84
## # ... with 14 more rows
Encontrando as faixas mais populares na base de dados
Faremos isso de dois jeitos. No primeiro vamos apenas ordenar a base em ordem decrescente de popularitdade.
base %>%
select(track_name, artist_name, genre, popularity) %>%
arrange(desc(popularity))## # A tibble: 175,084 x 4
## track_name artist_name genre popularity
## <chr> <chr> <chr> <dbl>
## 1 7 rings "Ariana Grande" Dance 100
## 2 break up with your girlfriend, i'm bored "Ariana Grande" Dance 99
## 3 Wow. "Post Malone" Rap 99
## 4 Con Calma "Daddy Yankee" Hip-Hop 98
## 5 Without Me "Halsey" Dance 97
## 6 Sweet but Psycho "Ava Max" Dance 97
## 7 Sunflower - Spider-Man: Into the Spider-Verse "Post Malone" Rap 97
## 8 Dancing With A Stranger (with Normani) "Sam Smith" Pop 97
## 9 Happier "Marshmello" Pop 97
## 10 Calma - Remix "Pedro Cap\xf3" Pop 97
## # ... with 175,074 more rows
No segundo, vamos filtrar para manter apenas as faixas com a maior popularidade por gênero musical.
base %>%
group_by(genre) %>%
filter(popularity == max(popularity)) %>%
ungroup() %>%
select(track_name, artist_name, genre, popularity) %>%
arrange(desc(popularity))## # A tibble: 34 x 4
## track_name artist_name genre popularity
## <chr> <chr> <chr> <dbl>
## 1 7 rings "Ariana Grande" Dance 100
## 2 Wow. "Post Malone" Rap 99
## 3 Con Calma "Daddy Yankee" Hip-Hop 98
## 4 Dancing With A Stranger (with Normani) "Sam Smith" Pop 97
## 5 Happier "Marshmello" Pop 97
## 6 Calma - Remix "Pedro Cap\xf3" Pop 97
## 7 Secreto "Anuel Aa" Reggaeton 96
## 8 Bad Liar "Imagine Dragons" Rock 90
## 9 All of Me "John Legend" Soul 85
## 10 Bola Rebola "Tropkillaz" Electronic 84
## # ... with 24 more rows
9 - Extras
- Playlist das músicas mais populares por gênero na base;
- Música do tidyverse;
- Pacote DT para criar
tabelas interativas e que se integram bem com o fluxo do
tidyverse; - Pacote echarts4r para gráficos interativos. Também utiliza pipes e é uma opção bem interessante para substituir o plotly;
- Big Book of R: uma
coletânea de mais de 200 livros grátis sobre R, incluindo muitos sobre o
tidyverseou sobre pacotes especÃficos; - O
ggplot2possui uma página com extensões feitas por outros autores. A melhor de todas é a ggdogs; - Um último pacote compatÃvel com o tidyverse.