Limpando e organizando dados com o dplyr
Introdução
Nesta aula, vamos trabalhar com o seguinte conjunto de dados:
- Major and trace element concentrations and Sr, Nd, Hf, Pb isotope ratios of global mid ocean ridge and ocean island basalts (Stracke et al. 2022)
Esse dataset está disponível no banco de dados GEOROC (Lehnert et al. 2000) e pode ser baixado através deste link.
Diferentemente das planilha com as quais trabalhamos até então, essa precisa de mais ajustes após a importação. Por exemplo, ela apresenta colunas duplicadas, algumas colunas totalmente vazias e alguns dados que precisam de preparação extra para análise.
Nós vamos usar o que aprendemos de dplyr na aula passada para limpá-la e organizá-la.
Removendo colunas duplicadas
Quando importamos uma planilha do Excel, a função read_excel() evita a criação de colunas com o mesmo nome, adicionando um sufixo (…número).
Isso acontece porque o parâmetro .name_repair dessa função é definido por padrão como “unique”:
Podemos checar quais colunas tiveram seu nome modificado pela função com o código abaixo:
Para evitar que read_excel() modifique o nome das colunas, podemos alterar o argumento padrão do parâmetro .name_repair de “unique” para “minimal”:
Agora, veja o que acontece se tentarmos selecionar a coluna TI:
Para trabalhar com dados tabulares de maneira eficiente, é importante que cada coluna tenha um nome diferente. Por isso, por padrão, read_excel() automaticamente adiciona sufixos às colunas duplicadas.
Mas e se quisermos eliminar as colunas duplicadas em vez de simplesmente mudar seus nomes? Para isso, vamos usar a função duplicated(), do R base.
duplicated() retorna FALSE se é a primeira ocorrência da coluna e TRUE a partir da segunda:
Além disso, o R base tem a função which(), que retorna a posição dos elementos de um vetor lógico que são TRUE. No nosso caso, which() retorna a posição das colunas duplicadas:
Para saber a posição das colunas que não foram duplicadas, podemos usar o operador !, que tem um sentido de negação, como vimos na aula de tipos e estruturas de dados. Por exemplo:
Agora que temos o vetor numero_colunas com a posição das colunas que não foram duplicadas, podemos selecioná-las usando o verbo select() em conjunto com all_of():
Agora, veja o que acontece quando tentamos selecionar a coluna TI novamente:
Pronto, resolvemos um problema com a nossa tabela, mas ainda temos outro: as colunas vazias.
Removendo colunas vazias
Para entender como remover todas as colunas vazias, é mais fácil primeiramente considerarmos o que faríamos se quiséssemos selecionar (select()) as colunas onde (where()) todos (all()) seus valores estão ausentes (is.na()):
Lembre-se que dentro de
where()podemos usar funções anônimas. Para relembrar como escrever uma função anônima, revise a aula de controles de fluxo e funções.
Para não (!) selecionar as colunas onde todos os valores estão ausentes, só precisamos fazer uma leve modificação no código acima:
Agora, temos uma tabela sem colunas duplicadas e sem colunas vazias. No entanto, ainda temos mais um problema.
Convertendo tipos de dados
Quando observamos a estrutura de morbs com glimpse(), percebemos que algumas colunas que deveriam conter números, como LOI e CL, estão classificadas como character.
Por que isso acontece? Vamos investigar, começando pela coluna LOI:
Perceba que, embora a maioria dos valores dessa coluna seja do tipo numeric, alguns valores contêm símbolos que indicam teores abaixo do limite de detecção e que não podem ser convertidos em números. Por isso, o R converte toda a coluna para character.
O que acontece quando aplicamos a função as.numeric() a essa coluna?
Tudo o que o R não conseguiu transformar em números foi convertido para NA. Isso é justamente o que queremos, visto que não sabemos a concentração de elementos que estão abaixo do limite de detecção.
Para saber quais colunas devemos converter para numeric, primeiro vamos identificar todas as colunas que foram classificadas como character:
Agora, vamos observar os valores de cada uma dessas colunas usando um for loop:
Com o código acima, vimos que — além de LOI — HE, CL, RE, OS, HE3_HE4, LOC PREC, HE3 e PA também contêm dados numéricos que foram importados como caracteres. Portanto, vamos converter essas colunas para numeric:
Execute o código abaixo para checar se a conversão foi feita com sucesso:
Agora sim, nossa tabela está cada vez melhor, mas ainda temos mais algumas etapas de preparação de dados pela frente!
Organizando dados
Ferro (Fe) está sendo representado de 4 maneiras diferentes no nosso dataset:
Para que possamos comparar as análises de Fe entre diferentes amostras de maneira consistente, é importante que estejamos trabalhando com o mesmo tipo de óxido.
O problema é que nossa tabela não está padronizada. Por exemplo, algumas amostras apresentam valores apenas para FEO, outras apenas para FE2O3, enquanto algumas trazem FEOT ou FE2O3T. Há ainda casos em que existem múltiplas representações de óxidos de Fe para a mesma amostra e outros em que não há nenhuma.
Como lidar com essa inconsistência?
Nesta aula, vamos padronizar a representação do Fe, expressando o teor de todas as amostras (que contenham Fe) como FE2O3T. Para isso, vamos usar mutate() em conjunto com case_when():
Com isso, temos uma tabela que já está pronta para análise.
Exportando dados
Até então, temos focado em importar tabelas para dentro do R. Mas como fazemos para exportá-las? Nós podemos usar dois pacotes diferentes, dependendo da extensão que queremos usar.
Para exportar nossa tabela morbs para .csv, podemos usar a função write_csv() do pacote readr, com o qual já trabalhamos:
library(readr)
write_csv(morbs, "caminho/do/arquivo.csv")Para exportar nossa tabela morbs para .xlsx, podemos usar a função write_xlsx() do pacote writexl:
library(writexl)
write_xlsx(morbs, "caminho/do/arquivo.xlsx")Exercício
Neste exercício, você vai trabalhar no ambiente do RStudio.
No RStudio, vá em File → New File → R Script para criar um novo script.
Recrie, no seu script, o fluxo de limpeza que fizemos em aula com os dados de MORB. Seu script deve:
Importar os dados
Verificar e remover colunas duplicadas
Remover colunas vazias (apenas
NA)Verificar e ajustar, se necessário, a classe das colunas (numeric, character…)
Organizar os dados de Fe (garantindo uma única representação)
Exportar a tabela final para uma pasta do seu computador
- Agora repita o processo para os dados de OIB, que estão na segunda aba da planilha. Note que os problemas dessa tabela não são exatamente os mesmos da tabela de MORB.
Dicas para trabalhar com os dados de OIB:
Importe a tabela usando
read_excel()com.name_repair = "minimal". Você precisará ajustar outros argumentos em relação ao que foi usado para importar “Data_MORB”.Durante a importação, você verá avisos sobre a coluna
LATITUDE (MAX). Isso ocorre porque alguns valores não são numéricos. Você pode ignorar esses avisos; valores não numéricos serão convertidos paraNA.Verifique se existem colunas vazias e remova-as (faça isso antes da próxima etapa).
Verifique se existem colunas duplicadas.
Verifique se a classe das colunas é apropriada.
Organize os dados de Fe.
Exporte a tabela final.
Bônus
Existe um pacote que foi desenvolvido para limpar e organizar dados: janitor. Embora ele não seja da coleção tidyverse, ele segue os mesmos princípios e funciona bem com os outros pacotes da coleção.
Vamos ver como algumas tarefas são simplificadas com o uso desse pacote. Para isso, vamos importar novamente os nossos dados:
Veja que o nome das colunas não segue um padrão. Podemos ajeitar isso automaticamente com a função clean_names() desse pacote:
Essa função tira espaços em branco do nome das colunas, padroniza tudo com minúsculo e adiciona um sufixos numéricos (_2, _3, _4, …) para colunas duplicadas.
Como algumas colunas aparecem repetidas várias vezes, temos que remover as colunas com o código abaixo:
Para remover colunas que estejam completamente vazias, podemos usar a função remove_empty() do pacote:
remove_empty() também pode ser usado para remover linhas vazias, ou, até mesmo, linhas e colunas vazias simultaneamente. Acesse a documentação da função para saber mais.
Desafio opcional
Em programação, quanto mais geral e reprodutível for o nosso código, melhor. Portanto, em vez de remover as colunas que terminam com “_número” tendo que escrever cada sufixo diferente, como fizemos anteriormente:
morbs <- morbs |>
select(!ends_with("_2")) |>
select(!ends_with("_3")) |>
select(!ends_with("_4"))Podemos remover todas as colunas que sigam esse padrão usando expressões regulares (regex). Esse tópico não faz parte do escopo deste curso, mas você pode ler mais a respeito de regex aqui.
No entanto, fica aqui o desafio caso você tenha interesse: como remover todas as colunas cujo nome termina com “_” seguido de um número? Dica: além de usar regex, as funções select() e matches() podem ser úteis.
Considerações finais
Com a nossa tabela limpa e organizada, podemos agora focar na análise dos dados. Na próxima aula, vamos utilizar o pacote ggplot2, também parte da coleção tidyverse, para construir diagramas. Continuaremos a trabalhar com os dados de MORB e OIB que tratamos nesta aula. A imagem abaixo mostra uma prévia do que iremos desenvolver nas próximas aulas: