Tipos e estruturas de dados
Introdução
Nem toda informação em um conjunto de dados é do mesmo tipo. Algumas variáveis representam números, outras textos, datas ou categorias. Em R, cada tipo de dado é tratado de forma diferente, e reconhecer essas diferenças é essencial para evitar erros e realizar análises corretamente. O tipo de dado determina quais operações podemos realizar com cada variável. Por exemplo, podemos somar números, mas não faz sentido somar textos.
Tipos de dados
Na primeira aula realizamos operações matemáticas como esta:
Podemos usar a função class() para verificar a que classe de dados esses valores pertencem:
O resultado do código acima nos diz que estes números pertencem à classe numeric. Objetos dessa classe são armazenados internamente como números com casas decimais de alta precisão (double). Mesmo quando escrevemos um número inteiro como o 5, o R o armazena nesse formato.
Para especificar que queremos armazenar um número inteiro (integer), devemos utilizar a letra L após o número. Veja:
Na prática, em análises de dados, quase nunca é necessário usar, por exemplo, 5L em vez de 5. O R utiliza o tipo double como padrão para números e realiza conversões automáticas entre integer e double quando necessário. O uso explícito de integer é mais comum em outros contextos mais específicos.
Na aula passada, nós vimos que caminhos de diretórios são escritos entre aspas, da seguinte maneira:
"C:/Documentos/R/arquivo.xlsx"Que tipo de dado é esse? Execute o código abaixo:
Character representa textos, como por exemplo nome de amostras, localidades, tipos de rochas, códigos de identificação ou, como no caso acima, caminhos de arquivos. Não surpreendentemente, nós não podemos fazer operações matemáticas com esse tipo de dado:
Por mais que isso pareça óbvio, muitas vezes quando importamos dados, colunas com valores númericos podem ser lidas erroneamente como texto, o que pode acarretar em erros como o abaixo:
Nós podemos converter um objeto de classe character para numeric utilizando a função as.numeric():
Embora não possamos fazer operações matemáticas com textos, há uma série de outras operações que podemos fazer. Vamos ver algumas! Execute os comandos abaixo e tente entender o que cada função faz. Lembre-se de que você sempre pode consultar a documentação das funções.
Em R, além de números, textos e datas, também trabalhamos com valores lógicos, que representam condições verdadeiras ou falsas. Esses valores são:
TRUE(verdadeiro)FALSE(falso)NA(dado ausente)
É importante digitá-los com todas as letras maiúsculas. Veja:
Quando usamos valores lógicos em operações matemáticas, o R converte TRUE para 1 e FALSE para 0.:
Obtemos valores lógicos ao comparar objetos com os operadores abaixo:
| Operador | Significado |
|---|---|
== |
igual a |
!= |
diferente de |
> |
maior que |
< |
menor que |
>= |
maior ou igual |
<= |
menor ou igual |
%in% |
pertence a |
Vamos ver alguns exemplos:
Podemos combinar condições usando os seguinte operadores:
| Operador | Significado |
|---|---|
& |
e |
| |
ou |
! |
não |
O operador & só retorna TRUE se ambas as condições forem verdadeiras:
O operador | retorna TRUE se pelo menos uma condição for verdadeira:
O operador ! é usado para inverter um valor lógico. Por exemplo:
Estrutura de dados
Agora, vamos trabalhar com um conjunto real de dados de terremotos disponibilizado pela United States Geological Survey (USGS). O arquivo contém informações como data e hora do evento, magnitude, profundidade, localização e outros atributos.
Embora dados recentes de terremotos possam ser baixados diretamente no site da USGS, para garantir que todos estejam trabalhando com o mesmo conjunto, utilize a versão disponibilizada nesta página, previamente obtida com registros dos meses de Janeiro e Fevereiro.
Vamos importar os dados como aprendemos na aula passada:
Perceba que utilizamos a função head() para visualizar apenas as primeiras linhas do nosso dataset. Isso é especialmente útil quando trabalhamos com tabelas longas e queremos ter uma ideia geral do seu conteúdo. Ao consultar a documentação da função, podemos observar que o número padrão de linhas exibidas é 6, mas que é possível definir outro valor, se desejarmos:
Se quisermos ver as últimas linhas, podemos usar a função tail() de maneira similar:
Nós importamos o dataset de terremotos e usamos a variável terremotos para referenciá-lo. Mas que tipo de objeto é terremotos?
Um data frame é uma das estruturas mais importantes do R para análise de dados. Ela representa uma lista de vetores de mesmo comprimento, organizada em formato tabular.
Para entender melhor, nós vamos montar o nosso próprio data frame do zero, começando pelos vetores. Nós criamos vetores com a função c():
Nós podemos selecionar elementos específicos de um vetor com base na sua ordem, usando []:
Além disso, podemos usar : para selecionar um intervalo de elementos:
Para selecionar elementos que não estão próximos você pode usar um outro vetor dentro de []:
Também podemos selecionar um vetor com valores lógicos:
Um outra forma de usar valores lógicos é fazer comparações diretamente no []:
Um vetor requer que todos os seus elementos sejam do mesmo tipo:
Então, o que ocorre se tentarmos criar um vetor misturando diferentes tipos de dados?
O R realiza coerção automática de tipos seguindo uma hierarquia. O tipo character tem prioridade sobre numeric (double), que tem prioridade sobre integer, que por sua vez tem prioridade sobre logical.
Para misturar diferentes tipos de dados, podemos criar uma lista usando a função list():
Selecionamos elementos de uma lista de maneira similar a como fazemos com vetores:
Perceba que quando usamos [] com listas obtemos uma outra lista. Para extrair um único elemento, podemos usar [[]]:
Mas qual a vantagem de um vetor em relação à uma lista? Uma das principais vantagens de um vetor é que as operações funcionam elemento por elemento automaticamente.
Por exemplo, suponha que você queira converter a concentração de Al2O3 wt% para Al wt%. Você pode fazê-lo usando o fator de conversão 0.5293. Vejamos o que acontece se você aplica o fator de conversão à uma lista:
E o que acontece quando usamos o fator com um vetor?
Agora que entendemos o que é um vetor, podemos criar nosso data frame. Nós tínhamos definido os seguintes vetores previamente:
Nós podemos criar um data frame a partir desses vetores usando a função data.frame():
Se quisermos, também podemos nomear as colunas:
Podemos selecionar colunas específicas usando $:
Também podemos usar []:
Se quisermos selecionar linhas e colunas específicas, podemos usar [] separado por vírgulas:
Exercício 1
Agora que temos um entendimento maior do que são data frames, podemos revisitar o objeto terremotos e fazer alguns exercícios.
- Selecione todas as linhas e as colunas
time,latitude,longitude,depthemagdo objetoterremotos:
- Extraia a coluna
mage a salve como um novo objeto chamadomagnitude:
- Quais elementos de
magnitudesão maiores que 3? Faça uma comparação e a salve como um novo objetomagnitude3. Qual a classe dos elementos desse vetor?
- O que acontece quando você roda o código abaixo?
- A função
is.na()identifica dados ausentes. Quando rodamos o código abaixo, selecionamos todas as linhas cujo valor de magnitude não foi registrado, além de todas as colunas:
E se quisermos o contrário? Selecione apenas as linhas que têm valores diferente de NA para magnitude:
- Sabendo que
TRUEequivale a 1 eFALSEequivale a 0, que função em posso usar abaixo para contar quantas observações com dados ausentes eu tenho emmagnitude?
Bônus: Trabalhando com data e horas
Em muitos conjuntos de dados, precisamos trabalhar com datas, como por exemplo, data de coleta de uma amostra. Em R, embora uma data pareça um texto, ela pode ser armazenada como um tipo específico, que permite realizar cálculos.
No entanto, assim como pode acontecer com valores numéricos, muitas vezes datas são lidas como character pelo R. Podemos converter objetos de classe character para Date usando a função as.Date():
Além de data, o R tem um tipo específico para datas que incluem horas. Vamos usar as funções Sys.Date() e Sys.time() para ilustrar essa diferença. Essas funções nos dizem a data e a hora atual do nosso sistema, respectivamente:
Quando vemos POSIXt, significa que o objeto é uma data-hora. Uma data-hora pode ser armazenada como POSIXct, como vimos acima, ou como POSIXlt:
O objeto terremotos têm duas colunas que deveriam armazenar data-horas: time e updated.
No entanto, elas foram lidas como character:
Uma das maneiras de fazer a conversão de character para POSIXt seria através das funções as.POSIXct() ou as.POSIXlt(). Veja um exemplo:
Note que para usar essa função, nós precisamos especificar o formato da data-hora e o fuso horário, o que não é muito prático.
Para facilitar a nossa vida, o pacote lubridate foi desenvolvido. Para converter uma data-hora que contém anos, meses, dias, horas, minutos e segundos, podemos usar a função ymd_hms() desse pacote:
Observação:
Perceba que se rodarmos o código abaixo:
Nenhuma alteração ocorre na coluna:
Para isso, temos que atualizar as colunas usando o operador <-:
Agora, se inspecionarmos o objeto, os dados estarão corretamente armazenados:
Exercício 2
- Nesta aula nós trabalhamos com a função
read.csv(), que faz parte do R base. Para esse exercício, instale o pacotereadre use sua funçãoread_csv()no lugar deread.csv()para importar os dados de terremoto:
- Inspecione com a função
str()o objeto que você criou. Que diferença você nota em comparação com a estrutura que vimos anteriormente após usarread.csv()?
- Qual a classe do objeto
terremotos? Qual a classe do objetomineraisque criamos comread_excel()na aula passada?
- Tanto a função
read_csv()do pacotereadrquanto a funçãoread_excel()do pacotereadxlque vimos na aula passada fazem parte de uma coleção de pacotes chamada tidyverse. Que estrutura de dados os pacotes da coleção tidyverse usam? Por quê?
Considerações finais
Nesta aula, exploramos os principais tipos de dados do R e entendemos como eles influenciam as operações que podemos realizar. Também vimos como esses tipos se organizam em estruturas como vetores, listas e tabelas. Observamos que, ao importar dados reais, é fundamental verificar a estrutura e as classes das variáveis para evitar erros nas análises. Mais adiante, veremos que muitos pacotes foram desenvolvidos para trabalhar diretamente com dados tabulares, tornando operações como filtrar, selecionar e transformar dados mais claras e consistentes.