Walkthrough — Manuseio de dados com o tidyverse

Aula 04 · Introdução à Linguagem R

Este documento é o aula-04.R executado, com as saídas à vista. Use-o para conferir se o que apareceu no seu console é o que deveria aparecer.

O arquivo de entrada é o anuros_altitude.csv; a descrição de cada coluna está nos metadados.

library(dplyr)
library(tidyr)

Ler e olhar

bruto <- read.csv(here::here("dados", "anuros_altitude.csv"))
dim(bruto)
[1] 225  26
names(bruto)
 [1] "ID"                       "Species"                 
 [3] "Sex"                      "EWL_Ugcm2s1"             
 [5] "WU_Ugcm2s1"               "perc_hidration_after_EWL"
 [7] "Bodymass_g"               "CTmin"                   
 [9] "CTmax"                    "Tbr"                     
[11] "WT"                       "Mountain_Range"          
[13] "Altitude_m"               "Altitude_categorical"    
[15] "Lat"                      "Lon"                     
[17] "BIO_1"                    "BIO_2"                   
[19] "BIO_5"                    "BIO_6"                   
[21] "BIO_7"                    "BIO_12"                  
[23] "BIO_13"                   "BIO_14"                  
[25] "PET_min_mes_seco"         "PET_max_mes_umido"       

Vinte e seis colunas, e os nomes carregam a unidade: EWL_Ugcm2s1, Bodymass_g, Altitude_m. Isso é bom para quem lê o arquivo solto e ruim para quem digita — vamos mover a unidade para o comentário e encurtar o nome.

Renomear

anuros <- bruto %>%
  rename(
    id        = ID,
    especie   = Species,
    sexo      = Sex,
    ewl       = EWL_Ugcm2s1,     # µg H2O / cm2 / s
    wu        = WU_Ugcm2s1,      # µg H2O / cm2 / s
    massa     = Bodymass_g,      # g
    ctmin     = CTmin,           # °C
    ctmax     = CTmax,           # °C
    amplitude = Tbr,             # °C  (derivada: ctmax - ctmin)
    tol_aquec = WT,              # °C  (derivada: ctmax - BIO_5)
    serra     = Mountain_Range,
    altitude  = Altitude_m       # m
  )
AvisoA unidade não sumiu — ela mudou de lugar

Tirar _g do nome só é aceitável porque a unidade está registrada em dois lugares: no comentário ao lado e no arquivo de metadados. Encurtar nome sem fazer isso é perder informação.

Selecionar

anuros <- anuros %>%
  select(id, especie, sexo, serra, altitude,
         massa, ewl, wu, ctmin, ctmax, amplitude, tol_aquec,
         bio5 = BIO_5, bio6 = BIO_6)
names(anuros)
 [1] "id"        "especie"   "sexo"      "serra"     "altitude"  "massa"    
 [7] "ewl"       "wu"        "ctmin"     "ctmax"     "amplitude" "tol_aquec"
[13] "bio5"      "bio6"     

select() renomeia também (bio5 = BIO_5), o que evita um rename() a mais.

Tipos

anuros <- anuros %>%
  mutate(
    especie   = factor(especie),
    serra     = factor(serra),
    sexo      = factor(sexo),
    alt_fator = factor(altitude, levels = sort(unique(altitude)))
  )
str(anuros)
'data.frame':   225 obs. of  15 variables:
 $ id       : chr  "RPB 41" "RPB 42" "RPB 69" "RPB 114" ...
 $ especie  : Factor w/ 5 levels "Boana faber",..: 5 5 5 5 5 5 5 5 5 5 ...
 $ sexo     : Factor w/ 2 levels "Female","Male": 2 2 NA 2 2 2 1 NA NA NA ...
 $ serra    : Factor w/ 2 levels "Serra da Mantiqueira",..: 2 2 2 2 2 2 2 2 2 2 ...
 $ altitude : int  820 820 820 820 820 820 820 1022 1022 1022 ...
 $ massa    : num  218.9 233.1 63.6 85 207.2 ...
 $ ewl      : num  1.97 2.08 2.77 1.52 1.45 ...
 $ wu       : num  93.1 55.3 54 78 77.7 ...
 $ ctmin    : num  3.4 4.2 3.2 1.8 2 2.8 4 2.8 3.4 1.3 ...
 $ ctmax    : num  39.5 39.3 40.6 39 39.6 38.8 NA 39.1 39.1 39 ...
 $ amplitude: num  36.1 35.1 37.4 37.2 37.6 36 NA 36.3 35.7 37.7 ...
 $ tol_aquec: num  14.2 14 15.3 13.7 14.3 ...
 $ bio5     : num  25.3 25.3 25.3 25.3 25.3 ...
 $ bio6     : num  7.2 7.2 7.2 7.2 7.2 ...
 $ alt_fator: Factor w/ 6 levels "35","550","820",..: 3 3 3 3 3 3 3 4 4 4 ...

Repare em alt_fator: altitude é um número, mas nesta amostra ela assume seis valores, que são seis sítios. Dependendo da pergunta, ela funciona como número (uma reta ao longo do gradiente) ou como fator (seis grupos). Guardar as duas versões evita ter que decidir agora.

Recalcular as derivadas

anuros <- anuros %>%
  mutate(amplitude = ctmax - ctmin,
         tol_aquec = ctmax - bio5)

O arquivo já trazia Tbr e WT prontos. Recalculamos assim mesmo — não por desconfiança do autor, mas porque agora a definição de cada uma está escrita no código, e não só num arquivo de metadados que o leitor pode não abrir.

all.equal(anuros$amplitude, bruto$Tbr)
[1] "Mean relative difference: 6.843391e-05"
all.equal(anuros$tol_aquec, bruto$WT)
[1] TRUE

A amplitude bate exatamente. A tolerância ao aquecimento devolve uma diferença relativa de cerca de 7 × 10⁻⁵ — em valores da ordem de 10 °C, isso é menos de 0,001 °C. Não é discordância: é o arredondamento com que a coluna pronta foi gravada no arquivo original.

Dicaall.equal(), não ==

== compara bit a bit e diz FALSE para dois números que diferem na décima quinta casa decimal — o que acontece o tempo todo em ponto flutuante. all.equal() compara com tolerância e, quando discorda, diz de quanto.

Filtrar

anuros %>% filter(especie == "Boana faber") %>% nrow()
[1] 46
anuros %>% filter(altitude >= 1000, !is.na(ctmax)) %>% nrow()
[1] 106
anuros %>% filter(is.na(sexo)) %>% nrow()
[1] 107

Cento e sete indivíduos sem sexo registrado, de 225. Não é erro de digitação: sexar anuro juvenil ou preservado nem sempre é possível. Mas é uma variável que você não vai conseguir usar como preditora.

split-apply-combine

resumo <- anuros %>%
  group_by(especie, alt_fator) %>%
  summarise(
    n         = n(),
    ctmax_med = round(mean(ctmax, na.rm = TRUE), 2),
    ctmax_dp  = round(sd(ctmax,   na.rm = TRUE), 2),
    ewl_med   = round(mean(ewl,   na.rm = TRUE), 3),
    .groups = "drop"
  )
resumo
especie alt_fator n ctmax_med ctmax_dp ewl_med
Boana faber 35 12 38.41 0.54 1.344
Boana faber 550 3 37.53 1.55 1.410
Boana faber 820 10 39.10 0.80 1.524
Boana faber 1022 10 38.28 0.49 1.109
Boana faber 1600 11 38.27 0.63 1.218
Dendropsophus minutus 35 15 33.43 1.50 1.674
Dendropsophus minutus 820 10 33.84 1.72 2.080
Dendropsophus minutus 1022 12 33.92 0.81 1.816
Dendropsophus minutus 1500 12 31.16 2.49 1.779
Dendropsophus minutus 1600 10 32.80 1.38 2.010
Leptodactylus latrans 35 12 39.10 0.61 1.879
Leptodactylus latrans 1022 8 38.69 0.53 1.808
Leptodactylus latrans 1600 11 38.49 0.58 1.599
Physalaemus cuvieri 550 4 36.25 0.65 2.934
Physalaemus cuvieri 820 9 34.65 2.76 2.698
Physalaemus cuvieri 1022 14 35.72 0.98 2.680
Physalaemus cuvieri 1600 12 34.40 0.81 2.469
Rhinella icterica 550 11 38.95 0.78 1.972
Rhinella icterica 820 7 39.47 0.63 1.874
Rhinella icterica 1022 10 38.86 0.30 1.659
Rhinella icterica 1500 11 38.33 0.66 1.502
Rhinella icterica 1600 11 38.51 0.56 1.398
Nota.groups = "drop"

Sem ele, o resultado sai ainda agrupado por especie, e a próxima operação que você fizer age dentro de cada espécie sem avisar. É a fonte silenciosa de resultado errado mais comum do dplyr.

O desenho amostral, em uma linha

anuros %>% count(serra, altitude)
serra altitude n
Serra da Mantiqueira 550 18
Serra da Mantiqueira 1600 55
Serra do Mar 35 39
Serra do Mar 820 36
Serra do Mar 1022 54
Serra do Mar 1500 23

Leia essa tabela com atenção: cada altitude aparece em uma serra só.

Altitude Serra
35, 820, 1022, 1500 m Serra do Mar
550, 1600 m Serra da Mantiqueira

Altitude e serra não estão cruzadas. Isso não é defeito do arquivo — é como a coleta foi feita, e vai determinar quais modelos são possíveis na aula 06. Guarde.

Gravar

dir.create("dados/processados", showWarnings = FALSE, recursive = TRUE)
write.csv(anuros, "dados/processados/anuros_limpo.csv", row.names = FALSE)

Duas regras que valem para sempre:

  1. O dado bruto nunca é sobrescrito. Ele fica em dados/, somente leitura, exatamente como veio da fonte.
  2. O dado processado é descartável. Ele vive em dados/processados/, que entra no .gitignore — porque o script que o gera está versionado, e isso basta para reconstruí-lo.

Exercício

  1. Quantas espécies ocorrem em ambas as serras? Responda com dplyr, não contando na tabela.
  2. Monte um resumo de ewl por serra e sexo. O que acontece com a linha dos NA de sexo, e o que você faz com ela?
  3. amplitude tem 35 valores faltantes, os mesmos de ctmax. Mostre com código que são de fato os mesmos indivíduos.