Neste segundo tópico de análise bivariada, passamos a cruzar uma variável qualitativa com uma quantitativa. Para fazer isso, foi visto que tabelas e gráficos ainda se mantêm como principais aliados no auxílio a interpretação dos dados — ao menos, ainda, no contexto de Análise Exploratória de Dados. Porém, para este tópico, a tabela passa a ser de medidas descritivas, e os gráficos passam a ter um ponto de interpretação a mais: a interseção entre grupos.
Veja, abaixo, 2 exemplos deste tipo de análise na prática.
1. Em uma granja pequena, a pedido do dono, uma equipe ficou responsável por fazer um estudo com 50 pintinhos. Esse estudo consisitia em analisar o efeito de 4 tipos de dietas diferentes, nesses pintinhos, durante um certo período. Para isso, cada pintinho recebeu uma identificação e teve seu peso medido a cada 2 dias, até completar 20 dias de estudo, sendo realizada, no 21º dia, uma última medição.
A base ChickWeight, presente na [linguagem] R, armazena os dados deste estudo. Vamos incorporar o papel de um dos integrantes da equipe montada, e analisar essa base, de modo que se responda a seguinte pergunta: há diferença no peso dos pintinhos quanto aos 4 tipos de dieta adotadas ao final desse período de 3 semanas, ou seja, aos 21 dias?
2. A base de dados anexada (weekly_fuel_price_italy) é composta por preços de 6 tipos de combustíveis diferentes, captados pelo governo italiano entre março de 2005 e agosto de 2021. Faremos as análises pertinentes para descobrir a seguinte coisa: há diferença entre os preços dos combustíveis do período de 06/01/2014 (inclusive) a a 02/01/2017 (exclusive)?
Primeiro, é preciso separar o trecho da base que nos interessa, ou seja, o trecho do estudo que armazena os dados do 21º dia. Mas antes, vejamos a base completa:
ChickWeight
Agora, vamos tomar do dataframe original apenas os dados referentes ao 21º dia de estudo e as variáveis de interesse, com auxílio da função subset, e visualizar isso.
db1 <- subset(ChickWeight, Time == 21)
db1
Nosso segundo passo consiste em separar as colunas que queremos trabalhar, ou seja, a coluna dos pesos e a coluna dos tipos de dieta.
peso <- db1$weight
dieta <- db1$Diet
Agora, é necessário construir uma tabela, mas não uma tabela qualquer. Precisamos de uma tabela-resumo de medidas descritivas da variável quantitativa em questão. Para isso, iremos aplicar a função tapply e a função cbind. Veja:
n <- tapply(peso, as.factor(dieta), FUN = length) # Tamanho de cada grupo
min <- tapply(peso, as.factor(dieta), FUN = min) # Mínimos dos pesos segundo tipo de dieta.
max <- tapply(peso, as.factor(dieta), FUN = max) # Máximos dos pesos segundo tipo de dieta.
q1 <- round(tapply(peso, as.factor(dieta), FUN = quantile, probs = .25), 2) # 1º quartil dos pesos segundo tipo de dieta.
q2 <- round(tapply(peso, as.factor(dieta), FUN = median), 2) # Mediana dos pesos segundo tipo de dieta.
q3 <- round(tapply(peso, as.factor(dieta), FUN = quantile, probs = .75), 2) # 3º quartil dos pesos segundo tipo de dieta.
media <- round(tapply(peso, as.factor(dieta), FUN = mean), 2) # Média aritmética dos pesos segundo tipo de dieta.
sd <- round(tapply(peso, as.factor(dieta), FUN = sd), 2) # Desvio padrão dos pesos segunto tipo de dieta.
tp_dieta <- c(1, 2, 3, 4) # Vetor para identificação do tipo de dieta
resumo_variaveis <- cbind(tp_dieta, n, min, max, q1, q2, q3, media, sd) # Matriz com as medidas "concatenadas".
A única coisa que fica faltando entrar na tabela é o coeficiente de assimetria. Para encontrá-lo cada devemos, primeiro, encontrar os quartis e a mediana das variáveis de peso associada a cada variável qualitativa. Para isso, separamos a base de dados em subsets pelo tipo de dieta. Ou seja:
d1 <- subset(db1, Diet == 1) # Subset das variáveis que possuem dieta 1
d2 <- subset(db1, Diet == 2) # Subset das variáveis que possuem dieta 2
d3 <- subset(db1, Diet == 3) # Subset das variáveis que possuem dieta 3
d4 <- subset(db1, Diet == 4) # Subset das variáveis que possuem dieta 4
Vamos encontrar para cada subset os quartis, a mediana e, por fim, calculamos o Coeficiente de Bowley:
# ---- Dieta 1 ----
q1_d1 <- quantile(d1$weight, 0.25) # Primeiro quartil dos pesos da dieta 1
md_d1 <- median(d1$weight) # Mediana dos pesos da dieta 1
q3_d1 <- quantile(d1$weight, 0.75) # Terceiro quartil dos pesos da dieta 1
a1 <- as.numeric(((q3_d1 - md_d1) - (md_d1 - q1_d1))/(q3_d1 - q1_d1)) # Calculando coeficiente de assimetria de Bowley e passando para um valor numérico
a1 <- round(a1, 2) # arredondando o valor encontrado para duas casas decimais
# Agora replicamos o código acima para as outras variáveis
# ---- Dieta 2 ----
q1_d2 <- quantile(d2$weight, 0.25) # Primeiro quartil dos pesos da dieta 2
md_d2 <- median(d2$weight) # Mediana dos pesos da dieta 2
q3_d2 <- quantile(d2$weight, 0.75) # Terceiro quartil dos pesos da dieta 2
a2 <- as.numeric(((q3_d2 - md_d2) - (md_d2 - q1_d2))/(q3_d2 - q1_d2))
a2 <- round(a2, 2)
# ---- Dieta 3 ----
q1_d3 <- quantile(d3$weight, 0.25) # Primeiro quartil dos pesos da dieta 3
md_d3 <- median(d3$weight) # Mediana dos pesos da dieta 3
q3_d3 <- quantile(d3$weight, 0.75) # Terceiro quartil dos pesos da dieta 3
a3 <- as.numeric(((q3_d3 - md_d3) - (md_d3 - q1_d3))/(q3_d3 - q1_d3))
a3 <- round(a3, 2)
# ---- Dieta 4 ----
q1_d4 <- quantile(d4$weight, 0.25) # Primeiro quartil dos pesos da dieta 4
md_d4 <- median(d4$weight) # Mediana dos pesos da dieta 4
q3_d4 <- quantile(d4$weight, 0.75) # Terceiro quartil dos pesos da dieta 4
a4 <- as.numeric(((q3_d4 - md_d4) - (md_d4 - q1_d4))/(q3_d4 - q1_d4))
a4 <- round(a4, 2)
Agora, colocamos tudo em um vetor único e integramos à tabela de resumo das estatísticas descritivas utlizando a função cbind.
assimetria <- c(a1, a2, a3, a4) # Combinando as 4 assimetrias na ordem
resumo_variaveis <- cbind(resumo_variaveis, assimetria) # Juntando coluna à matriz existente
| tp_dieta | n | min | max | q1 | q2 | q3 | media | sd | assimetria |
|---|---|---|---|---|---|---|---|---|---|
| 1 | 16 | 96 | 305 | 137.5 | 166.0 | 207.5 | 177.75 | 58.70 | 0.19 |
| 2 | 10 | 74 | 331 | 169.0 | 212.5 | 261.5 | 214.70 | 78.14 | 0.06 |
| 3 | 10 | 147 | 373 | 229.0 | 281.0 | 317.0 | 270.30 | 71.62 | -0.18 |
| 4 | 9 | 196 | 322 | 204.0 | 237.0 | 264.0 | 238.56 | 43.35 | -0.10 |
Observando a tabela acima, avaliando as medidas, é possível enxergar que, de maneira geral, ambos os tipos de dieta apresentam leves assimetrias. A dieta 2 (2ª linha) entregou uma maior variabilidade de pesos, indicando não ser a melhor entre as 4, e uma distribuição quase simétrica. Enquanto isso, a dieta 4 (4ª linha), aparenta ter sido a que trouxe resultados positivos mais consistentes (menor variabilidade, com valor de mínimo mais alto, indicando um ganho de peso relativamente bom), com uma leve assimetria à esquerda. Porém, observando os valores de máximo e mínimo, é possível notar interseções entre os grupos (dietas). Portanto, ainda que, aparentemente, tenha havido diferença entre um dieta ou outra, não se pode concluir que exista diferença de fato.
Veja melhor nossa conclusão com auxílio dos gráficos abaixo. Primeiro, um boxplot.
par(mar = c(6, 6, 5, 4))
boxplot(
peso ~ dieta,
col = c("#ff9430"),
ylim = c(50, 400),
xlab = "Tipo de Dieta",
ylab = "Peso",
main = "Peso final, em gramas, por dieta, após 21 dias",
cex.main = 1.1)
Agora, um gráfico de barras de erro.
plot(
1:4,
media,
xaxt = 'n',
ylim = range(c(media - 1.3*sd, media + 1.3*sd)), # Multiplicou-se o desvio adrão por 1.3 com o intuito de aumentar o limite vertical do gráfico, e marcar no eixo Y os pontos necessários.
pch = 19,
xlab = "Tipos de Dieta",
ylab = "Peso",
main = "Peso final, em gramas, por dieta, após 21 dias",
cex.main = 1.1)
axis(side = 1, at = c(1,2,3,4), labels = c(1,2,3,4))
arrows(1:4, media - sd, 1:4, media + sd, length = 0.05, angle = 90, code = 3)
Através dos gráficos acima, ficam mais clara as interseções entre os grupos — eles passam pela mesma região no eixo vertical — e, portanto, fica claro, também, como dito na análise da tabela, que não há diferença entre as dietas, em termos de resultado1.
O primeiro passo para resolver esta questão, é importar a base de dados que será trabalhada, e visualizá-la.
db2 <- read.csv("weekly_fuel_price_italy.csv") # Lembrem-se de que o método e caminho de importação de base de dados é relativo a quem produz ou lê o código. Portanto, a menos que essa base esteja em seu diretório de trabalho, este código não irá rodar do modo que foi posto.
db2
Vamos fazer mais um movimento prévio nesta base. Notem que os dados são sequenciais, seguindo uma linha de tempo. Portanto, teremos de extrair da base apenas o trecho que corresponde ao período estipulado, além de manter somente as variáveis que queremos trabalhar.
db3_aux1 <- subset(db2, SURVEY_DATE >= "2014-01-06") # Extraindo da base o subconjunto que corresponde a todas as entradas a partir do limite inferior do período desejado.
db3_aux2 <- subset(db3_aux1, SURVEY_DATE < "2017-01-02") # Extraindo do subconjunto da base outro subconjunto, que corresponde às entradas antes do limite superior do período desejado.
db3 <- db3_aux2[ ,c(3,4)] # Mantendo apenas as colunas que serão trabalhadas.
Façamos uma pequena tabela-resumo de algumas estatísticas descritivas.
preços <- db3$PRICE
combustiveis <- db3$PRODUCT_NAME
n <- tapply(preços, as.factor(combustiveis), FUN = length)
min <- tapply(preços, as.factor(combustiveis), FUN = min)
max <- tapply(preços, as.factor(combustiveis), FUN = max)
q1 <- round(tapply(preços, as.factor(combustiveis), FUN = quantile, probs = .25), 2)
q2 <- round(tapply(preços, as.factor(combustiveis), FUN = median), 2)
q3 <- round(tapply(preços, as.factor(combustiveis), FUN = quantile, probs = .75), 2)
média <- round(tapply(preços, as.factor(combustiveis), FUN = mean), 2)
sd <- round(tapply(preços, as.factor(combustiveis), FUN = sd), 2)
resumo_variaveis_2 <- cbind(n, min, max, média, q1, q2, q3, sd)
Aqui, assim como no primeiro exercício, devemos calcular a assimetria dos preços de combustíveis associados ao tipo de combustível. Portanto, vamos fazer igual foi feito no primeiro exemplo:
base_1 <- subset(db3, PRODUCT_NAME == "Euro-Super 95") # Subset dos combustíveis do tipo Euro-Super 95.
base_2 <- subset(db3, PRODUCT_NAME == "Automotive gas oil") # Subset dos combustíveis do tipo Automotive gas oil.
base_3 <- subset(db3, PRODUCT_NAME == "Heating gas oil") # Subset dos combustíveis do tipo Heating gas oil.
base_4 <- subset(db3, PRODUCT_NAME == "LPG") # Subset dos combustíveis do tipo LPG.
base_5 <- subset(db3, PRODUCT_NAME == "Residual fuel oil") # Subset dos combustíveis do tipo Residual fuel oil.
base_6 <- subset(db3, PRODUCT_NAME == "Heavy fuel oil") # Subset dos combustíveis do tipo Heavy fuel oil.
Para cada base acima, iremos calcular os quartis, a mediana e, por fim, o Coeficiente de Assimetria de Bowley.
# ---- Base 1 ----
q1_base1 <- quantile(base_1$PRICE, 0.25) # Primeiro quartil dos preços.
md_base1 <- median(base_1$PRICE) # Mediana dos preços.
q3_base1 <- quantile(base_1$PRICE, 0.75) # Terceiro quartil dos preços.
a1_2 <- as.numeric(((q3_base1 - md_base1) - (md_base1 - q1_base1)) / (q3_base1 - q1_base1)) # Calculando Coeficiente de Assimetria de Bowley e passando para um valor numérico.
a1_2 <- round(a1_2, 2) # Arredondando o valor encontrado para duas casas decimais.
# Agora, replicamos o código acima para as outras variáveis.
# ---- Base 2 ----
q1_base2 <- quantile(base_2$PRICE, 0.25)
md_base2 <- median(base_2$PRICE)
q3_base2 <- quantile(base_2$PRICE, 0.75)
a2_2 <- as.numeric(((q3_base2 - md_base2) - (md_base2 - q1_base2)) / (q3_base2 - q1_base2))
a2_2 <- round(a2_2, 2)
# ---- Base 3 ----
q1_base3 <- quantile(base_3$PRICE, 0.25)
md_base3 <- median(base_3$PRICE)
q3_base3 <- quantile(base_3$PRICE, 0.75)
a3_2 <- as.numeric(((q3_base3 - md_base3) - (md_base3 - q1_base3)) / (q3_base3 - q1_base3))
a3_2 <- round(a3_2, 2)
# ---- Base 4 ----
q1_base4 <- quantile(base_4$PRICE, 0.25)
md_base4 <- median(base_4$PRICE)
q3_base4 <- quantile(base_4$PRICE, 0.75)
a4_2 <- as.numeric(((q3_base4 - md_base4) - (md_base4 - q1_base4)) / (q3_base4 - q1_base4))
a4_2 <- round(a4_2, 2)
# ---- Base 5 ----
q1_base5 <- quantile(base_5$PRICE, 0.25)
md_base5 <- median(base_5$PRICE)
q3_base5 <- quantile(base_5$PRICE, 0.75)
a5_2 <- as.numeric(((q3_base5 - md_base5) - (md_base5 - q1_base5)) / (q3_base5 - q1_base5))
a5_2 <- round(a5_2, 2)
# ---- Base 6 ----
q1_base6 <- quantile(base_6$PRICE, 0.25)
md_base6 <- median(base_6$PRICE)
q3_base6 <- quantile(base_6$PRICE, 0.75)
a6_2 <- as.numeric(((q3_base6 - md_base6) - (md_base6 - q1_base6)) / (q3_base6 - q1_base6))
a6_2 <- round(a6_2, 2)
Agora, finalmente, armazenaremos todos os valores encontrados em uma variável, para, posteriormente, acrescentá-la como uma nova coluna da tabela de estatísticas descritivas utilizando a função cbind.
assimetria <- c(a1_2, a2_2, a3_2, a4_2, a5_2, a6_2) # Juntando todos os coeficientes em uma variável.
resumo_variaveis_2 <- cbind(resumo_variaveis_2, assimetria) #Atualizando a tabela existente com a nova coluna.
| n | min | max | média | q1 | q2 | q3 | sd | assimetria | |
|---|---|---|---|---|---|---|---|---|---|
| Automotive gas oil | 147 | 1186.14 | 1656.65 | 1433.88 | 1316.44 | 1417.22 | 1609.52 | 145.33 | 0.33 |
| Euro-Super 95 | 147 | 1361.22 | 1763.22 | 1565.53 | 1457.14 | 1542.91 | 1713.28 | 124.45 | 0.31 |
| Heating gas oil | 147 | 1000.36 | 1419.39 | 1223.81 | 1124.70 | 1200.98 | 1384.47 | 128.35 | 0.41 |
| Heavy fuel oil | 147 | 216.74 | 589.63 | 400.05 | 312.28 | 381.10 | 537.20 | 113.63 | 0.70 |
| LPG | 147 | 539.97 | 888.33 | 649.09 | 577.58 | 604.30 | 754.31 | 93.28 | 0.54 |
| Residual fuel oil | 147 | 667.12 | 990.06 | 829.80 | 754.10 | 801.11 | 957.39 | 98.66 | 0.39 |
Agora, partiremos para o gráfico; e o escolhido é um boxplot.
par(xaxt = "n")
p3 <- boxplot(
preços ~ combustiveis,
col = c("#ffffcc", "#c7e9b4", "#7fcdbb", "#41b6c4", "#2c7fb8", "#253494"),
ylim = c(0, 2000),
xlab = "Tipos de Combustíveis",
ylab = "Preços",
main = "Preço dos combustíveis na Itália no período de 06/01/2014 até 02/01/2017",
cex.main = 1.05
)
legend(
"topright",
legend = c("Automotive gas oil", "Euro-Super 95", "Heating gas oil", "Heavy fuel oil", "LPG", "Residual fuel oil"),
fill = c("#ffffcc", "#c7e9b4", "#7fcdbb", "#41b6c4", "#2c7fb8", "#253494"),
cex = 0.8
)
Ao analisar gráfico e tabela, é possível perceber que ambos os grupos (combustíveis) apresentam uma assimetria positiva forte2. Há uma separação clara entre o 4º grupo (Heavy fuel oil) e os 3 primeiros, e também do 4º com o 6º combustível (Residual fuel oil). Há, também, uma certa semelhança na distribuição dos 3 primeiros grupos e na distribuição dos valores do 5º e 6º grupos.
Notem que podemos perceber que os 3 primeiros grupos (tipos de combustíveis) formam um grande bloco de combustíveis e que os 3 últimos grupos formam outro grande bloco. Podemos afirmar que há diferença entre o primeiro grande bloco e o segundo. Isso fica mais claro ao observar as informações das linhas 3 e 6 da tabela, que correspondem ao último grupo de cada grande bloco: não há sobreposição entre as cercas internas. Na verdade, não há sobreposição entre o mínimo do primeiro bloco e o máximo do segundo bloco.