library(dplyr)
library(tidyr)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.
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
)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.
all.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 |
.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:
- O dado bruto nunca é sobrescrito. Ele fica em
dados/, somente leitura, exatamente como veio da fonte. - 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
- Quantas espécies ocorrem em ambas as serras? Responda com
dplyr, não contando na tabela. - Monte um resumo de
ewlporserraesexo. O que acontece com a linha dosNAde sexo, e o que você faz com ela? amplitudetem 35 valores faltantes, os mesmos dectmax. Mostre com código que são de fato os mesmos indivíduos.