Ir para o conteúdo

Estruturando Banco

Hoje, vamos trabalhar com um banco de dados sobre doenças cardíacas. O dado está disponível UCI - Machine Learning Repository e no Kaggle. Os responsáveis pelo estudo foram as seguintes entidades:

  1. Hungarian Institute of Cardiology. Budapest: Andras Janosi, M.D.
  2. University Hospital, Zurich, Switzerland: William Steinbrunn, M.D.
  3. University Hospital, Basel, Switzerland: Matthias Pfisterer, M.D.
  4. V.A. Medical Center, Long Beach and Cleveland Clinic Foundation: Robert Detrano, M.D., Ph.D.

A diagnóstico de doença cardíaca foi feita a partir de uma angiografia. Se houvesse estreitamento maior do que 50% para pelo menos um vaso sanguíneo principal, o paciente foi classificado como portador de doença cardíaca. Caso contrário, o paciente não foi diagnosticado com a doença.

Variável Descrição
age_sex Idade - Sexo
cp Tipo de dor no peito
trestbps Pressão sanguínea (mm Hg) em descanso
chol Colesterol (mg/dl)
fbs Açúcar no sangue em jejum maior do que 120mg/dl (1 = Sim, 0 = Não)
restecg Resultados eletrocardiográficos em repouso
thalach Maior ritmo cardíaco atingido
exang Angina induzida por exercício
ca Número de vasos principais coloridos por flourosopy
target Diagnóstico de doença cardíaca (1 = Sim, 0 = Não)

Vamos começar, então?

O banco hoje está em formato Excel. Você sabe qual função e pacote utilizamos para abrir esse arquivo?

library(<pacote>)

banco <- <funcao>(<caminho_do_arquivo>)

1. tidyr

O tidyr é o pacote utilizado para estruturar os nossos bancos de dados. Em geral, ele pode ser utilizado para unir (unite) e separar (separate) colunas ou para derreter (gather) e esticar (spread) as colunas.

Esse pacote é construído com base no conceito de tidy data. Deixar os seus dados tidy significa transformar a estrutura deles de tal maneira que tenhamos observações nas linhas, variáveis nas colunas e valores nas células. Em geral, esperamos também que um banco tidy contenha apenas uma unidade de observação, granularidade, etc.

O que é unidade de observação / granularidade?

Trata-se da unidade contida nas linhas do seu banco. Por exemplo, se montarmos um banco de dados com informações sobre pessoas (altura, idade, peso, etc.) em cada linha teremos pessoas como unidade de observação. Qual a unidade de observação do nosso banco?

1.1 separate()

Instalando pacotes

Caso você não tenha o tidyr ou o tibbleno seu computador, por favor, execute o código install.packages(c('tidyr', 'tibble')).

separate() é uma função do pacote tidyr que tem como objetivo separar valores contidos em uma coluna. Às vezes, mais de uma informação é agrupado dentro da mesma variável. Repare no exemplo abaixo.

exemplo_separete <- tibble::tribble(
  ~ CIDADE , ~MORTALIDADE,
  'São José dos Campos - SP', 80,
  'Porto Alegre - RS', 100,
  'Brasília - DF', 81
)

exemplo_separete

Qual variável contém mais de uma informação? Por mais que talvez você tenha o costume de agrupar a sua cidade à unidade federativa dela, esses dois valores dizem respeito a qualidades diferentes da nossa obervação. Imagine que o banco se estendesse para todas as cidades brasileiras. Como faríamos para identificar as unidades federativas com maior média de mortalidade?

Como aplicar a função separate()? Antes de tudo, tente executar o comando ?separate. A documentação do tidyr tende a ser muito boa e normalmente teremos uma boa explicação de como a função opera e de quais parâmetros precisamos utilizar nela.

No caso, o separate() recebe (1) no primeiro parâmetro o banco de dados, (2) no segundo, a coluna que desejamos separar, (3) na terceira o nome das colunas que desejamos criar a partir da coluna informada no segundo parâmetro, (4) o separador, ou seja, o padrão de caracteres que serão utilizados para separar a coluna do segundo parâmetro nas colunas especificadas no terceiro parâmetro. Isso pode parecer complicado, mas é bem simples.

separate(<dataframe>, <coluna_que_desejamos_separar>, <vetor_com_as_colunas_a_serem_criadas>, <separador>)

Tente fazer sozinho.



  • Reposta:
separate(exemplo_separete, 'CIDADE', c('CIDADE', 'UF'), '-')

1.1.1. Aplicando no nosso banco

Você consegue descobrir qual variável no nosso banco tem esse problema?

Funções para explorar dataframes

Tente utilizar funções para explorar o seu dataframe. Por exemplo, com head() conseguimos obter facilmente as primeiras linhas do nosso banco.

Você percebeu alguma coisa de estranho na variável age_sex? O que?

age_sex contém dois tipos de informações diferentes. De um lado, temos a idade da pessoa. Do outro, temos o sexo dessa pessoa. Novamente, precisamos separar essas duas informações em duas variáveis para que possamos prosseguir com a nossa análise.

Vamos lá? Agora é a sua vez. Como precisamos fazer para separar as duas variáveis? 💪

PS: Não se preocupe com o remove = FALSE. Utilizamos esse parâmetro apenas para manter a variável antiga e sermos capazes de validar o nosso resultado.

separate(<banco>, <variavel>, <vetor>, <sep>, remove = FALSE)

Que tal dar um View() no seu banco agora?

View(banco)

Salvando alterações

Nunca se esqueça de que o R não sabe que você deseja sobrescrever uma variável. Ao contrário do Stata, por exemplo, as alterações devem ser escritas sobre uma variável com <-. Caso contrário, o R irá apenas imprimir no console o resultado.


Agora, podemos utilizar o View() ou o head() para verificar se deu tudo certo.

1.2. unite

A função unite() tem o comportamento inverso separate(). Nós a utilizamos para unir valores que fazem mais sentido juntos do que separados. Repare no exemplo abaixo. O que você faria para obter em uma única variável uma informação mais precisa?

exemplo_unite <- tibble::tribble(
  ~ID,~DIA, ~MES, ~ANO, ~DOSE_REMEDIO, ~RESULTADO,
  1, 14, 8, 2018, 1, 0,
  1, 15, 8, 2018, 2, 0,
  2, 9, 7, 2018, 1 ,0,
  2, 10, 7, 2018, 2, 0,
  2, 11, 7, 2018, 3, 1
)

exemplo_unite

Após executar o código acima, vamos prosseguir com a função unite(). Ela recebe (1) no primeiro parâmetro o banco de dados e (2) no segundo o nome da nova variável. Em seguida, (3) adicionamos todas as colunas que queremos concatenar (4) e, por fim, podemos escolher o separador entre as colunas.

unite(<banco>, <nome_da_nova_variavel>, sep = '<separador>')

Vamos tentar sozinho primeiro?



  • Resposta:
banco <- unite(exemplo_unite, "DATA", ANO, MES, DIA, sep = "-")

Verifique o resultado com a função head().

1.2.1. Aplicando no nosso banco

Que tal arrumar, agora, o nosso banco de dados também? Lembre-se que é possível utilizar a as funções head(), str(), entre outras para ter uma ideia geral da estrutura do nosso dataframe.

Provavelmente, você reparou que existem três variáveis que, na verdade, fornam uma única: birth_day, birth_month e birth_year. Que tal tentar uni-las com a função separate()?

2. Exercícios - Parte 1

Nós acabamos de aprender a estruturar o nosso banco no que diz respeito a unir e separar variáveis. Vamos praticar mais um pouco?

2.1. Exercício - Exemplos de unite e separate

Para cada tabela abaixo (1) diga se é necessário realizar um unite() ou um separate() e (2) escreva o código que corrija esse problema.

  • Item I:
ex2_1_1 <- tibble::tribble(
  ~ANO, ~RAZAO_DE_HOMICIDIO,
  2014, '1200293 / 102000000',
  2015, '201992 / 102929222',
  2016, '203918 / 175999271',
  2017, '2901827 / 228191900',
  2018, '201928 / 201928238', 
)
  • Item II:
ex_2_1_2 <- tibble::tribble(
  ~NOME, ~LOGRADOUR, ~NUMERO, ~COMPLEMENTO,
  'Lyandra', 'Rua Ademar de Barros', 20, 'APT 28',
  'Monica', 'Avenida São Pedro', 30, 'BLOCO A',
  'Luis', 'Rua do Lago', 22, 'Portão do lado esquerdo',
  'Isaac', 'Avenida Paulista', 22, 'APT 102',
  'Sônia', 'Rua Brigadeiro', 982, 'APT 283'
)
  • Item III:
ex_2_1_3 <- tibble::tribble(
  ~ID, ~DIA, ~MES, ~ANO, ~ALTURA_PESO,
  1, 10, 2, 1998, '180 - 340',
  2, 11, 2, 1998, '190 - 200',
  3, 20, 3, 1998, '188 - 176',
  2, 30, 5, 1998, '192 - 180'
)

Parabéns! 🎉 🎉 🎉

3. Gather e Spread

Você já ouviu falar em bancos no formato wide e long?

Mortalidade na China e no Brasil - Wide

País 2015 2016
Brasil 12 12.2
China 20 30

Mortalidade na China e no Brasil - Long

País Ano Mortalidade
Brasil 2015 12
Brasil 2016 12.2
China 2015 20
China 2016 30

Qual a diferença entre os dois bancos? Quais deles está no formato tidy? Se possível escreva ou tente explicar em voz alta.


Como o primeiro banco não contém apenas uma obervação por linha, ele não é tidy. Você concorda que as tomadas de dados sobre mortalidade sobre dois anos não podem ocorrer simultaneamente no mesmo ano? Além disso, 2015 e 2016 estão ocupando a posição de variáveis, mas elas na verdade são valores de uma outra variável? Você sabe dizer qual?

O exemplo de banco tidy é o segundo. Nele, temos as variáveis claramente denominadas no cabeçalho do nosso banco. País é uma variável, assim como Ano e Mortalidade. E cada linha possui uma única observação.

Que fique bem claro: tidy não é uma definição de qualidade do banco. Apenas tentamos estruturar a nossa tabela nesse formato porque isso torna mais fácil a interação com outras funções (ex. dplyr, ggplot, etc.).

3.1. gather()

gather() é utilizado para derreter/agrupar as colunas dos nossos bancos. Pensando no exemplo anterior, essa função transforma o nosso banco wide em um banco long.

tabela_wide <- tibble::tribble(
  ~País , ~`2015`, ~`2016`,
  'Brasil' , 10.2 , 11.2,
  'China' , 14.3 , 18.4,
)

tabela_wide

gather() recebe (1) o banco de dados, (2) o nome da variável (key) que está como nome das colunas, (3) o nome da variável que está nas células, (4) os nomes das colunas em que iremos realizar a operação sem aspas.

gather(<banco>, <key>, <value>, <VAR1>, <VAR2>, <VARn>)

Dado isso, qual código devemos utilizar para realizar a transformação em tabela_wide.


  • Resposta:
gather(tabela_wide, key = 'ano', value = 'mortalidade' ,`2015`, `2016`)

3.1.1. Aplicando

Como o nosso banco não possui esse problema, vamos fingir que decidimos realizar uma segunda coleta de dados e queremos avaliar a evolução de doenças cardíacas nossos pacientes após 1 ano. Porém, o seu estagiário era preguiçoso e ao invés de estruturar o seu banco no formato tidy ele (1) alterou o nome da coluna target para target2018 e (2) criou uma coluna target2019 para os novos resultados

ex3_1_1 <- tibble::tribble(
  ~ID, ~target2018, ~target2019,
  1, 0, 0,
  2, 1, 1,
  3, 1, 1,
  4, 1, 1,
  5, 1, 0,
)

ex3_1_1

Qual código seria utilizado para deixar essa tabela tidy?

3.2. spread()

spread() realiza a operação inversa do gather(). Ela estica os nossos dados horizontalmente. Mas isso não acaba com o formato tidy do nosso banco? Sim, mas lembre-se que apenas estruturamos os nossos dados de acordo com os princípios do tidy data porque a maior parte das funções esperam esse tipo de estrutura! Isso não significa que ao apresentar uma tabela para alguém ela deva estar no formato tidy. Ela simplesmente deve estar na maneira mais intuitiva de ler.

tabela_long <- tibble::tribble(
  ~pais, ~ano, ~mortalidade,
  'Brasil' , 2015 , 10.2,
  'Brasil' , 2016 , 11.2,
  'China' , 2015 , 14.3,
  'China' , 2016 , 18.4,
)

tabela_long

A função spread() recebe os seguintes parâmetros: (1) Primeiro, precisamos fornecer o banco de dados; (2) em segundo lugar, o nome da variável cujo os valores serão dispostos como colunas; (3) por fim, a variável que fornecerá os valores para as células. São os mesmos parâmetros da função gather()!

spread(<banco>, <key>, <value>)

Como você faria para deixar o país na linha e os anos no cabeçalho com os valores de mortalidade ocupando o centro da tabela?



  • Resposta:
spread(tabela_long, ano, pib)

3.2.1. Aplicando no nosso banco

Por favor, execute o código abaixo. Não se preocupe em entender como ele funciona. Nós veremos isso na próxima aula. Tenha apenas em mente que ele retorna a quantidade de observações diagnosticadas com doença cardíaca entre homens e mulheres.

ex3_1_1 <- banco %>% 
  group_by(sex, target) %>%
  summarise(n = n())

ex3_1_1

Será que essa é a melhor maneira de visualizar esse resultado? Ainda que você consiga extrair alguma informação, imagine se tivéssemos 3 ou 4 categorias em cada variável. Provavelmente, não seria tão fácil. Uma maneira elegante de resolver esse problema é com a função spread(). Como você aplicaria essa função para que a variável target fique no cabeçalho?

4. Exercícios - Parte 2

4.1. Exercício - spread()

Apresente a tabela abaixo de tal maneira que as UFs fiquem nas linhas e o nível de escolaridade esteja distribuído pelas colunas.

  • Item I:
ex_4_1_1 <- tibble::tribble(
  ~UF, ~mes, ~nivel_escolaridade,
  'SP', 'Jan', 20.2,
  'SP', 'Fev', 29.2,
  'SP', 'Mar', 12.3,
  'SP', 'Abr', 14.3,
  'RJ', 'Jan', 28.2,
  'RJ', 'Fev', 19.2,
  'RJ', 'Mar', 9.3,
  'RJ', 'Abr', 30.3,
)
  • Item II:
ex_4_1_2 <- tibble::tribble(
  ~pais, ~ano, ~venda,
  'Brasil', 2014, 20.2,
  'Brasil', 2015, 29.2,
  'Brasil', 2016, 12.3,
  'Brasil', 2017, 14.3,
  'Colômbia', 2014, 28.2,
  'Colômbia', 2015, 19.2,
  'Colômbia', 2016, 9.3,
  'Colômbia', 2017, 30.3,
)

4.2. Exercício - gather()

Transforme as próximas tabelas em tidy data.

  • Item I: Escolaridade:
ex_4_2_1 <- tibble::tribble(
  ~pais, ~`Jan`, ~`Fev`,
  'China', 92, 20.2,
  'EUA', 10.2, 42,
  'França', 72.2, 26,
  'Chile', 80.2, 90,
  'Japão', 19.1, 25,
)
  • Item II: Mortes por arma de fogo
ex_4_2_2 <- tibble::tribble(
  ~UF, ~`2015`, ~`2016`,
  'SP', 92, 20.2,
  'RJ', 10.2, 42,
  'RS', 72.2, 26,
  'CO', 80.2, 90,
  'PE', 19.1, 25,
)

4. dplyr

Dentre os pacotes mais importantes do tidyverse, o dplyr se destaca. Ele traz uma biblioteca pode rasa de funções que nos permitem transformar dados. Essa é a tarefa que você provavelmente passará mais tempo fazendo já que nem sempre os dados estão limpos. Muitas vezes teremos que criar variáveis ou extrair parte da informação presente em uma antes de rodarmos um modelo.

Quais são os verbos do dplyr? Obviamente, o pacote é extenso e possui uma quantidade razoável de funções, mas queremos que você se atente, agora, a três funções: select(), filter(), mutate().

4.1. select()

O select() é capaz de selecionar colunas específicas do nosso banco de dados. O uso dela é bem simples e precisamos apenas escrever as variáveis que desejamos selecionar sem aspas. Caso você deseje excluir uma variável, acrescente um - antes do no nome. Assim como nas funções do tidyr (gather(), spread(), etc.), informamos o banco de dados no primeiro parâmetro de nossa função.

select(<banco>, <VAR1>, <VAR2>, -<VAR3>)

Repare que antes de <VAR3> nós adicionamos um -. Isso significa que queremos excluir essa coluna no banco

Imagine que após muita pesquisa, você tenha descoberto que a melhor variável para prever doenças cardíacas seja idade (age). Como você faria para selecionar apenas target e age?



  • Resposta:
select(banco, age, target)

Agora, imagine que tenha sido provado que sexo é irrelevante. Como você faria para excluir essa variável do banco?



select(banco, -sex)

4.2. filter()

Novamente, o nome da função já entrega o objetivo dela. filter() filtra (dãã) as observações da nossa tabela por meio de operações booleanas.

Operadores lógicos

Nós vimos na primeira aula que operadores lógicas são operações que retornam necessariamente apenas dois resultados: verdadeiro e falso. A partir de agora, começaremos a utilizar eles cada vez mais. Então, se não estiver se sentindo confortável com o tema, volte para a primeira aula e reveja os exemplos.

Como ela funciona? Simples, você apenas precisa realizar um teste de verdadeiro e falso a partir de uma variável do seu banco.

filter(<banco>, <operacao_booleana>)

Repare no banco abaixo. Como você faria para selecionar as observações de SP?

exemplo_filter <- tibble::tribble(
  ~UF, ~`2015`, ~`2016`,
  'SP', 92, 20.2,
  'RJ', 10.2, 42,
  'RS', 72.2, 26,
  'CO', 80.2, 90,
  'PE', 19.1, 25,
)

exemplo_filter

Qual o operador lógico que testa igualdade? Se você pensou no ==, acertou! 🎉 Agora, só precisamos colocá-lo dentro do filter() e deixar que mágica aconteça.



  • Resposta:
filter(exemplo_filter, UF == 'SP')

4.3. mutate()

mutate() nos permite transformar e criar colunas em nossa tabela de maneira rápida e intuitiva. Repare na tabela abaixo.

exemplo_mutate <- tibble::tribble(
  ~UF, ~mes, ~total, ~homi
  'SP', 'Jan', 20.2, 3
  'SP', 'Fev', 29.2, 4
  'SP', 'Mar', 12.3, 6
  'SP', 'Abr', 14.3, 4
  'RJ', 'Jan', 28.2, 3
  'RJ', 'Fev', 19.2, 3
  'RJ', 'Mar', 9.3, 10
  'RJ', 'Abr', 30.3, 20
)

Sento total o total de mortes registradas naquele mês e homi valor absoluto de homicídios, é possível retirar a taxa de homicídios entre todas as outras mortes realizando com a operação de divisão.

mutate(<banco>,
       <variavel> = <operacao>)

Novamente, o banco de dados é fornecido como primeiro parâmetro. A diferença é que dessa vez iremos fazer referências a variáveis (<variavel>) dentro da função mutate() para alterá-las. Em <operacoes>, você pode fazer referência a outras variáveis do banco ou até mesmo de outros objetos para realizar a sua conta.

Você consegue imaginar como escrever o código acima para retirar a taxa de homicídios?