Documento disponível em: https://rpubs.com/JoaoLRPT/carseats_eda
1. Indicação da lista de bibliotecas usadas no início do trabalho e a justificativa para uso
# Lista de pacotes usados:
library(ISLR) # contem o dataset CarSeats, utilizado no trabalho
library(data.table) # contem o formato das tabelas, utilizado no trabalho
library(psych) # para fazer o teste de correlação
library(ggplot2) # para o mapa de calor (heatmap), scatterplots, histogramas e gráfico de barras.
library(tidyverse) # para o histograma sobreposto
library(grid) # para o histograma sobreposto
library(gridExtra) # para o histograma sobreposto
Justificativa de uso:
ISLR: por conter o dataset utilizado aqui;
data.table: tenho grande familiaridade com essa forma
de armazenar tabelas. Em meu trabalho, em geral, utilizávamos dplyr.
Porém, constatamos certa lentidão em alguns projetos, ao passo que
estávamos trabalhando com um conjunto de dados muito grande. Se tornou
hábito limpar a memória, com rm() e gc(), ao longo do script, para não
haver estouro de memória. Pesquisando, verifiquei que o data.table era
até 10x mais rápido do que as outras opções, como dplyr, sqldf e o base
do R;
psych: biblioteca conhecida pelo corr.test, que realiza
o teste de correlação, determinando quais colunas apresentam correlação
significativa;
ggplot2: biblioteca conhecida por sua variedade de
gráficos. Sua function ggplot() foi utilizada na construção dos
seguintes gráficos: heatmap (mapa de calor), scatterplots (gráfico de
pontos), histogramas e barplots (gráfico de barras).
As seguintes bibliotecas foram utilizadas para facilitar a construção
dos histogramas sobrepostos:
tidyverse: é importante o analista de dados sempre se
atentar aos conflitos entre pacotes, quando existem métodos com mesmas
nomenclatura (homônimos), conforme warning após o library(tidyvere).
Note uma série de métodos homônimos entre o tidyverse versus ggplot2,
dplyr, lubridate e purr.
grid.
gridExtra.
2. Indicação da fonte dos dados
# Informacoes do dataset usado
?Carseats
No code chunk acima, podemos verificar que se trata de um conjunto de
dados simulados contendo vendas de assentos infantis de carro em 400
diferentes lojas.
3. Apresentar o metadado (dicionário de dados) do dataset
No ?Carseats, acima, conseguimos extrair, dentre outras informações,
os seguintes metadados:
Temos uma tabela de dados contendo 400 observações e as seguintes 11
variáveis:
Sales: Vendas unitárias (em milhares) da loja;
CompPrice: Preço cobrado pelo concorrente de cada
loja;
Income: Nível de renda da comunidade (em milhares de
dólares);
Advertising: Orçamento de publicidade de cada loja (em
milhares de dólares);
Population: População na região (em milhares);
Price: Preço que a empresa cobra pelos assentos
infantis de carro;
ShelveLoc: Um fator com níveis Ruim, Bom e Médio que
indica a qualidade da localização das estantes das cadeirinhas em cada
loja;
Age: Idade média da população local;
Education: Nível de educação de cada localidade;
Urban: Um fator com níveis Não e Sim para indicar se a
loja está localizada em uma localidade urbana (Yes) ou rural (no);
US: Um fator com níveis Não e Sim para indicar se a
loja está nos USA (Yes) ou não (No).
4. Escrever o código para a importação do dataset
# Carregar o dataset
data("Carseats")
setDT(Carseats)
# Estrutura do banco de dados
str(Carseats)
Classes ‘data.table’ and 'data.frame': 400 obs. of 11 variables:
$ Sales : num 9.5 11.22 10.06 7.4 4.15 ...
$ CompPrice : num 138 111 113 117 141 124 115 136 132 132 ...
$ Income : num 73 48 35 100 64 113 105 81 110 113 ...
$ Advertising: num 11 16 10 4 3 13 0 15 0 0 ...
$ Population : num 276 260 269 466 340 501 45 425 108 131 ...
$ Price : num 120 83 80 97 128 72 108 120 124 124 ...
$ ShelveLoc : Factor w/ 3 levels "Bad","Good","Medium": 1 2 3 3 1 1 3 2 3 3 ...
$ Age : num 42 65 59 55 38 78 71 67 76 76 ...
$ Education : num 17 10 12 14 13 16 15 10 10 17 ...
$ Urban : Factor w/ 2 levels "No","Yes": 2 2 2 2 2 1 2 2 1 1 ...
$ US : Factor w/ 2 levels "No","Yes": 2 2 2 2 1 2 1 2 1 2 ...
- attr(*, ".internal.selfref")=<externalptr>
5. Destacar os valores máximos das 20 primeiras linhas
# avaliando as 20 primeiras linhas
Carseats[1:20,]
# detectando os campos numericos:
campos_numericos = names(Filter(is.numeric, Carseats))
campos_numericos
[1] "Sales" "CompPrice" "Income" "Advertising" "Population" "Price"
[7] "Age" "Education"
# função para devolver os valores máximos de cada coluna, removendo os NA
colMax <- function(df) sapply(df, max, na.rm = TRUE)
# valores máximos das 20 primeiras linhas desses campos numéricos
colMax(Carseats[1:20, names(Carseats) %in% campos_numericos, with=FALSE])
Sales CompPrice Income Advertising Population Price Age Education
13.91 149.00 117.00 16.00 503.00 144.00 78.00 18.00
6. Destacar os valores mínimos das 20 últimas linhas
# função para devolver os valores mínimos de cada coluna, removendo os NA
colMin <- function(df) sapply(df, min, na.rm = TRUE)
# valores mínimos das 20 últimas linhas desses campos numéricos
colMin(Carseats[(nrow(Carseats)-19):nrow(Carseats), names(Carseats) %in% campos_numericos, with=FALSE])
Sales CompPrice Income Advertising Population Price Age Education
3.9 98.0 23.0 0.0 17.0 68.0 28.0 10.0
7. Listar os nomes das colunas
names(Carseats)
[1] "Sales" "CompPrice" "Income" "Advertising" "Population" "Price"
[7] "ShelveLoc" "Age" "Education" "Urban" "US"
8. Verificar a dimensão do dataset (total de linhas e colunas)
cat("Número de linhas: ", nrow(mtcars), "\n", sep="")
Número de linhas: 32
cat("Número de colunas: ", ncol(mtcars), "\n", sep="")
Número de colunas: 11
9. Contar o total de amostras por uma das variáveis categóricas tanto ordinal ou nominal
table(Carseats$Urban, useNA="always")
No Yes <NA>
118 282 0
10. Apresentar a estatística básica para o dataset – summary()
summary(Carseats)
Sales CompPrice Income Advertising Population
Min. : 0.000 Min. : 77 Min. : 21.00 Min. : 0.000 Min. : 10.0
1st Qu.: 5.390 1st Qu.:115 1st Qu.: 42.75 1st Qu.: 0.000 1st Qu.:139.0
Median : 7.490 Median :125 Median : 69.00 Median : 5.000 Median :272.0
Mean : 7.496 Mean :125 Mean : 68.66 Mean : 6.635 Mean :264.8
3rd Qu.: 9.320 3rd Qu.:135 3rd Qu.: 91.00 3rd Qu.:12.000 3rd Qu.:398.5
Max. :16.270 Max. :175 Max. :120.00 Max. :29.000 Max. :509.0
Price ShelveLoc Age Education Urban US
Min. : 24.0 Bad : 96 Min. :25.00 Min. :10.0 No :118 No :142
1st Qu.:100.0 Good : 85 1st Qu.:39.75 1st Qu.:12.0 Yes:282 Yes:258
Median :117.0 Medium:219 Median :54.50 Median :14.0
Mean :115.8 Mean :53.32 Mean :13.9
3rd Qu.:131.0 3rd Qu.:66.00 3rd Qu.:16.0
Max. :191.0 Max. :80.00 Max. :18.0
É possível analisar o range (amplitude), concentração (através dos
quartis) e média de cada campo quantitativo e as categorias presentes
nos campos qualitativos. Note a ausência de dados faltantes (NA) no
dataset. Para confirmar essa ausência de NA’s, é possível rodar o
seguinte script:
sum(is.na(Carseats))
[1] 0
11. Realizar a análise de correlação via linha de comando
# análise de correlação entre todos os campos numéricos
carseats_numericos = Carseats[,names(Carseats) %in% campos_numericos, with=FALSE]
round(cor(carseats_numericos),4)
Sales CompPrice Income Advertising Population Price Age Education
Sales 1.0000 0.0641 0.1520 0.2695 0.0505 -0.4450 -0.2318 -0.0520
CompPrice 0.0641 1.0000 -0.0807 -0.0242 -0.0947 0.5848 -0.1002 0.0252
Income 0.1520 -0.0807 1.0000 0.0590 -0.0079 -0.0567 -0.0047 -0.0569
Advertising 0.2695 -0.0242 0.0590 1.0000 0.2657 0.0445 -0.0046 -0.0336
Population 0.0505 -0.0947 -0.0079 0.2657 1.0000 -0.0121 -0.0427 -0.1064
Price -0.4450 0.5848 -0.0567 0.0445 -0.0121 1.0000 -0.1022 0.0117
Age -0.2318 -0.1002 -0.0047 -0.0046 -0.0427 -0.1022 1.0000 0.0065
Education -0.0520 0.0252 -0.0569 -0.0336 -0.1064 0.0117 0.0065 1.0000
1ª) r = 0.5848 - Note que a maior correlação ocorre
entre preço e preço da concorrência (Price vs CompPrice, com correlação
= 0.5848), o que é bastante lógico, ao passo que os preços devem seguir
um padrão por região, logo, preços entre concorrentes tendem a serem
relacionados.
2ª) r = -0.4450 - Como a segunda maior correlação,
temos a correlação entre preço e vendas (Price vs Sales, com correlação
= -0.4450). Essa relação também é esperada, ao passo que quando os
preços caem a tendência é de que as vendas aumentem.
3ª) r = 0.2695 - Como a terceira maior correlação,
temos vendas vs publicidade (Sales vs Advertising, com correlação =
0.2695). Isso reflete o impacto positivo do orçamento gasto com
campanhas publicitárias sobre o total de vendas de cadeiras infantis
para carros.
4ª) r = 0.2657 - Correlação grande ocorre entre população e publicidade (Population vs Advertising). Ao passo que a população aumenta, o investimento em publicidade também tende a seguir essa tendência.
5ª) r = -0.2318 - É interessante destacar a
correlação entre vendas e idade (Sales vs Age). Naturalmente, uma
população mais velha tende a ter menos filhos pequenos, o que leva a
tendência de reduzir as compras de cadeiras infantis para veículos
automotivos. Essa relação está presente com r = -0.2318 de correlação
entre vendas e idade.
# solicitando a análise de correlação, em termos de p-valor:
analise_correlacao = corr.test(x = carseats_numericos, # Dados
use = "complete", # Como vamos lidar com missings
method = "pearson") # tipo de correlação
Analisando p-valores dessas correlações:
round(analise_correlacao$p,4)
Sales CompPrice Income Advertising Population Price Age Education
Sales 0.0000 1.0000 0.0531 0.0000 1.0000 0.0000 0.0001 1.0000
CompPrice 0.2009 0.0000 1.0000 1.0000 1.0000 0.0000 0.9024 1.0000
Income 0.0023 0.1073 0.0000 1.0000 1.0000 1.0000 1.0000 1.0000
Advertising 0.0000 0.6294 0.2391 0.0000 0.0000 1.0000 1.0000 1.0000
Population 0.3140 0.0584 0.8752 0.0000 0.0000 1.0000 1.0000 0.7353
Price 0.0000 0.0000 0.2579 0.3743 0.8087 0.0000 0.8632 1.0000
Age 0.0000 0.0451 0.9258 0.9276 0.3948 0.0411 0.0000 1.0000
Education 0.2999 0.6154 0.2566 0.5029 0.0334 0.8148 0.8971 0.0000
A tabela abaixo apresenta os p-valores (p) através de asteriscos, onde:
p < 0.001 retornará três asteriscos,
0.001 < p < 0.01 retornará dois asteriscos,
0.01 < p < 0.05 retornará um asterisco:
ifelse(analise_correlacao$p <.001, "***", ifelse(analise_correlacao$p < 0.01, "**", ifelse(analise_correlacao$p <0.05, "*", "")))
Sales CompPrice Income Advertising Population Price Age Education
Sales "***" "" "" "***" "" "***" "***" ""
CompPrice "" "***" "" "" "" "***" "" ""
Income "**" "" "***" "" "" "" "" ""
Advertising "***" "" "" "***" "***" "" "" ""
Population "" "" "" "***" "***" "" "" ""
Price "***" "***" "" "" "" "***" "" ""
Age "***" "*" "" "" "" "*" "***" ""
Education "" "" "" "" "*" "" "" "***"
Note as 5 maiores correlações destacadas, e explicadas anteriormente, como obtendo p-valores <0.001. Com uma correlação quase borderline, mas ainda assim, significante a 5%, note preço versus idade, significativo a 5%, mas bem próximo da borda (p-valor = 0.0411). Com r = -0.1022, populações mais velhas tendem a pagar menos por cadeiras infantis para carro. Isso levanta uma hipótese de causa seguindo os princípios da lei da oferta x demanda, onde população envelhecendo tende a comprar menos cadeiras infantis por terem menos filhos pequenos, o que infla os estoques, o que obriga a baixarem os preços.
Análise de Correlação:
12. Criar um gráfico heatmap a partir das variáveis usadas na correlação
#Mapa de calor (heat map) Iris
#library(ggplot2) # para o mapa de calor (heatmap)
#library(reshape2) # reshape2::melt foi substituído por data.table::melt
dat <- carseats_numericos
cor <- data.table::melt(cor(dat, use="p"))
Aviso: Um matrix foi passado para o genérico melt em data.table, que vai tentar redirecionar para o método relevante no pacote reshape2; favor notar que reshape2 foi substituído e não está mais em desenvolvimento ativo, e que esse redirecionamento agora é obsoleto. Para continuar usando métodos melt de reshape2 enquanto ambos os pacotes estão anexados, p.ex. melt.list, você pode prefixar com o namespace, p.ex. reshape2::melt(cor(dat, use = "p")). Na próxima versão, este aviso se tornará um erro.
head(cor)
heat <- ggplot(data=cor, aes(x=Var1, y=Var2, fill=value))
heat + geom_tile() + labs(x = "", y = "") + scale_fill_gradient2(limits=c(-1, 1))
Destaque para correlações negativas fortes entre vendas e preço
(Sales vs Price) e correlações positivas fortes entre preço e preço da
concorrência (Price vs CompPrice). Tais situações já foram analisadas
anteriormente. O mapa de calor acima apresenta um bom apelo visual,
tornando a interpretação mais rápida.
13. Criar um scatterplot para o par de variáveis com maior
correlação
# scatterplot Price vs CompPrice
# Gráfico de dispersão - scatterplot
plot(carseats_numericos$Price,carseats_numericos$CompPrice, xlab="Preço", ylab="Preço dos Concorrentes")
Conforme já visualizado na análise de correlação, note a correlação
positiva entre preço dos assentos infantis versus preço da concorrência,
o que indica que os preços entre concorrentes locais são
correlacionados.
14. Realizar a análise bivariada por meio de scatterplots
para exibir a distribuição dos dados entre as principais variáveis
categóricas. Utilize cores e altere o tamanho dos pontos para facilitar
a interpretação
# Vetor de cores com base nas classes de ShelveLoc (Qualidade da localização das estantes)
cores <- c("Bad" = "red", "Medium" = "yellow", "Good" = "green")
cores_pontos <- scale_colour_manual(name="ShelveLoc", values=cores)
#Alterando o tamanho dos pontos
ggplot(Carseats, aes(x=Price, y=Sales, shape = ShelveLoc)) +
geom_point(size = 2, alpha= I(0.8), aes(colour = ShelveLoc)) +
cores_pontos +
xlab("Preço por assento (US$)") +
ylab("Vendas (milhares de unidades)")
Logo, é perceptível a relação de preços grandes e vendas baixas com
estantes mal localizadas. É possível que a qualidade da localização da
estante esteja impactando nas vendas, e não apenas o preço
unitário.
15. Realizar a análise univariada com um histograma para uma
variável numérica
# Criar o histograma com intervalos de 0,5
hist(Carseats$Sales, breaks = seq(min(Carseats$Sales),
max(Carseats$Sales) + 0.5, by = 0.5), xlim = c(0, max(Carseats$Sales)+3), main = "",
xlab = "Vendas (milhares de unidades)", ylab = "Frequência", col = "lightblue")
Note a concentração da quantia de vendas da loja bastante presente
entre 5 e 10 mil unidades.
16. Apresentar em apenas um gráfico vários histogramas para
as variáveis numéricas
Apresentando gráficos de maneira única, para visualização de todos os
campos numéricos:
# Histogramas de todas variáveis numéricas
par(mfrow = c(2,3))
hist(carseats_numericos$Sales, xlab="Vendas", main="", col="blue")
hist(carseats_numericos$CompPrice, xlab="Preço da Concorrência", main="", col="lightblue")
hist(carseats_numericos$Income, xlab="Renda comunitária (US$ milhares)", main="", col="blue")
hist(carseats_numericos$Advertising, xlab="Orçamento publicitário (US$ milhares)", main="", col="lightblue")
hist(carseats_numericos$Population, xlab="População (milhares)", main="", col="blue")
hist(carseats_numericos$Price, xlab="Preço do assento infantil", main="", col="lightblue")
hist(carseats_numericos$Age, xlab="Idade média populacional", main="", col="blue")
hist(carseats_numericos$Education, xlab="Nível educacional", main="", col="lightblue")
Porém, é possível utilizar histogramas sobrepostos para comparação de
campos numéricos entre grupos, como é feito abaixo:
# Será que Lojas Urbanas vendem mais ou menos do que Lojas Rurais?
# Além disso, Lojas Urbanas contêm preços maiores ou menores do que Lojas Rurais?
# Será que Lojas Americanas vendem mais ou menos do que Lojas Não Americanas?
# Além disso, quais apresentam preços maiores?
# Será que Lojas com Estantes bem localizadas vendem mais? E elas têm preços maiores?
#Histogramas
#library(tidyverse)
#library(grid)
#library(gridExtra)
h1 <- Carseats %>%
ggplot(aes(Sales)) + geom_histogram(aes(fill=Urban), binwidth=1, col="black") +
geom_vline(aes(xintercept=mean(Sales)), linetype="dashed", color="black") + labs(x="Vendas (milhares de unidades)", y="Frequência") +
theme(legend.position="none")
h2 <- Carseats %>%
ggplot(aes(Price)) + geom_histogram(aes(fill=Urban), binwidth=10, col="black") +
geom_vline(aes(xintercept=mean(Price)), linetype="dashed", color="black") + labs(x="Preço (US$)", y="Frequência") +
theme(legend.position="right")
grid.arrange(h1,h2, nrow=1, top=textGrob(""))
h1 <- Carseats %>%
ggplot(aes(Sales)) + geom_histogram(aes(fill=US), binwidth=1, col="black") +
geom_vline(aes(xintercept=mean(Sales)), linetype="dashed", color="black") + labs(x="Vendas (milhares de unidades)", y="Frequência") +
theme(legend.position="none")
h2 <- Carseats %>%
ggplot(aes(Price)) + geom_histogram(aes(fill=US), binwidth=10, col="black") +
geom_vline(aes(xintercept=mean(Price)), linetype="dashed", color="black") + labs(x="Preço (US$)", y="Frequência") +
theme(legend.position="right")
grid.arrange(h1,h2, nrow=1, top=textGrob(""))
h1 <- Carseats %>%
ggplot(aes(Sales)) + geom_histogram(aes(fill=ShelveLoc), binwidth=1, col="black") +
geom_vline(aes(xintercept=mean(Sales)), linetype="dashed", color="black") + labs(x="Vendas (milhares de unidades)", y="Frequência") +
theme(legend.position="none")
h2 <- Carseats %>%
ggplot(aes(Price)) + geom_histogram(aes(fill=ShelveLoc), binwidth=10, col="black") +
geom_vline(aes(xintercept=mean(Price)), linetype="dashed", color="black") + labs(x="Preço (US$)", y="Frequência") +
theme(legend.position="right")
grid.arrange(h1,h2, nrow=1, top=textGrob(""))
Urban: Há bastante similaridade nas vendas e preços
de lojas urbanas e rurais.
US: Já na análise da nacionalidade das lojas, é
possível notar um volume de vendas um pouco maior para as lojas
americanas do que para as não americanas. Mas a diferença parece
sutil.
Shelveloc: Na mesma linha do scatterplot anteriormente
visualizado, nos histogramas é possível notar um comportamento visível
no aumento de vendas ao passo que a localização do produto nas estantes
fiquei melhor.
17. Verificar com boxplots a presença de possíveis
outliers
# Boxplots de todas variáveis numéricas
par(mfrow = c(2,3))
boxplot(carseats_numericos$Sales, xlab="Vendas", main="", col="blue")
boxplot(carseats_numericos$CompPrice, xlab="Preço da Concorrência", main="", col="lightblue")
boxplot(carseats_numericos$Income, xlab="Renda comunitária (US$ milhares)", main="", col="blue")
boxplot(carseats_numericos$Advertising, xlab="Orçamento publicitário (US$ milhares)", main="", col="lightblue")
boxplot(carseats_numericos$Population, xlab="População (milhares)", main="", col="blue")
boxplot(carseats_numericos$Price, xlab="Preço do assento infantil", main="", col="lightblue")
boxplot(carseats_numericos$Age, xlab="Idade média populacional", main="", col="blue")
boxplot(carseats_numericos$Education, xlab="Nível educacional", main="", col="lightblue")
Outliers encontrados:
Vendas: dois outliers superiores, representando duas
lojas com mais de 15 mil vendas;
Preço da Concorrência: um outlier superior e um outlier
inferior, representando, respectivamente, uma loja com concorrente
cobrando um valor muito alto na cadeira infantil para carro (mais do que
US$ 160) e uma loja com concorrente cobrando um valor muito baixo no
item (menos de US$ 80).
Preço: dois outliers superiores, cobrando mais de US$
160 e três outliers inferiores, cobrando menos de U$ 60 no
produto.
18. Criar um gráfico de barras ou colunas
#Gráfico de barras de vendas dentro e fora dos US
ggplot(Carseats, aes(x = US)) +
geom_bar() +
theme_classic() +
ggtitle("US") +
xlab("US") +
ylab("Frequency") +
geom_text(stat= "count",aes(label = ..count..), vjust = -1) + coord_cartesian(ylim = c(0, max(table(Carseats$US)*1.1)))
Note a predominância de lojas americanas na amostra.
19. Criar um gráfico de pizza ou setores para uma variável
categórica com porcentagens
pie(table(Carseats$Urban),
labels = paste(names(table(Carseats$Urban)), " - ",
table(Carseats$Urban)," (", 100*table(Carseats$Urban)/sum(table(Carseats$Urban)), "%)", sep=""), col=c("black", "white"), main = "Loja urbana")
É possível constatar que 70.5% (282 lojas) da amostra é referente a
lojas situadas na região urbana da cidade, em contraponto aos 29.5% (118
lojas) situados na região rural.
20. Interpretar os gráficos gerados
Interpretação efetuada ao longo do documento.