Organizar Dados em Tabelas
Desenho Experimental e Análise Avançada de Dados Ecológicos
Que Peixes Conservamos?
1
A
seabream
12.1
2
A
seabream
14.3
3
A
seabass
9.8
Exemplo didáctico: peixes medidos em dois tanques.
Queremos os peixes do tanque A com mais de 12 cm.
Uma Linha, Um Registo
1
P17
1
2
P23
1
3
P17
2
4
P23
2
4 observações · 2 peixes
O número do registo não identifica uma nova unidade independente.
Colunas e Variáveis
species · length_cm · tank
Cada coluna guarda uma variável; cada linha guarda um registo.
Peixes distintos podem partilhar a espécie. Partilhar um tanque também pode criar dependência.
Retomar a diferença entre repetir uma categoria e repetir uma unidade. Em amostragem independente, vários exemplares da mesma espécie não são, só por isso, medidas repetidas. Aqui os peixes partilham tanques: a tabela não basta para decidir qual seria a réplica numa análise. A visita repetida ao peixe marcado mostra um caso de identidade conhecida.
Criar Uma Tabela
fish <- data.frame (
id = 1 : 6 ,
tank = c ("A" , "A" , "A" , "B" , "B" , "B" ),
species = c ("seabream" , "seabream" , "seabass" ,
"seabass" , "seabream" , "seabass" ),
length_cm = c (12.1 , 14.3 , 9.8 , 16.0 , 11.5 , 13.2 )
)
Quatro vectores, cada um com seis elementos: os mesmos seis registos .
Um data.frame aceita colunas de tipos diferentes, mas todas têm o mesmo número de linhas. O R pode reciclar vectores mais curtos: não confiar nisso ao construir a tabela. Os nomes e valores de código estão em inglês; seabream corresponde aqui a sargo e seabass a robalo.
Primeiro: Inspeccionar
[1] "id" "tank" "species" "length_cm"
id tank species length_cm
1 1 A seabream 12.1
2 2 A seabream 14.3
3 3 A seabass 9.8
linhas × colunas → nomes → primeiras linhas
Ler a Estrutura
'data.frame': 6 obs. of 4 variables:
$ id : int 1 2 3 4 5 6
$ tank : chr "A" "A" "A" "B" ...
$ species : chr "seabream" "seabream" "seabass" "seabass" ...
$ length_cm: num 12.1 14.3 9.8 16 11.5 13.2
Confirmem: length_cm é numérica?
Linhas ou Colunas?
Que peixes?
filter()
Linhas
Que variáveis?
select()
Colunas
Começamos por escolher os peixes.
Uma Condição
[1] TRUE TRUE FALSE TRUE FALSE TRUE
TRUE · FALSE · um valor por linha
Quantos TRUE? Que identificadores esperamos conservar?
filter(): Escolher Linhas
filter (fish, length_cm > 12 )
id tank species length_cm
1 1 A seabream 12.1
2 2 A seabream 14.3
3 4 B seabass 16.0
4 6 B seabass 13.2
4 linhas · as mesmas 4 colunas
Dentro de filter() , escrevemos length_cm sem repetir fish$.
Duas Condições
filter (fish, tank == "A" , length_cm > 12 )
id tank species length_cm
1 1 A seabream 12.1
2 2 A seabream 14.3
Vírgula: ambas têm de ser verdadeiras. Ficamos com os peixes 1 e 2.
== compara valores; = nomeia argumentos.
As condições separadas por vírgula equivalem aqui a usar &. O operador | aceita uma condição ou a outra. filter() conserva apenas TRUE; se uma condição der NA, a linha fica de fora. Discutir a decisão sobre valores em falta na limpeza, sem os tratar como exclusões cientificamente justificadas.
select(): Escolher Colunas
select (fish, id, length_cm)
id length_cm
1 1 12.1
2 2 14.3
3 3 9.8
4 4 16.0
5 5 11.5
6 6 13.2
6 linhas · 2 colunas
Escolher variáveis não exclui peixes.
Encadear com |>
selected <- fish |>
filter (tank == "A" , length_cm > 12 ) |>
select (id, length_cm)
selected
id length_cm
1 1 12.1
2 2 14.3
partir de fish → escolher peixes → escolher variáveis
|> passa o resultado ao primeiro argumento da função seguinte.
mutate(): Criar Uma Coluna
prepared <- fish |>
mutate (length_m = length_cm / 100 )
select (prepared, id, length_cm, length_m)
id length_cm length_m
1 1 12.1 0.121
2 2 14.3 0.143
3 3 9.8 0.098
4 4 16.0 0.160
5 5 11.5 0.115
6 6 13.2 0.132
Mesmos seis registos. Agora, duas unidades de comprimento.
Verificar o Resultado
summary (prepared$ length_m)
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0980 0.1165 0.1265 0.1282 0.1403 0.1600
6 × 4 → 6 × 5 · Os metros correspondem aos centímetros / 100?
fish conserva os dados de partida.
Uma Pequena Adaptação
3 minutos · em pares
Adaptem o encadeamento para obter:
peixes do tanque B , com menos de 14 cm ;
apenas id e o comprimento em metros .
Antes de executar: quantas linhas e colunas esperam?
Solução esperada: fish |> filter(tank == “B”, length_cm < 14) |> mutate(length_m = length_cm / 100) |> select(id, length_m). Duas linhas (id 5 e 6), duas colunas; 0.115 e 0.132 m. Pedir que expliquem por que select() fica depois de mutate(): se retirarem length_cm antes, já não podem fazer a conversão.
Também Há Tibbles
fish_tbl <- as_tibble (fish)
class (fish_tbl)
[1] "tbl_df" "tbl" "data.frame"
Um tibble é uma forma de data.frame.
filter() , select() e mutate() aceitam ambos.
Na consola, o tibble limita a impressão e mostra os tipos das colunas. O renderer dos slides apresenta tabelas de outra forma; experimentar fish_tbl na consola para ver a diferença. Não é preciso converter para usar dplyr. A diferença de indexação que se segue é mais importante do que decorar classes.
Tabela ou Vector?
class (fish[, "length_cm" ])
class (fish_tbl[, "length_cm" ])
[1] "tbl_df" "tbl" "data.frame"
[1] 12.1 14.3 9.8 16.0 11.5 13.2
Uma coluna com [ pode simplificar o data.frame para vector.
$ pede o vector em ambos; select() conserva uma tabela.
Reconhecer Base R
fish[1 : 3 , c ("id" , "length_cm" )]
id length_cm
1 1 12.1
2 2 14.3
3 3 9.8
[linhas, colunas] · A vírgula separa as duas decisões.
Útil para ler código existente e localizar células.
Reconhecer Um Filtro
fish[fish$ tank == "A" , c ("id" , "length_cm" )]
O mesmo pedido em dplyr:
fish |> filter (tank == "A" ) |> select (id, length_cm)
id length_cm
1 1 12.1
2 2 14.3
3 3 9.8
Não pedir que mantenham duas versões de cada transformação. Preferimos dplyr no workflow; a notação de base R serve também para reconhecer posições e corrigir células. Com condições que contêm NA, [ pode produzir linhas de NA, ao contrário de filter(). Remeter esse detalhe para a referência canónica.
Contar Antes de Concluir
table (fish$ tank, fish$ species)
seabass seabream
A 1 2
B 2 1
Um número por célula. Não um número por réplica, a menos que cada linha seja uma réplica.
Juntar por Chave
tanks <- data.frame (
tank = c ("A" , "B" ),
temperature_C = c (18.5 , 21.0 )
)
combined <- merge (fish, tanks, by = "tank" )
select (combined, id, tank, temperature_C)
id tank temperature_C
1 1 A 18.5
2 2 A 18.5
3 3 A 18.5
4 4 B 21.0
5 5 B 21.0
6 6 B 21.0
A chave tank associa cada peixe à temperatura do seu tanque.
Posição Não É Chave
mesma ordem ≠ mesma unidade
Seis linhas antes e depois; uma correspondência por tanque em tanks.
merge() procura os valores da chave; não confia na ordem das linhas. Uma chave duplicada em tanks poderia multiplicar as linhas de peixes. Ter o mesmo número de linhas é uma verificação útil, mas não demonstra que a chave escolhida seja cientificamente adequada.
Escolher Um Ficheiro
No Windows, escolham o CSV na janela. Copiem o caminho devolvido para o script .
Caminhos relativos são interpretados a partir de getwd() .
Exercício: Qualidade da Água
20 minutos · pares · Descarregar o CSV
path <- "C:/.../WaterQualityTesting.csv"
file.exists (path)
water <- read.csv (path)
names (water) <- c ("Sample" , "pH" , "Temp" , "NTU" ,
"Oxygen" , "Conductivity" )
Inspeccionem com dim() , names() e str() .
Adaptar o Workflow
Com water, criem uma nova tabela usando |>:
Conservem os registos com Oxygen > 8.
Criem oxygen_ref_pct = 100 * Oxygen / 8.
Conservem Sample, Oxygen e oxygen_ref_pct.
Verifiquem dimensões e valores. water mudou?
8 mg/L é uma referência didáctica, não saturação nem limiar ecológico.
Solução: water_selected <- water |> filter(Oxygen > 8) |> mutate(oxygen_ref_pct = 100 * Oxygen / 8) |> select(Sample, Oxygen, oxygen_ref_pct). Comparar dim(water) e dim(water_selected); inspeccionar head(water_selected). A tabela nova tem três colunas; as percentagens são superiores a 100. Não interpretar como percentagem de saturação, que exigiria outra informação.
O Que Representa Uma Linha?
Sample identifica um registo. E a unidade ecológica?
Que metadados faltam para reconhecer o local, o momento e eventuais revisitas?
O ficheiro não estabelece local, momento ou protocolo de amostragem. A unidade ecológica e a independência são desconhecidas. Pedir local, data/hora, unidade amostrada e identificação de eventuais revisitas ou grupos; não inventar um protocolo a partir do cabeçalho.
Exercício: Tanques
10 minutos · individual, depois comparem
Contem os peixes por tanque e espécie.
Juntem fish e tanks pela chave tank.
Verifiquem as seis linhas e a temperatura dos peixes 1 e 4.
Se o número de linhas mudar, parem e encontrem a razão.
Quando a Tabela Engana
valores em falta · espécies escritas de duas maneiras · linhas duplicadas
Estes problemas ficam para a limpeza de dados. Hoje basta reconhecê-los.
Guardar Uma Cópia
file.choose (new = TRUE )
output_path <- "C:/.../water_selected.csv"
write.csv (water_selected, output_path, row.names = FALSE )
file.exists (output_path)
No Windows, escolham o destino na consola e copiem o caminho para o script .
file.choose(new = TRUE) permite escolher onde criar o ficheiro no Windows. Substituir o caminho ilustrativo pelo caminho devolvido. Usar um nome novo para conservar o CSV recebido; row.names = FALSE evita exportar os números das linhas como uma coluna extra.
Antes de Continuar
A tabela tem a forma que esperávamos?
Cada linha continua a guardar o mesmo registo?
Os tipos e as unidades estão certos?