--- title: "Manipulação de dados" subtitle: "Aula 2" author: "Bruno Montezano" institute: "Grupo Alliance
Programa de Pós-Graduação em Psiquiatria e Ciências do Comportamento
Universidade Federal do Rio Grande do Sul" date: last-modified date-format: long lang: pt-br execute: echo: true format: revealjs: incremental: true smaller: true theme: [default, ../assets/custom.scss] logo: "../assets/logo_ufrgs.png" --- ## Conteúdo de hoje - Importação de dados - Valores ausentes - Fatores - Manipulação de dados - Selecionar colunas - Criar e modificar colunas - Filtragem de linhas - Ordenação de linhas - Sumarização de dados - Agrupar e resumir dados ## `tidyverse` ![](../assets/logo_tidyverse.png){fig-align="center" width=45%} ## O que é o `tidyverse`? O `tidyverse` é uma coleção de pacotes de R desenvolvidos para ciência de dados. . . . No curso, vamos usar alguns pacotes do `tidyverse`, como: - `readr`, `readxl` e `haven`: importação de dados - `dplyr`: manipulação de dados - `ggplot2`: visualização de dados . . . Para instalar o conjunto de pacotes: ```{r instalar-tidyverse} #| eval: false install.packages("tidyverse") ``` ## Importando e exportando dados com `readr` ![](../assets/logo_readr.png){fig-align="center" width=45%} ## Caminhos Um passo essencial na importação de dados é saber onde está o arquivo a ser importado. . . . Toda função vai exigir um **caminho**, ou seja, uma **string** que representa o endereço do arquivo no computador. . . . Há duas formas de passar o caminho ao R: através de um **caminho absoluto** ou **caminho relativo**. . . . Antes, vamos entender o que é o **diretório de trabalho**. ## Diretório de trabalho O diretório de trabalho (*working directory*) é a pasta na qual o R vai buscar os arquivos ao lê-los ou salvá-los. . . . Podemos descobrir que pasta é essa através da função `getwd()`: ```{r getwd} getwd() ``` A função retorna uma string com o caminho do seu diretório de trabalho. . . . O diretório de trabalho pode ser modificado através da função `setwd()`: ```{r setwd} #| eval: false setwd("/home/bruno/Documents/Projects/Thesis") ``` ## Caminhos absolutos Caminhos absolutos são aqueles que iniciam na pasta raíz do seu computador. . . . Este é o caminho absoluto onde os slides desta aula foram produzidos: ``` "/home/bruno/dox/projects/r-workshop/slides/2_manipulacao" ``` . . . Em computadores com Windows pode ter uma cara parecida com esta: ``` "C:/Users/Bruno/Documents/Projects/Thesis" ``` . . . Na maioria dos casos, caminhos absolutos são **má prática**, pois deixam o código irreprodutível. Se você trocar de computador ou passar o script para outra pessoa rodar, o código não vai funcionar, pois o caminho absoluto para o arquivo muito provavelmente será diferente. ## Caminhos relativos Caminhos relativos são aqueles que iniciam no diretório de trabalho da sua sessão. . . . O diretório de trabalho da sessão usada para produzir os slides é a pasta `r-workshop`. Olhem o caminho absoluto no slide anterior. Logo, o caminho relativo para a pasta onde os slides foram produzidos seria apenas `slides/2_manipulacao`. . . . Trabalhar com **projetos no RStudio** ajuda bastante o uso de caminhos relativos, pois nos incentiva a colocar todos os arquivos da análise dentro da pasta do projeto. Projetos podem ser criados em *Arquivo > Novo Projeto...* . . . Assim, se você usar apenas caminhos relativos e compartilhar a pasta do projeto com alguém, todos os caminhos existentes nos códigos continuarão a funcionar em qualquer computador! ## Arquivos de texto delimitados Uma das formas mais comuns de armazenar dados é em um arquivo de texto delimitado, como por exemplo: valores separados por vírgula (**.csv**) ou valores separados por tabulação (**.tsv**). :::: {.columns} ::: {.column width="50%"} Aqui estão dados em **.csv**: ``` "id","diagnostico","sexo","idade" 001,"thb","masculino",34 002,"tdm","feminino",25 003,"tdm","masculino",19 004,"thb","feminino",31 005,"tdm","feminino",24 006,"thb","masculino",21 ``` ::: ::: {.column width="50%"} E aqui estão dados em **.tsv**: ``` "id" "diagnostico" "sexo" "idade" 001 "thb" "masculino" 34 002 "tdm" "feminino" 25 003 "tdm" "masculino" 19 004 "thb" "feminino" 31 005 "tdm" "feminino" 24 006 "thb" "masculino" 21 ``` ::: :::: . . . Ou poderíamos ter um arquivo em **.txt** separado por barras, por exemplo: ``` "id"/"diagnostico"/"sexo"/"idade" 001/"thb"/"masculino"/34 002/"tdm"/"feminino"/25 003/"tdm"/"masculino"/19 004/"thb"/"feminino"/31 005/"tdm"/"feminino"/24 006/"thb"/"masculino"/21 ``` ## `readr` O pacote `readr` possui várias funções para carregar arquivos de texto delimitados: `read_csv()`, `read_csv2()`, `read_tsv()` e `read_delim()`. . . . `readr` possui algumas vantagens: - É mais rápido! - Trabalha melhor com data e hora - Barra de progresso para arquivos grandes . . . Também podemos exportar dados em `.csv`, por exemplo: ```{r exportar-csv} #| eval: false library(dados) library(readr) write_csv( x = pixar_bilheteria, file = "/home/bruno/tmp/dados_bilheteria.csv" ) ``` ## `haven` e `readxl` Na saúde, é comum trabalhar com dados provenientes de outros softwares. . . . Podemos usar algumas funções dos pacotes `haven` e `readxl` para lê-los: - Arquivos do SPSS: `read_spss()` do pacote `haven` - Arquivos do SAS: `read_sas()` do pacote `haven` - Arquivos do Stata: `read_stata()` do pacote `haven` - Arquivos do Excel: `read_xlsx()` do pacote `readxl` . . . Da mesma forma, podemos exportar dados em `.sav` (formato do SPSS): ```{r exportar-sav} #| eval: false library(dados) library(haven) write_sav( data = pixar_bilheteria, path = "/home/bruno/tmp/dados_bilheteria.sav" ) ``` ## Pacote `dplyr` ![](../assets/logo_dplyr.png){fig-align="center" width=45%} ## `dplyr` O pacote `dplyr` provê um conjunto de funções que nos ajudam nos problemas mais comuns em manipulação de dados. - `mutate()`: Adicionar ou modificar variáveis - `select()`: Selecionar variáveis (colunas) - `filter()`: Selecionar observações (linhas) - `summarise()`: Reduzir múltiplos valores a um único resumo - `arrange()`: Reordenar as observações (linhas) . . . O `dplyr` possibilita o uso da função `group_by()` para performar as operações de forma agrupada. . . . Vamos ver cada uma destas funções separadamente. ## Mas antes, *pipes* (`|>`) A maior parte das funções do `tidyverse` são construídas com o uso do **pipe** (`|>`) em mente. . . . Os atalhos para inserir o pipe são: `Ctrl + Shift + M` ou `⌘ + Shift + M`. . . . Os pipes pegam o objeto da *esquerda* e aplicam a função da *direita*. Lemos como: "e então...". ```{r exemplo-pipe} x <- c(2, 7, 40, 11, 21) sqrt(sum(x)) # Somar valores do vetor x e tirar a raíz quadrada da soma x |> sum() |> sqrt() # Mesmo cálculo, agora com pipe! ``` . . . Os pipes nos poupam tempo de digitação, tornam o código legível e permitem o encadeamento de funções como acima, por isso os usamos o tempo todo quando manipulamos data frames. ## Usando pipe Os pipes são mais legíveis quando temos cada função em uma nova linha. . . . ```{r estrutura-pipe} #| eval: false pegue_estes_dados |> aplicar_primeira_funcao(com = este_valor) |> aplicar_proxima_funcao(usando = esse_valor) |> ... ``` . . . O que estiver à esquerda do pipe (ou no exemplo, o que estiver acima) é repassado como *primeiro argumento* da função na direita (ou abaixo). Outros argumentos seguem à direita. ## Atribuindo valores ao usar pipe Ao criar objetos provenientes da saída de funções encadeadas com pipe, coloque o operador de atribuição (`<-`) no início. ```{r atribuir-com-pipe} raiz_quadrada_da_soma <- x |> sum() |> sqrt() raiz_quadrada_da_soma ``` . . . Não importa o tamanho da cadeia de funções, eu recomendo que vocês realizem a atribuição sempre no topo. ## Vamos colocar a mão na massa no `questionario` `questionario` é uma base de dados disponibilizada no pacote `dados`. ```{r carregar-questionario} # install.packages("dados") library(dados) questionario ``` Trata-se de uma base de dados do General Social Survey (GSS). Dados de 2000 a 2014. ## Valores ausentes Valores ausentes ocorrem quando nenhum valor é armazenado para uma variável em uma observação. . . . O R identifica os valores ausentes através do `NA`. Vamos entender as implicações do `NA`. . . . ```{r criar-vetor-com-missing-e-media} vetor_com_missing <- c(25, NA, 13, 44, 12, NA) mean(vetor_com_missing) ``` . . . No entanto, existem maneiras de aplicar funções em vetores (ou colunas) com `NA`s. ```{r media-com-narm} mean(vetor_com_missing, na.rm = TRUE) ``` . . . ```{r media-narm-na-mao} mean(vetor_com_missing[!is.na(vetor_com_missing)]) ``` ## Fatores **Fatores** são uma classe de dados para categorizar dados e armazenar como **níveis**. . . . Variáveis como sexo e diagnóstico psiquiátrico seriam bons exemplos de fatores. . . . ```{r vetor-com-fator} sexo <- factor(c("Masculino", "Feminino", "Feminino", "Masculino", "Masculino")) sexo ``` ```{r printar-questionario} questionario ``` ## Filtrando linhas com `filter()` Podemos usar a função `filter()` para filtrar as observações da base de dados. . . . ::: {style="font-size: 0.68em"} :::: {.columns} ::: {.column width="50%"} ```{r filtrar-divorciado} library(dplyr) divorciados <- questionario |> filter(estado_civil == "Divorciado(a)") divorciados ``` ::: ::: {.column width="50%"} ```{r divorciados-sem-missing-horas-tv} divorciados |> filter(!is.na(horas_tv)) ``` ::: :::: ::: . . . No segundo exemplo, `!is.na(horas_tv)` mantém na base de dados apenas as observações dos divorciados que **não** possuem valores ausentes na coluna `horas_tv`. ## Ordenando linhas com `arrange()` Com a função `arrange()`, nós podemos reordenar as observações da base de dados. . . . ```{r reordenar-questionario} questionario |> arrange(ano, desc(idade)) ``` . . . Os dados foram ordenados pelo `ano` de forma ascendente e `idade` de forma descendente. ## Selecionando colunas com `select()` Não apenas podemos limitar as linhas, mas podemos incluir colunas específicas (e colocá-las na ordem listada) usando `select()`. . . . ```{r selecionar-variaveis-divorciados} divorciados |> select(ano, idade, horas_tv) ``` ## Removendo colunas com `select()` Ao invés de selecionar, podemos remover colunas específicas com `select()` usando `-`. ```{r removendo-com-select} divorciados |> select(-estado_civil, -renda, -denominacao) ``` ## Funções auxiliares para o `select()` `select()` tem uma série de funções auxiliares como `starts_with()`, `ends_with()`, e `contains()`, ou pode ser dada uma gama de colunas em sequência `varinicial:varfinal`. . . . :::: {.columns} ::: {.column width="50%"} ```{r select-starts-with} questionario |> select(ends_with("ao")) ``` ::: ::: {.column width="50%"} ```{r select-sequencia} questionario |> select(ano:raca) ``` ::: :::: . . . `?select` para mais detalhes. ## Criando e modificando colunas com `mutate()` No `dplyr`, nós podemos adicionar ou modificar colunas usando `mutate()`. . . . :::: {.columns} ::: {.column width="56%"} ```{r adicionar-colunas-mutate} divorciados |> filter(raca == "Branca") |> select(ano, idade, horas_tv) |> mutate(idade_em_decadas = idade / 10, horas_tv_em_minutos = horas_tv * 60) ``` ::: ::: {.column width="44%"} ```{r modificar-colunas-mutate} divorciados |> filter(religiao == "Protestante") |> select(ano, idade, horas_tv) |> mutate(horas_tv = horas_tv * 60) ``` ::: :::: ## Agregando dados com `summarise()` `summarise()` pega suas colunas de dados e computa algo usando todas as linhas. - Contar a quantidade de linhas - Calcular a média ou mediana - Computar a soma - Obter um valor mínimo ou máximo . . . Ou seja, qualquer função que agregue *múltiplos valores* em um *único valor* (por exemplo, `sd()`, `mean()` ou `max()`) podem ser usadas com o `summarise()`. ## Exemplo com `summarise()` Para os divorciados do ano de 2000, vamos captar o número de observações, a média da idade, a mediana das horas de TV assistidas diariamente e a amplitude de horas de TV assistidas por dia. . . . ```{r exemplo-summarise} divorciados |> filter(ano == 2000) |> summarise(n_observacoes = n(), media_idade = mean(idade, na.rm = TRUE), mediana_horas_tv = median(horas_tv, na.rm = TRUE), amplitude_horas_tv = max(horas_tv, na.rm = TRUE) - min(horas_tv, na.rm = TRUE)) ``` . . . Estas novas variáveis foram calculadas usando *todas as linhas* do conjunto de dados `divorciados`. ## Evitando repetição com `summarise(across())` Talvez vocês precisem calcular a média e o desvio padrão de um conjunto de colunas. Com `across()`, coloquem as variáveis a serem calculadas primeiro (usando `c()`) e coloque as funções a serem usadas em uma `list()` depois. . . . ```{r summarise-across} divorciados |> filter(ano == 2014) |> summarise(across(c(idade, horas_tv), list(media = \(x) mean(x, na.rm = TRUE), desvio_padrao = \(x) sd(x, na.rm = TRUE)))) ``` ## Agrupando com `group_by()` A função `group_by()` muda como as funções operam sobre os dados, em especial, a `summarise()`. . . . Funções usadas após o `group_by()` são computadas dentro de cada grupo como definido pelas variáveis dadas, em vez de sobre todas as linhas de uma só vez. . . . Normalmente, vamos agrupar por variáveis de valores: - Inteiros - Fatores - Caracteres . . . E **não** por valores contínuos (números com vírgula). ## Exemplo de `group_by()` Vamos supor que eu queira saber o número de religiões reportadas pelos entrevistados, o número de observações (tamanho da amostra) e a média de horas diárias assistidas de TV em cada ano, ou seja, *agrupado* pelo ano. . . . ```{r exemplo-group-by} questionario |> group_by(ano) |> summarise(numero_de_religioes = n_distinct(religiao), n_observacoes = n(), media_horas_tv = mean(horas_tv, na.rm = TRUE)) ``` ## Tarefa de casa - Escolha uma das bases de dados do pacote `dados` e utilize ao menos uma vez as funções: `select()`, `mutate()`, `filter()`, `arrange()`, `summarise()` e `group_by()` - As bases disponíveis podem ser acessadas no [site de referência do pacote](https://cienciadedatos.github.io/dados/reference/index.html) - Nos dados `questionario` do pacote `dados`, crie um subconjunto apenas com as observações do ano de 2014 e armazene em um objeto chamado `dados_2014` - Dica de leitura: Capítulo 4 de *Data transformation* do livro [*R for Data Science (segunda edição)*](https://r4ds.hadley.nz/data-transform.html)