Como fatores internos e externos afetam vendas?
Hoje em dia, vivemos em uma era em que comprar se tornou basicamente um esporte, e entre as grandes empresas de vendas não temos como esquecer o Walmart. A multinacional é uma das maiores redes de varejo do mundo, conhecida por oferecer uma ampla variedade de produtos e preços competitivos. Fundada nos Estados Unidos, a empresa opera supermercados, hipermercados e lojas de departamento em diversos países, assim como suas operações influenciam fortemente o mercado varejista, pressionando concorrentes a reduzir preços e melhorar logística. O Walmart também impacta cadeias de suprimentos globais, exigindo altos níveis de eficiência de fornecedores. Além disso, sua presença afeta economias locais, levantando debates sobre concorrência e condições de trabalho.
Como o Walmart é uma das maiores redes de vendas, o exemplo que o mesmo deixa pode ajudar o leitor a entender um pouco sobre como o Walmart e outras grandes lojas podem afetar a economia local.
Para isso, utilizaremos dados de vendas do Walmart durante um período de 2 anos. Esses dados foram coletados anonimamente de 45 lojas do Walmart e incluem dados como vendas, descontos, feriados, entre outros.
Minha abordagem de análise é separar os dados que possam ajudar a entender as vendas do Walmart, em seguida, plotar diferentes gráficos que possam ajudar com esse objetivo.
Após verificar os resultados, identificarei as relações que os mesmos têm com as vendas e como podemos melhorar isso.
Essa análise serve de auxílio para entender o impacto das vendas. E, a partir dela, as conclusões podem ser utilizadas para melhorar os negócios de profissionais que trabalham com vendas durante certos períodos (feriados) ou ao longo do tempo.
| Pacotes | Proposito |
|---|---|
| dplyr | Para melhor manipulação dos datasets |
| readr | Para melhor importação dos dados |
| DT | Criação de interação com os dados |
| ggplot2 | Para criação de gráficos de fácil visualisação |
| ggthemes | Fornece temas adicionais para o ggplot2 |
| tidyverse | Coleção de pacotes para manipulação e visualização de dados |
| knitr | Para criar tabelas de fácil visualização |
| kableExtra | Permite estilizar as tabelas do knitr |
Leitura e tratamento
Os conjuntos de dados para essa análise estão divididos entre três
datasets: Stores (contém informações sobre 45 lojas), Features (contém
informações adicionais sobre as lojas e a região em que se encontram) e
Sales (onde se encontram os dados das vendas durante as datas de
2010-02-05 a 2012-11-01).
Primeiramente precisamos carregar os
dados, que podem ser econtrados no kaggle
ou carregados localmente e examinar suas variáveis.
df1 <- read_csv("/home/rick/Documentos/Rstudio_files/Features data set.csv")
df2 <- read_csv("/home/rick/Documentos/Rstudio_files/sales data-set.csv")
df3 <- read_csv("/home/rick/Documentos/Rstudio_files/stores data-set.csv")
| Variavel | Descricao |
|---|---|
| Store | O número da loja |
| Date | A semana |
| Temperature | Temperatura média na região |
| Fuel_Price | Custo do combustível na região |
| MarkDown1-5 | Dados anonimizados relacionados a descontos promocionais |
| CPI | Índice de Preços ao Consumidor, uma índice sobre o preço medio regional |
| Unemployment | A taxa de desemprego |
| IsHoliday | Indica se a semana é um feriado |
| Variavel | Descricao |
|---|---|
| Store | O número da loja |
| Dept | O número do departamento |
| Date | A semana |
| Weekly_Sales | Vendas na loja especificada durante aquela semana |
| IsHoliday | Indica se a semana é um feriado |
| Variavel | Descricao |
|---|---|
| Store | O número da loja |
| Type | O tipo da loja (A = loja grande, B = loja média, C= loja pequena) |
| Size | O tamanho da loja em metros quadrados |
Primeiramente, começando pelo dataset Features, ao fazermos algumas análises básicas podemos observar que algumas variáveis estão repletas de Nas. Para resolver isso, precisamos substituir os valores.
# informações básicas do dataset
str(df1)
## spc_tbl_ [8,190 × 12] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
## $ Store : num [1:8190] 1 1 1 1 1 1 1 1 1 1 ...
## $ Date : chr [1:8190] "05/02/2010" "12/02/2010" "19/02/2010" "26/02/2010" ...
## $ Temperature : num [1:8190] 42.3 38.5 39.9 46.6 46.5 ...
## $ Fuel_Price : num [1:8190] 2.57 2.55 2.51 2.56 2.62 ...
## $ MarkDown1 : num [1:8190] NA NA NA NA NA NA NA NA NA NA ...
## $ MarkDown2 : num [1:8190] NA NA NA NA NA NA NA NA NA NA ...
## $ MarkDown3 : num [1:8190] NA NA NA NA NA NA NA NA NA NA ...
## $ MarkDown4 : num [1:8190] NA NA NA NA NA NA NA NA NA NA ...
## $ MarkDown5 : num [1:8190] NA NA NA NA NA NA NA NA NA NA ...
## $ CPI : num [1:8190] 211 211 211 211 211 ...
## $ Unemployment: num [1:8190] 8.11 8.11 8.11 8.11 8.11 ...
## $ IsHoliday : logi [1:8190] FALSE TRUE FALSE FALSE FALSE FALSE ...
## - attr(*, "spec")=
## .. cols(
## .. Store = col_double(),
## .. Date = col_character(),
## .. Temperature = col_double(),
## .. Fuel_Price = col_double(),
## .. MarkDown1 = col_double(),
## .. MarkDown2 = col_double(),
## .. MarkDown3 = col_double(),
## .. MarkDown4 = col_double(),
## .. MarkDown5 = col_double(),
## .. CPI = col_double(),
## .. Unemployment = col_double(),
## .. IsHoliday = col_logical()
## .. )
## - attr(*, "problems")=<externalptr>
summary(df1)
## Store Date Temperature Fuel_Price
## Min. : 1 Length:8190 Min. : -7.29 Min. :2.472
## 1st Qu.:12 Class :character 1st Qu.: 45.90 1st Qu.:3.041
## Median :23 Mode :character Median : 60.71 Median :3.513
## Mean :23 Mean : 59.36 Mean :3.406
## 3rd Qu.:34 3rd Qu.: 73.88 3rd Qu.:3.743
## Max. :45 Max. :101.95 Max. :4.468
##
## MarkDown1 MarkDown2 MarkDown3 MarkDown4
## Min. : -2781 Min. : -265.76 Min. : -179.26 Min. : 0.22
## 1st Qu.: 1578 1st Qu.: 68.88 1st Qu.: 6.60 1st Qu.: 304.69
## Median : 4744 Median : 364.57 Median : 36.26 Median : 1176.42
## Mean : 7032 Mean : 3384.18 Mean : 1760.10 Mean : 3292.94
## 3rd Qu.: 8923 3rd Qu.: 2153.35 3rd Qu.: 163.15 3rd Qu.: 3310.01
## Max. :103185 Max. :104519.54 Max. :149483.31 Max. :67474.85
## NA's :4158 NA's :5269 NA's :4577 NA's :4726
## MarkDown5 CPI Unemployment IsHoliday
## Min. : -185.2 Min. :126.1 Min. : 3.684 Mode :logical
## 1st Qu.: 1440.8 1st Qu.:132.4 1st Qu.: 6.634 FALSE:7605
## Median : 2727.1 Median :182.8 Median : 7.806 TRUE :585
## Mean : 4132.2 Mean :172.5 Mean : 7.827
## 3rd Qu.: 4832.6 3rd Qu.:213.9 3rd Qu.: 8.567
## Max. :771448.1 Max. :229.0 Max. :14.313
## NA's :4140 NA's :585 NA's :585
colSums(is.na(df1))
## Store Date Temperature Fuel_Price MarkDown1 MarkDown2
## 0 0 0 0 4158 5269
## MarkDown3 MarkDown4 MarkDown5 CPI Unemployment IsHoliday
## 4577 4726 4140 585 585 0
Começando pelas colunas Markdown 1-5, Markdown é o valor que representa o desconto promocional em dada semana (que pode se referir a vários departamentos). Para substituir os valores que faltam, podemos usar a média dos valores, já que, dessa forma, podemos preservar o “centro” da distribuição dos dados sem alterar a soma total esperada de forma drástica.
Para isso utilizamos um loop simples para remover todos NAs e substituí-los pela média
#colunas que precisam ser exluidas do processo
exclude_col <- c("CPI","Unemployment")
#loop para tirar a média
for(col in names(df1)){
if(!(col%in%exclude_col) & is.numeric(df1[[col]])){
df1[[col]][is.na(df1[[col]])]<-mean(df1[[col]],na.rm=TRUE)
}
}
mark <- df1 %>%
select(MarkDown1:MarkDown5)
datatable(tail(mark, 100))
Note que acima excluímos as colunas “CPI” e “Unemployment”, isso se deve à diferença de categoria das duas colunas, os valores dessas colunas podem ser considerados categóricos, e tendem a se repetir por várias semanas ou meses. Usar a moda preenche os buracos com o valor “padrão” daquela região/período, evitando a criação de números decimais artificiais que não existem na realidade.
Como o R não tem uma função nativa para conseguir a moda dos valores, foi criada a função get_mode para facilitar o processo de aplicar a moda com um loop.
#função para pegar a moda
get_mode <- function(x) {
ux <- unique(x[!is.na(x)])
ux[which.max(tabulate(match(x, ux)))]
}
#colunas para incluir no loop
cols_to_fill <- c("CPI", "Unemployment")
# loopa pra preencher NAs
for (col in cols_to_fill) {
if (is.factor(df1[[col]])) {
df1[[col]] <- as.character(df1[[col]])
}
df1[[col]][is.na(df1[[col]])] <- get_mode(df1[[col]])
}
cpun <- df1 %>%
select(CPI, Unemployment)
datatable(tail(cpun,100))
Observando brevemente os datasets de vendas e lojas, podemos ver que ambos não têm nenhum problema aparente, então podemos passar para a junção.
str(df2)
## spc_tbl_ [421,570 × 5] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
## $ Store : num [1:421570] 1 1 1 1 1 1 1 1 1 1 ...
## $ Dept : num [1:421570] 1 1 1 1 1 1 1 1 1 1 ...
## $ Date : chr [1:421570] "05/02/2010" "12/02/2010" "19/02/2010" "26/02/2010" ...
## $ Weekly_Sales: num [1:421570] 24924 46039 41596 19404 21828 ...
## $ IsHoliday : logi [1:421570] FALSE TRUE FALSE FALSE FALSE FALSE ...
## - attr(*, "spec")=
## .. cols(
## .. Store = col_double(),
## .. Dept = col_double(),
## .. Date = col_character(),
## .. Weekly_Sales = col_double(),
## .. IsHoliday = col_logical()
## .. )
## - attr(*, "problems")=<externalptr>
colSums(is.na(df2))
## Store Dept Date Weekly_Sales IsHoliday
## 0 0 0 0 0
str(df3)
## spc_tbl_ [45 × 3] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
## $ Store: num [1:45] 1 2 3 4 5 6 7 8 9 10 ...
## $ Type : chr [1:45] "A" "A" "B" "A" ...
## $ Size : num [1:45] 151315 202307 37392 205863 34875 ...
## - attr(*, "spec")=
## .. cols(
## .. Store = col_double(),
## .. Type = col_character(),
## .. Size = col_double()
## .. )
## - attr(*, "problems")=<externalptr>
colSums(is.na(df3))
## Store Type Size
## 0 0 0
Ao juntarmos os dados, juntamos principalmente por loja, data e
feriado.
Também aproveitamos que todos os dados estão juntos para converter alguns tipos de dados. Convertemos a data para um formato adequado para tratamento e Store e Dept para fatores, já que cada valor é único. Por fim, vamos criar uma nova coluna chamada total_markdown que é a soma de todos os markdowns (para facilitar as análises posteriores).
#juntando, formatando e criando dados
df<-df2%>%left_join(df1,by=c("Store","Date","IsHoliday"))%>%left_join(df3,by="Store")%>%
mutate(Store = as.factor(Store), Dept = as.factor(Dept), Date = as.Date(Date,format="%d/%m/%Y"),
Total_MarkDown = MarkDown1 + MarkDown2 + MarkDown3 + MarkDown4 + MarkDown5)%>%
filter(!is.na(Weekly_Sales))
str(df)
## tibble [421,570 × 17] (S3: tbl_df/tbl/data.frame)
## $ Store : Factor w/ 45 levels "1","2","3","4",..: 1 1 1 1 1 1 1 1 1 1 ...
## $ Dept : Factor w/ 81 levels "1","2","3","4",..: 1 1 1 1 1 1 1 1 1 1 ...
## $ Date : Date[1:421570], format: "2010-02-05" "2010-02-12" ...
## $ Weekly_Sales : num [1:421570] 24924 46039 41596 19404 21828 ...
## $ IsHoliday : logi [1:421570] FALSE TRUE FALSE FALSE FALSE FALSE ...
## $ Temperature : num [1:421570] 42.3 38.5 39.9 46.6 46.5 ...
## $ Fuel_Price : num [1:421570] 2.57 2.55 2.51 2.56 2.62 ...
## $ MarkDown1 : num [1:421570] 7032 7032 7032 7032 7032 ...
## $ MarkDown2 : num [1:421570] 3384 3384 3384 3384 3384 ...
## $ MarkDown3 : num [1:421570] 1760 1760 1760 1760 1760 ...
## $ MarkDown4 : num [1:421570] 3293 3293 3293 3293 3293 ...
## $ MarkDown5 : num [1:421570] 4132 4132 4132 4132 4132 ...
## $ CPI : num [1:421570] 211 211 211 211 211 ...
## $ Unemployment : num [1:421570] 8.11 8.11 8.11 8.11 8.11 ...
## $ Type : chr [1:421570] "A" "A" "A" "A" ...
## $ Size : num [1:421570] 151315 151315 151315 151315 151315 ...
## $ Total_MarkDown: num [1:421570] 19602 19602 19602 19602 19602 ...
para facilitar as análises
Primeiramente, para analisarmos o impacto que feriados têm nas vendas, criamos um subset que contém a soma total das vendas semanais e a informação se houve um feriado naquela semana ou não.
daily_sales <- limpo %>%
group_by(Date) %>%
summarise(
Total_Sales = sum(Weekly_Sales),
IsHoliday = any(IsHoliday) # Se qualquer loja tem feriado, marca o dia
)
datatable(head(daily_sales, 100))
Como os dados são de várias lojas de diferentes tamanhos, podemos separar as vendas semanais e a relação de vendas pelo tamanho da loja para posteriormente inferir.
store_perf <- limpo %>%
group_by(Store, Type, Size) %>%
summarise(Total_Sales = sum(Weekly_Sales), .groups = 'drop') %>%
mutate(Efficiency = Total_Sales / Size)
datatable(head(store_perf,100))
Aqui separamos todas as variáveis numéricas que usaremos para descobrir a correlação de como esses diferentes fatores afetam as vendas do Walmart.
numeric_vars <- limpo %>%
select(Weekly_Sales, Size, Total_MarkDown, Fuel_Price, Temperature, CPI, Unemployment)
datatable(head(numeric_vars,100))
Por fim, separamos os dados necessários para identificar como as vendas se comparam em média, durante dias normais e dias com feriados.
holiday_impact <- limpo %>%
group_by(Type, IsHoliday) %>%
summarise(Mean_Sales = mean(Weekly_Sales), .groups = 'drop')
datatable(head(holiday_impact,100))
Essa primeira análise foca na evolução das vendas ao longo do tempo. Podemos observar no gráfico de linhas que existe um padrão sazonal extremamente forte e repetitivo.
O pico de vendas não ocorre apenas no Natal, mas há um pico secundário muito forte no final de novembro (Ação de graças/Black friday), seguido por uma queda abrupta e depois o pico de Natal.
As semanas de feriado (pontos vermelhos) coincidem quase perfeitamente com os picos locais de vendas, mas note que nem todos os feriados geram o mesmo impacto. O Super Bowl (início do ano) tem um impacto muito menor comparado aos feriados de fim de ano.
# Gráfico de Linha com destaque para Feriados
daily_sales$Date <- as.Date(daily_sales$Date)
p1 <- ggplot(daily_sales, aes(x = Date, y = Total_Sales, group = 1)) +
geom_line(color = "#2c3e50", size = 0.8) +
# Adicionar pontos vermelhos apenas nos feriados
geom_point(data = filter(daily_sales, IsHoliday == TRUE),
aes(color = "Feriado"), size = 2) +
scale_color_manual(values = c("Feriado" = "#e74c3c"), name = "") +
theme_hc() + # Tema estilo Highcharts
labs(
title = "Tendência de Vendas e impactos de feriados (2010-2012)",
x = "Data",
y = "Vendas Totais ($)"
)
p1
De acordo com o tamanho de uma loja, o volume total de vendas pode ser enganoso se a loja for muito grande. No gráfico, podemos ver quais tipos de loja (A, B ou C) aproveitam melhor seu espaço físico.
Embora as lojas do tipo A tenham o maior volume absoluto de vendas, as lojas do tipo B mostram uma mediana de eficiência surpreendentemente competitiva, e em alguns casos, superior em consistência.
Lojas do Tipo C têm a menor eficiência por metro quadrado, indicando que, além de menores, elas convertem menos vendas por unidade de área do que os outros formatos. Isso sugere que o sortimento ou a localização dessas lojas pode precisar de revisão.
p2 <- ggplot(store_perf, aes(x = Type, y = Efficiency, fill = Type)) +
geom_boxplot(alpha = 0.7) +
scale_fill_tableau() + # Paleta de cores do Tableau
theme_hc() +
labs(
title = "Eficiência: Vendas por Metro Quadrado",
subtitle = "Comparação entre Tipos de Loja (A, B, C)",
x = "Tipo de Loja",
y = "Vendas Totais / Tamanho ($/Size)"
)
p2
Nesta análise, calculamos a correlação de Weekly_Sales com as variáveis numéricas para descobrir se fatores externos como preço do combustível ou desemprego realmente afetam as vendas semanais. No gráfico de barras divergentes conseguimos ver que:
Unemployment: Tem uma correlação negativa (-0.027) com as vendas semanais. Isso indica que as vendas do Walmart são resilientes a flutuações nas taxas de desemprego locais.
CPI: Com uma correlação de -(0.021), podemos ver que o CPI (um índice de média de preços da região) não afeta as vendas. O que está dentro do esperado, já que o cidadão comum não leva em consideração esse índice.
Temperature: Com o valor se aproximando de zero (0.002), podemos concluir que a temperatura da região não impede as pessoas de irem às compras.
Fuel_Price: A correlação é zero. O que quer dizer que o preço da gasolina não dita o quanto as pessoas gastam no Walmart semanalmente.
Total_MarkDown: A correlação é levemente positiva (0.072), o que indica que as promoções afetam as compras que as pessoas fazem, no entanto, a correlação é menor do que o esperado.
Size: A correlação positiva mais forte (0.2) com vendas semanais, o que é esperado, já que as pessoas tendem a comprar mais em lojas que contêm mais mercadorias.
O que podemos concluir é que o volume de vendas é muito mais impulsionado por fatores internos (tamanho da loja, descontos) do que por fatores externos e macroeconomia local (desemprego, CPI, temperatura e combustível).
# Calcular correlação apenas contra Weekly_Sales
cor_target <- cor(numeric_vars, use = "complete.obs")
cor_target_df <- as.data.frame(cor_target) %>%
select(Weekly_Sales) %>%
rownames_to_column(var = "Variavel") %>%
filter(Variavel != "Weekly_Sales") %>% # Remover a auto-correlação (que é 1)
arrange(desc(Weekly_Sales))
# Gráfico de Barras Divergentes
p3 <- ggplot(cor_target_df, aes(x = reorder(Variavel, Weekly_Sales), y = Weekly_Sales, fill = Weekly_Sales > 0)) +
geom_bar(stat = "identity", width = 0.6) +
coord_flip() + # Barras horizontais para facilitar leitura
scale_fill_manual(values = c("TRUE" = "#27ae60", "FALSE" = "#c0392b"), guide = "none") +
theme_hc() +
labs(
title = "O que impulsiona as Vendas?",
subtitle = "Correlação de cada variável com Weekly_Sales",
x = "Variável",
y = "Coeficiente de Correlação (Pearson)"
) +
geom_text(aes(label = round(Weekly_Sales, 3)),
hjust = ifelse(cor_target_df$Weekly_Sales > 0, -0.2, 1.2),
size = 3.5)
p3
Abaixo, comparamos a média de vendas em semanas normais vs semanas de feriado, segmentado pelo tipo de loja.
Em média, uma semana de feriado traz um aumento razoável nas vendas médias por departamento. O gráfico de barras mostra que as lojas do Tipo A (as maiores) são as que mais se beneficiam proporcionalmente dos feriados, indicando que os consumidores preferem as lojas grandes para compras de grandes eventos sazonais.
p4 <- ggplot(holiday_impact, aes(x = Type, y = Mean_Sales, fill = IsHoliday)) +
geom_bar(stat = "identity", position = "dodge", alpha = 0.8) +
scale_fill_manual(values = c("FALSE" = "#95a5a6", "TRUE" = "#e67e22")) +
theme_hc() +
labs(
title = "Média de Vendas: Feriado vs Dias Comuns",
subtitle = "Impacto relativo por tipo de loja",
x = "Tipo de Loja",
y = "Média de Vendas ($)"
)
p4
Esta análise tem como objetivo ajudar o leitor a formar uma compreensão fundamentada sobre o desempenho de vendas do Walmart com base nos dados fornecidos. Apoiada por representações gráficas, o foco desta análise é observar a relação das vendas aos feriados dentro de um perído de tempo e identificar possíveis fatores ou padrões que possam estar influenciando esses resultados.
Para obter uma compreensão mais clara sobre as tendências de vendas do Walmart ao longo do tempo, começamos separando as variáveis categóricas e numéricas, permitindo uma organização mais precisa dos dados. Em seguida, utilizamos diversos gráficos para inferir informações relevantes sobre o comportamento das vendas em diferentes períodos e contextos. Por fim, identificamos padrões importantes, relacionados à influência de fatores internos e externos, como feriados e tamanho das lojas no desempenho das vendas, permitindo uma análise mais profunda sobre o que impulsiona ou reduz a demanda.
Os feriados afetam fortemente as vendas semanais do Wallmart, entretanto nem todos os feriados aumentam consideravelmente as vendas da loja
Lojas de maior porte e consequentemente maior variedade de produtos tiveram vendas maiores
Fatores externos são menos propenços a afetar as vendasdo que fatores externos
Feriados em geral trazem um aumento de vendas, mas as lojas de maior porte se beneficiam mais do que as outras.
A análise pode ser utilizada para compreender como diferentes fatores afetam os níveis de vendas do Walmart. Observamos que determinados eventos, como feriados, tendem a elevar ou reduzir o volume de vendas de forma consistente. Esse padrão torna-se ainda mais evidente em datas de grande movimento, como o Natal e a Black Friday, quando há promoções. Além disso, percebemos como aspectos externos podem alterar significativamente o comportamento dos consumidores. Esses insights são valiosos para profissionais que trabalham com vendas, pois permitem identificar oportunidades de melhoria, planejar estratégias mais eficazes e antecipar impactos que determinados fatores podem ter no resultado final.
Essa análise apresenta limitações devido à natureza dos dados disponíveis. Como os dados são anônimos e pouco detalhados em alguns aspectos, não é possível considerar informações essenciais, como a localização das lojas ou o perfil dos clientes. Essas ausências restringem nossa capacidade de identificar padrões específicos relacionados a regiões, tipos de consumidores ou comportamentos de compra segmentados.
Futuramente, seria interessante adicionar tais dados, pois permitiria uma compreensão mais profunda e direcionada, ajudando profissionais da área de vendas a desenvolver estratégias ainda mais eficazes e alinhadas às necessidades reais do mercado.