Este relatório reúne, em abas, os cinco itens pedidos no exercício: manipulação de dados, tabela interativa, equações em LaTeX, figuras e referências bibliográficas.
Usamos o palmerpenguins (que traz os dados), o
dplyr e o DT para a tabela interativa da aba
2.
O conjunto penguins reúne medidas de pinguins coletadas
entre 2007 e 2009 na Estação Palmer, na Antártida, por Gorman et al. (2014). Cada linha é um pinguim, e
as colunas descrevem a espécie, a ilha onde ele vive, o sexo e medidas
do corpo.
## [1] 344 8
## # A tibble: 6 × 8
## species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
## <fct> <fct> <dbl> <dbl> <int> <int>
## 1 Adelie Torgersen 39.1 18.7 181 3750
## 2 Adelie Torgersen 39.5 17.4 186 3800
## 3 Adelie Torgersen 40.3 18 195 3250
## 4 Adelie Torgersen NA NA NA NA
## 5 Adelie Torgersen 36.7 19.3 193 3450
## 6 Adelie Torgersen 39.3 20.6 190 3650
## # ℹ 2 more variables: sex <fct>, year <int>
O conjunto tem 344 pinguins e 8 variáveis:
| Variável | Significado |
|---|---|
species |
Espécie (Adelie, Chinstrap ou Gentoo) |
island |
Ilha do arquipélago (Biscoe, Dream ou Torgersen) |
bill_length_mm |
Comprimento do bico (mm) |
bill_depth_mm |
Profundidade (altura) do bico (mm) |
flipper_length_mm |
Comprimento da nadadeira (mm) |
body_mass_g |
Massa corporal (g) |
sex |
Sexo (female ou male) |
year |
Ano da coleta |
Vemos a estrutura com str(): species,
island e sex são fatores
(variáveis categóricas) e as medidas são numéricas.
## tibble [344 × 8] (S3: tbl_df/tbl/data.frame)
## $ species : Factor w/ 3 levels "Adelie","Chinstrap",..: 1 1 1 1 1 1 1 1 1 1 ...
## $ island : Factor w/ 3 levels "Biscoe","Dream",..: 3 3 3 3 3 3 3 3 3 3 ...
## $ bill_length_mm : num [1:344] 39.1 39.5 40.3 NA 36.7 39.3 38.9 39.2 34.1 42 ...
## $ bill_depth_mm : num [1:344] 18.7 17.4 18 NA 19.3 20.6 17.8 19.6 18.1 20.2 ...
## $ flipper_length_mm: int [1:344] 181 186 195 NA 193 190 181 195 193 190 ...
## $ body_mass_g : int [1:344] 3750 3800 3250 NA 3450 3650 3625 4675 3475 4250 ...
## $ sex : Factor w/ 2 levels "female","male": 2 1 1 NA 1 2 1 2 NA NA ...
## $ year : int [1:344] 2007 2007 2007 2007 2007 2007 2007 2007 2007 2007 ...
NA)Contamos os NA em cada coluna:
## species island bill_length_mm bill_depth_mm
## 0 0 2 2
## flipper_length_mm body_mass_g sex year
## 2 2 11 0
São 19 valores ausentes no total. Há duas situações:
Como as análises seguintes comparam medidas e sexo, removemos as
linhas com algum NA. Com filter() e
if_all() mantemos só as linhas em que nenhuma coluna está
vazia:
completos <- dados %>%
filter(if_all(everything(), ~ !is.na(.x)))
nrow(dados) - nrow(completos) # linhas removidas## [1] 11
## [1] 0
Como os 2 pinguins sem medidas também não têm sexo, os
NA se concentram em apenas 11 linhas, que
foram removidas (3.2% do total), restando 333 pinguins
sem nenhum NA.
mutate)Agora criamos variáveis novas e traduzimos os rótulos para o português:
especie, ilha e sexo: colunas
categóricas com nomes em portuguêsmassa_kg: massa em quilogramas;razao_bico: comprimento ÷ profundidade do bico. Valores
altos indicam bicos longos e finos; valores baixos, bicos curtos e
altos;porte: classificação da massa em faixas (Pequeno <
3,5 kg ≤ Médio < 4,75 kg ≤ Grande), feita com case_when
e transformada em fator com os níveis na ordem certa;massa_z: massa padronizada dentro da
espécie (escore-z). Ela diz se o pinguim é mais pesado ou mais
leve que a média da sua própria espécie, usando
group_by() antes do mutate().pinguins <- completos %>%
mutate(
especie = species,
ilha = island,
sexo = recode(sex, female = "Fêmea", male = "Macho"),
massa_kg = body_mass_g / 1000,
razao_bico = round(bill_length_mm / bill_depth_mm, 2),
porte = factor(case_when(
massa_kg < 3.5 ~ "Pequeno",
massa_kg < 4.75 ~ "Médio",
TRUE ~ "Grande"),
levels = c("Pequeno", "Médio", "Grande")
)
) %>%
group_by(especie) %>%
mutate(massa_z = round((massa_kg - mean(massa_kg)) / sd(massa_kg), 2)) %>%
ungroup() %>%
select(especie, ilha, sexo, ano = year,
bico_comp = bill_length_mm, bico_prof = bill_depth_mm,
nadadeira = flipper_length_mm, massa_kg, razao_bico, porte, massa_z)
head(pinguins)## # A tibble: 6 × 11
## especie ilha sexo ano bico_comp bico_prof nadadeira massa_kg razao_bico
## <fct> <fct> <fct> <int> <dbl> <dbl> <int> <dbl> <dbl>
## 1 Adelie Torgers… Macho 2007 39.1 18.7 181 3.75 2.09
## 2 Adelie Torgers… Fêmea 2007 39.5 17.4 186 3.8 2.27
## 3 Adelie Torgers… Fêmea 2007 40.3 18 195 3.25 2.24
## 4 Adelie Torgers… Fêmea 2007 36.7 19.3 193 3.45 1.9
## 5 Adelie Torgers… Macho 2007 39.3 20.6 190 3.65 1.91
## 6 Adelie Torgers… Fêmea 2007 38.9 17.8 181 3.62 2.19
## # ℹ 2 more variables: porte <fct>, massa_z <dbl>
A nova tabela pinguins tem 11 colunas com nomes em
português.
Veja como a razao_bico separa as espécies. A média por
espécie é:
## # A tibble: 3 × 2
## especie razao_media
## <fct> <dbl>
## 1 Adelie 2.12
## 2 Chinstrap 2.65
## 3 Gentoo 3.18
Os Gentoo têm a maior razão (3.18), ou seja, bicos longos e finos, enquanto os Adelie têm a menor (2.12), com bicos curtos e altos. Uma única variável criada já diferencia bem as espécies.
filter)Filtro 1: fêmeas mais pesadas que a média da sua espécie (massa padronizada maior que 0). Comparamos com o mesmo filtro para os machos:
femeas_acima <- pinguins %>%
filter(sexo == "Fêmea", massa_z > 0)
machos_acima <- pinguins %>%
filter(sexo == "Macho", massa_z > 0)
femeas_acima %>% count(especie)## # A tibble: 3 × 2
## especie n
## <fct> <int>
## 1 Adelie 8
## 2 Chinstrap 6
## 3 Gentoo 4
## [1] 18
## [1] 140
Só 18 fêmeas ficam acima da média da sua espécie, contra 140 machos. Como a média de cada espécie mistura os dois sexos, quase só os machos ficam acima dela, o que já indica que eles são mais pesados.
Filtro 2: pinguins Adelie das ilhas Dream ou
Torgersen com nadadeira de pelo menos 195 mm, usando o operador
%in% para testar várias ilhas de uma vez:
adelie_nadadeira <- pinguins %>%
filter(especie == "Adelie",
ilha %in% c("Dream", "Torgersen"),
nadadeira >= 195)
adelie_nadadeira %>% count(ilha)## # A tibble: 2 × 2
## ilha n
## <fct> <int>
## 1 Dream 12
## 2 Torgersen 18
Dos 102 Adelie que vivem nessas duas ilhas, 30 (29.4%) têm nadadeira de 195 mm ou mais.
arrange)Os 5 pinguins mais pesados do conjunto:
pinguins %>%
arrange(desc(massa_kg)) %>%
select(especie, sexo, ilha, nadadeira, massa_kg) %>%
head(5)## # A tibble: 5 × 5
## especie sexo ilha nadadeira massa_kg
## <fct> <fct> <fct> <int> <dbl>
## 1 Gentoo Macho Biscoe 221 6.3
## 2 Gentoo Macho Biscoe 230 6.05
## 3 Gentoo Macho Biscoe 220 6
## 4 Gentoo Macho Biscoe 222 6
## 5 Gentoo Macho Biscoe 223 5.95
Ordenação por mais de uma coluna: dentro de cada
espécie (ordem alfabética), o pinguim com a maior
nadadeira. Para isso, ordenamos e pegamos a primeira linha de
cada grupo com slice(1):
maior_nadadeira <- pinguins %>%
arrange(especie, desc(nadadeira)) %>%
group_by(especie) %>%
slice(1) %>%
ungroup() %>%
select(especie, sexo, nadadeira, massa_kg)
maior_nadadeira## # A tibble: 3 × 4
## especie sexo nadadeira massa_kg
## <fct> <fct> <int> <dbl>
## 1 Adelie Macho 210 4
## 2 Chinstrap Macho 212 4.3
## 3 Gentoo Macho 231 5.65
Os cinco mais pesados são todos Gentoo, e o maior valor de nadadeira entre todas as espécies é 231 mm, de um pinguim Gentoo.
group_by +
summarise)Primeiro, onde vive cada espécie, com
count():
## # A tibble: 5 × 3
## ilha especie n
## <fct> <fct> <int>
## 1 Biscoe Adelie 44
## 2 Biscoe Gentoo 119
## 3 Dream Adelie 55
## 4 Dream Chinstrap 68
## 5 Torgersen Adelie 47
A tabela mostra que só os Adelie aparecem nas três ilhas; os Chinstrap só vivem em Dream e os Gentoo só em Biscoe.
Agora o resumo das medidas por espécie e sexo:
resumo <- pinguins %>%
group_by(especie, sexo) %>%
summarise(
n = n(),
massa_media = round(mean(massa_kg), 2),
massa_dp = round(sd(massa_kg), 2),
nadadeira_media = round(mean(nadadeira), 1),
bico_comp_medio = round(mean(bico_comp), 1),
.groups = "drop"
) %>%
arrange(desc(massa_media))
resumo## # A tibble: 6 × 7
## especie sexo n massa_media massa_dp nadadeira_media bico_comp_medio
## <fct> <fct> <int> <dbl> <dbl> <dbl> <dbl>
## 1 Gentoo Macho 61 5.48 0.31 222. 49.5
## 2 Gentoo Fêmea 58 4.68 0.28 213. 45.6
## 3 Adelie Macho 73 4.04 0.35 192. 40.4
## 4 Chinstrap Macho 34 3.94 0.36 200. 51.1
## 5 Chinstrap Fêmea 34 3.53 0.29 192. 46.6
## 6 Adelie Fêmea 73 3.37 0.27 188. 37.3
Por fim, calculamos o dimorfismo sexual (quanto os machos são mais pesados que as fêmeas, em %) em cada espécie:
dimorfismo <- pinguins %>%
group_by(especie) %>%
summarise(
massa_femea = mean(massa_kg[sexo == "Fêmea"]),
massa_macho = mean(massa_kg[sexo == "Macho"])
) %>%
mutate(diferenca_pct = round(100 * (massa_macho - massa_femea) / massa_femea, 1)) %>%
arrange(desc(diferenca_pct))
dimorfismo## # A tibble: 3 × 4
## especie massa_femea massa_macho diferenca_pct
## <fct> <dbl> <dbl> <dbl>
## 1 Adelie 3.37 4.04 20
## 2 Gentoo 4.68 5.48 17.2
## 3 Chinstrap 3.53 3.94 11.7
Interpretação dos resultados:
A diferença entre machos e fêmeas é real ou pode ser acaso? Usamos o teste t de Welch (equação 5 da aba de equações) nos Gentoo:
gentoo <- pinguins %>% filter(especie == "Gentoo")
teste <- t.test(massa_kg ~ sexo, data = gentoo)
teste##
## Welch Two Sample t-test
##
## data: massa_kg by sexo
## t = -14.761, df = 116.64, p-value < 2.2e-16
## alternative hypothesis: true difference in means between group Fêmea and group Macho is not equal to 0
## 95 percent confidence interval:
## -0.9131130 -0.6970763
## sample estimates:
## mean in group Fêmea mean in group Macho
## 4.679741 5.484836
O p-valor é <2e-16, muito menor que 0,05. Então a diferença de massa entre machos e fêmeas Gentoo é estatisticamente significativa: não é fruto do acaso.
Para fechar, a correlação de Pearson entre o tamanho da nadadeira e a massa:
## [1] 0.873
O valor r = 0.873 indica uma correlação positiva e muito forte: pinguins com nadadeiras maiores tendem a ser mais pesados.
A tabela abaixo foi feita com a função datatable() do
pacote DT e mostra o conjunto pinguins
criado na aba 1. Com ela é possível:
datatable(
pinguins,
rownames = FALSE,
filter = "top",
colnames = c("Espécie", "Ilha", "Sexo", "Ano", "Bico comp. (mm)",
"Bico prof. (mm)", "Nadadeira (mm)", "Massa (kg)",
"Razão do bico", "Porte", "Massa padronizada (z)"),
caption = "Tabela 1: Pinguins do arquipélago Palmer após a manipulação.",
options = list(
scrollX = TRUE,
pageLength = 10,
lengthMenu = c(10, 25, 50, 100),
order = list(list(7, "desc")),
language = list(url = "https://cdn.datatables.net/plug-ins/1.10.11/i18n/Portuguese-Brasil.json")
)
) %>%
formatStyle(
"porte",
backgroundColor = styleEqual(
c("Pequeno", "Médio", "Grande"),
c("#D6EAF8", "#FFF3CD", "#F5CBA7")
)
) %>%
formatStyle(
"massa_z",
color = styleInterval(c(-1, 1), c("#C0392B", "black", "#1E8449")),
fontWeight = styleInterval(c(-1, 1), c("bold", "normal", "bold"))
)A tabela abre ordenada pela massa (do maior para o menor). A coluna Porte é colorida por faixa, e na Massa padronizada aparecem em verde os pinguins bem mais pesados que a média da espécie (z > 1) e em vermelho os bem mais leves (z < −1).
As cinco equações abaixo foram escritas com a sintaxe do LaTeX.
\[ H(S) = -\sum_{c=1}^{C} p_c \log_2 p_c, \qquad IG(S, A) = H(S) - \sum_{v \in \text{valores}(A)} \frac{\lvert S_v \rvert}{\lvert S \rvert}\, H(S_v) \]
Significado: a entropia \(H(S)\) mede o quanto um conjunto \(S\) está “misturado”: vale \(0\) quando todos os exemplos são da mesma classe e é máxima quando as \(C\) classes aparecem na mesma proporção (\(p_c\) é a proporção da classe \(c\)). O ganho de informação \(IG(S, A)\) mede quanto essa mistura cai ao dividir \(S\) pelo atributo \(A\) em subconjuntos \(S_v\). A árvore de decisão escolhe, a cada nó, o atributo com maior ganho.
\[ G(S) = 1 - \sum_{c=1}^{C} p_c^{2}, \qquad \hat{y}(\mathbf{x}) = \underset{c}{\arg\max} \sum_{b=1}^{B} \mathbb{1}\big(T_b(\mathbf{x}) = c\big) \]
Significado: o índice de Gini \(G(S)\) é outra medida de impureza de um nó,
usada por padrão no randomForest do R: é a probabilidade de
classificar errado um exemplo sorteado ao acaso se o rótulo também fosse
sorteado pelas proporções \(p_c\). A
segunda parte é a previsão da Random Forest: cada uma
das \(B\) árvores \(T_b\), treinadas em amostras
bootstrap diferentes, dá um voto, e a classe final \(\hat{y}\) é a mais votada (\(\mathbb{1}(\cdot)\) vale 1 quando a árvore
vota em \(c\) e 0 caso contrário).
\[ P(y = 1 \mid \mathbf{x}) = \sigma(\mathbf{w}^\top \mathbf{x} + b) = \frac{1}{1 + e^{-(\mathbf{w}^\top \mathbf{x} + b)}}, \qquad J(\mathbf{w}, b) = -\frac{1}{n} \sum_{i=1}^{n} \Big[ y_i \log \hat{p}_i + (1 - y_i) \log (1 - \hat{p}_i) \Big] \]
Significado: a função sigmoide \(\sigma\) transforma uma combinação linear das variáveis \(\mathbf{x}\) (com pesos \(\mathbf{w}\) e intercepto \(b\)) em uma probabilidade entre 0 e 1. Por isso a regressão logística serve para classificação binária (por exemplo, prever se um pinguim é macho ou fêmea pelas medidas). A função de custo \(J\), a entropia cruzada, penaliza muito quando o modelo dá alta probabilidade para a classe errada; o treino busca os \(\mathbf{w}\) e \(b\) que a minimizam.
\[ \text{EQM}(\boldsymbol{\theta}) = \frac{1}{n}\sum_{i=1}^{n}\left(y_i - \boldsymbol{\theta}^\top \mathbf{x}_i\right)^2, \qquad \boldsymbol{\theta}^{(t+1)} = \boldsymbol{\theta}^{(t)} - \eta \, \nabla_{\boldsymbol{\theta}} \text{EQM} = \boldsymbol{\theta}^{(t)} + \frac{2\eta}{n} \sum_{i=1}^{n} \left(y_i - \boldsymbol{\theta}^{(t)\top} \mathbf{x}_i\right)\mathbf{x}_i \]
Significado: o erro quadrático médio (EQM) mede o quanto as previsões \(\boldsymbol{\theta}^\top \mathbf{x}_i\) se afastam dos valores reais \(y_i\). O gradiente descendente encontra os parâmetros \(\boldsymbol{\theta}\) que minimizam esse erro aos poucos: a cada iteração \(t\), dá um passo de tamanho \(\eta\) (a taxa de aprendizado) na direção contrária ao gradiente, ou seja, “descendo a ladeira” da função de erro. É o algoritmo por trás do treino da maioria dos modelos de machine learning.
\[ t = \frac{\bar{x}_1 - \bar{x}_2}{\sqrt{\dfrac{s_1^2}{n_1} + \dfrac{s_2^2}{n_2}}}, \qquad \nu \approx \frac{\left( \dfrac{s_1^2}{n_1} + \dfrac{s_2^2}{n_2} \right)^2}{\dfrac{\left(s_1^2 / n_1\right)^2}{n_1 - 1} + \dfrac{\left(s_2^2 / n_2\right)^2}{n_2 - 1}} \]
Significado: o teste t de Welch verifica se as
médias de dois grupos (\(\bar{x}_1\) e
\(\bar{x}_2\)) são diferentes de
verdade ou se a diferença pode ser só acaso. Ele divide a diferença das
médias pelo seu erro padrão, usando as variâncias \(s^2\) e os tamanhos \(n\) de cada grupo, sem supor que as
variâncias sejam iguais. Os graus de liberdade \(\nu\) (fórmula de Welch–Satterthwaite)
definem a distribuição t usada para calcular o p-valor. É o teste que o
t.test() do R faz por padrão, e foi usado na aba 1 para
comparar a massa de machos e fêmeas.
A figura mostra as etapas de um projeto de ciência de dados segundo Wickham et al. (2023): importar os dados, arrumá-los (tidy) e entrar no ciclo de entendimento, onde se transforma, visualiza e modela repetidamente até chegar a uma conclusão. A última etapa é comunicar os resultados, que é justamente o papel de relatórios em R Markdown (Xie et al. 2018) e do storytelling com dados (Knaflic 2015).
O diagrama mostra a ciência de dados como a interseção de três áreas: computação, matemática e estatística e conhecimento do domínio. Juntar só computação e estatística dá machine learning (James et al. 2021); juntar computação e domínio sem base estatística é a “zona de perigo”, onde se tiram conclusões erradas com facilidade.
As referências abaixo foram gerenciadas com BibTeX, a partir do
arquivo referencias.bib indicado no campo
bibliography do cabeçalho. Elas foram citadas ao longo do
relatório com a sintaxe [@chave].