Explorando Tendências de Consumo e Preferências Regionais no Brasil no E-commerce Brasileiro

Introdução

Declaração do Problema

Com o crescimento do comércio eletrônico, entender como as pessoas compram e como isso afeta a satisfação do cliente é muito importante para qualquer empresa. Nota-se que os clientes cada vez mais exigentes, onde suas escolhas guiam o fluxo do mercado, fazem as empresas se moldarem para atender às necessidades dos seus consumidores (Rodrigues 2021). Compreender essas tendências pode auxiliar as organizações a aumentar seu lucro e a fidelizar os clientes, através de estratégias cada vez mais ágeis e centradas no cliente.

Por meio da tecnologia, é possível obter um histórico de como os clientes se comportam, quando eles compram, por quanto e como eles avaliam a pre-venda e pós-venda. A Olist, uma das maiores lojas de departamentos dos mercados brasileiros, disponibilizou um conjunto de dados (Olist and Sionek 2018) abrangente sobre pedidos realizados em sua plataforma. Que abrange informações detalhadas sobre pedidos, pagamentos, produtos, vendedores, clientes e avaliações coletadas no ano de 2016 à 2018, oferece uma oportunidade valiosa para investigar diversos aspectos das operações de e-commerce. Através da análise destes dados, podemos identificar padrões, tendências e potenciais áreas de melhoria que podem beneficiar tanto os comerciantes quanto os consumidores. Junto ao conjunto de dados, também utilizaremos a API do IBGE (Instituto Brasileiro de Geografia e Estatástica 2024) e um Conjunto de CEPs do Brasil (Filho and Atilio 2019).

Este relatório tem como objetivo analisar as tendências de compras e o comportamento dos consumidores em diferentes regiões do Brasil. A análise visa proporcionar uma compreensão abrangente de onde estão concentrados os principais clientes, segmentados por categoria de compra. Ao investigar esses padrões de consumo, o relatório pretende oferecer informações estratégicas para que as empresas entendam seus mercados-alvos e otimizem suas operações para atender às demandas variadas dos consumidores em diferentes localidades do país, ajustando suas estratégias de mercado de forma mais precisa e eficaz.

Método

O método foca na coleta e extração de dados de transações comerciais volumosas, seguido pela visualização e pré-processamento simultâneos desses dados, com o objetivo de gerar gráficos e tabelas para análise do conteúdo obtido. A figura abaixo ilustra o passo a passo deste processo.

Conjunto de Dados

Conjuntos e subcojuntos de dados que serão usados:

Nome Descrição Fonte
CEPS do Brasil Lista de CEPs de todo o Brasil, incluindo faixas de CEP por localidade. Lista de CEPs do Brasil
Localidades do IBGE Informações detalhadas sobre localidades no Brasil, incluindo dados de municípios, estados, mesorregiões e microrregiões. API de Localidades do IBGE
olist_customers_dataset Dataset com informações sobre clientes da plataforma Olist. Olist Brazilian E-Commerce Dataset
olist_order_items_dataset Dataset com detalhes dos itens de pedidos na plataforma Olist. Olist Brazilian E-Commerce Dataset
olist_order_payments_dataset Dataset com informações de pagamentos de pedidos na plataforma Olist. Olist Brazilian E-Commerce Dataset
olist_order_reviews_dataset Dataset com avaliações de pedidos na plataforma Olist. Olist Brazilian E-Commerce Dataset
olist_orders_dataset Dataset com dados gerais de pedidos na plataforma Olist. Olist Brazilian E-Commerce Dataset
olist_products_dataset Dataset com informações sobre produtos na plataforma Olist. Olist Brazilian E-Commerce Dataset
olist_sellers_dataset Dataset com informações sobre vendedores na plataforma Olist. Olist Brazilian E-Commerce Dataset

Detalhamento de cada conjunto e subconjuntos:

CEPS do Brasil

O dataset foi projetado em meados de 2017 a partir do site site de busca de CEPS dos Correios disponibilizado em 2019 por (Filho and Atilio 2019) e está disponível neste link no Kaggle.

Coluna Descrição
Estado Sigla do Estado/UF da Localidade
Localidade Nome da Cidade/Município
Faixa de CEP Intervalo de CEPs atribuídos à localidade
CEP Inicial Primeiro CEP do intervalo atribuído à localidade
CEP Final Último CEP do intervalo atribuído à localidade.
Situação Descrição do status de codificação da localidade.
Tipo de Faixa Indicação sobre faixa de CEP

Localidades do IBGE

O dataset tem origem da API de Localidades do IBGE, e contém dados detalhados das localidades, desde regiões até distritos. Para este relatório, serão destacadas apenas as variáveis relevantes:

Coluna Descrição
municipio-nome Nome do Município
microrregiao-nome Nome da microrregião
mesorregiao-nome Nome da mesorregião
UF-sigla Sigla da Unidade da Federação
UF-nome Nome da Unidade da Federação
regiao-nome Nome da região

Olist Brazilian E-Commerce Dataset

Todas as colunas dos conjuntos da Olist usados no relátorio unificado.

Coluna Descrição
customer_id Identificador do cliente por pedido
customer_unique_id Identificador único do cliente
customer_zip_code_prefix Prefixo do CEP do cliente
customer_city Cidade do cliente
customer_state Estado do cliente
order_id Identificador único do pedido
order_item_id Número sequencial que identifica os itens incluídos no mesmo pedido
product_id Identificador exclusivo do produto
seller_id Identificador do vendedor
shipping_limit_date Data limite para o vendedor enviar o pedido ao parceiro logístico
price Preço do item
freight_value Valor do frete por item
payment_sequential Número sequencial do pagamento realizado pelo cliente
payment_type Forma de pagamento escolhida pelo cliente
payment_installments Número de parcelas escolhidas pelo cliente
payment_value Valor da transação
review_id Identificador único da avaliação
review_score Nota da avaliação em escala Likert
review_comment_title Título do comentário da avaliação
review_comment_message Mensagem de comentário da avaliação
review_creation_date Data em que a pesquisa de satisfação foi enviada ao cliente
review_answer_timestamp Data/hora da resposta da pesquisa de satisfação
order_status Status da encomenda (entregue, expedida, etc.)
order_purchase_timestamp Data/hora da compra
order_approved_at Data/hora de aprovação do pagamento
order_delivered_carrier_date Data/hora de postagem do pedido no operador logístico
order_delivered_customer_date Data real de entrega do pedido ao cliente
order_estimated_delivery_date Data estimada de entrega informada ao cliente no momento da compra
product_category_name Categoria raiz do produto
product_name_lenght Número de caracteres do nome do produto
product_description_lenght Número de caracteres da descrição do produto
product_photos_qty Número de fotos publicadas do produto
product_weight_g Peso do produto em gramas
product_length_cm Comprimento do produto em centímetros
product_height_cm Altura do produto em centímetros
product_width_cm Largura do produto em centímetros
seller_zip_code_prefix Prefixo do CEP do vendedor
seller_city Cidade do vendedor
seller_state Estado do vendedor

Pacotes necessários

Para realizar a análise das tendências de compras e comportamento dos consumidores em diferentes regiões do Brasil, serão utilizadas os seguintes pacotes:

Pacote Descrição
rmarkdown para centralizar as informações em apenas um documento.
readxl para carregar dados de arquivos Excel.
dplyr para manipulação eficiente de dados.
httr para realizar requisições HTTP.
jsonlite para trabalhar com dados em formato JSON.
stringi para manipulação avançada de strings.
purrr para operações funcionais em R.
ggplot2 para criar gráficos estáticos.
plotly para criar visualizações gráficas interativas.
DT para criar tabelas interativas.
tidyverse para manipulação e visualização de dados de forma eficiente.

Esses pacotes serão empregados para carregar, manipular, unir e visualizar os dados dos conjuntos de dados, bem como para realizar análises estatísticas e gráficas.

Preparação dos dados

Importando todos o pacotes necessários:

library(rmarkdown)  
library(readxl)
library(dplyr)
library(httr)
library(jsonlite)
library(stringi)
library(purrr)
library(DT)
library(ggplot2)
library(tidyverse)
library(plotly)

Extração dos conjuntos de dados para o formato de Dataframe:

reviews <- read.csv("olist_order_reviews_dataset.csv")
orders <- read.csv("olist_orders_dataset.csv")
order_payments <- read.csv("olist_order_payments_dataset.csv")
order_customer <- read.csv("olist_customers_dataset.csv")
order_items <- read.csv("olist_order_items_dataset.csv")
products <- read.csv("olist_products_dataset.csv")
sellers <- read.csv("olist_sellers_dataset.csv")
ceps <- read_excel("Lista_de_CEPs.xlsx")
df_municipios <- fromJSON(content(GET("https://servicodados.ibge.gov.br/api/v1/localidades/municipios?view=nivelado"), "text"), flatten = TRUE)

Primeiramente, verificaremos a existência de dados nulos nos conjuntos de dados:

# Verificando a presença de valores nulos nos conjuntos de dados
sum(is.na(reviews))
## [1] 0
sum(is.na(orders))
## [1] 0
sum(is.na(order_payments))
## [1] 0
sum(is.na(order_customer))
## [1] 0
sum(is.na(order_items))
## [1] 0
sum(is.na(products))
## [1] 1838
sum(is.na(sellers))
## [1] 0
sum(is.na(ceps))
## [1] 0
sum(is.na(df_municipios))
## [1] 0

Verifica-se também se há diferenças nos nomes das cidades entre os conjuntos da Olist e os dados do IBGE. Nota-se que em df_municipios, as letras estão misturadas entre maiúsculas e minúsculas, ao contrário dos outros conjuntos que estão todas em minúsculas. Para padronizar a comparação entre os conjuntos, criaremos novas colunas em todos os conjuntos, repetindo o nome da cidade, porém removendo sinais e transformando todas as letras em minúsculas.

#Imprimindo a primeira linha de cada cidade para comparação inicial
print(order_customer$customer_city[1])
## [1] "franca"
print(sellers$seller_city[1])
## [1] "campinas"
print(df_municipios$`municipio-nome`[1])
## [1] "Alta Floresta D'Oeste"
#Criando nova coluna com nomes das cidades formatados.
df_municipios <- df_municipios %>%
  mutate(municipio_nome_clean = tolower(stri_trans_general(`municipio-nome`, "Latin-ASCII")))

sellers <- sellers %>%
  mutate(seller_city_clean = tolower(stri_trans_general(seller_city, "Latin-ASCII")))

order_customer <- order_customer %>%
  mutate(customer_city_clean = tolower(stri_trans_general(customer_city, "Latin-ASCII")))

#Comparando os nomes das cidades
result_seller_city <- sellers %>%
  filter(!seller_city_clean %in% df_municipios$municipio_nome_clean)

head(result_seller_city$seller_city)
## [1] "lages - sc"             "balenario camboriu"     "ferraz de  vasconcelos"
## [4] "auriflama/sp"           "sao paulo / sao paulo"  "vicente de carvalho"
length(unique(result_seller_city$seller_city))
## [1] 70
result_customer_city <- order_customer 
result_customer_city <- result_customer_city %>%
  filter(!customer_city_clean %in% df_municipios$municipio_nome_clean)

head(result_customer_city$customer_city)
## [1] "parati"           "embu"             "espigao do oeste" "mogi-guacu"      
## [5] "passa tres"       "mogi-guacu"
length(unique(result_customer_city$customer_city))
## [1] 253

Podemos observar que há cerca de 70 cidades com nomes incorretos no conjunto sellers e 253 no conjunto order_customer. Vamos abordar isso utilizando o conjunto ceps, tentando corrigir a maior quantidade possível de cidades incorretas, como nomes de distritos e bairros no lugar do nome do município (por exemplo, “passa tres” de RJ no DF customer_city) e misturas de cidade e estado (por exemplo, “lages - sc” de SC no DF sellers).

Começaremos visualizando o conjunto de dados ceps em e o conjunto de dados df_municipios:

nrow(ceps) #Contagem de linhas
## [1] 6015
unique(ceps$`Tipo de Faixa`) 
## [1] "Total do município"       "Exclusiva da sede urbana"
head(ceps) #Visão inicial do df
## # A tibble: 6 × 7
##   Estado Localidade      `Faixa de CEP`       `CEP Inicial` `CEP Final` Situação
##   <chr>  <chr>           <chr>                        <dbl>       <dbl> <chr>   
## 1 AC     Acrelândia      69945-000 a 69949-9…      69945000    69949999 Não cod…
## 2 AC     Assis Brasil    69935-000 a 69939-9…      69935000    69939999 Não cod…
## 3 AC     Brasiléia       69932-000 a 69933-9…      69932000    69933999 Não cod…
## 4 AC     Bujari          69926-000 a 69926-9…      69926000    69926999 Não cod…
## 5 AC     Capixaba        69931-000 a 69931-9…      69931000    69931999 Não cod…
## 6 AC     Cruzeiro do Sul 69980-000 a 69981-9…      69980000    69981999 Não cod…
## # ℹ 1 more variable: `Tipo de Faixa` <chr>
nrow(df_municipios) #Contagem de linhas
## [1] 5570
head(df_municipios) #Visão inicial do DF
##   municipio-id        municipio-nome microrregiao-id microrregiao-nome
## 1      1100015 Alta Floresta D'Oeste           11006            Cacoal
## 2      1100023             Ariquemes           11003         Ariquemes
## 3      1100031                Cabixi           11008 Colorado do Oeste
## 4      1100049                Cacoal           11006            Cacoal
## 5      1100056            Cerejeiras           11008 Colorado do Oeste
## 6      1100064     Colorado do Oeste           11008 Colorado do Oeste
##   mesorregiao-id  mesorregiao-nome regiao-imediata-id regiao-imediata-nome
## 1           1102 Leste Rondoniense             110005               Cacoal
## 2           1102 Leste Rondoniense             110002            Ariquemes
## 3           1102 Leste Rondoniense             110006              Vilhena
## 4           1102 Leste Rondoniense             110005               Cacoal
## 5           1102 Leste Rondoniense             110006              Vilhena
## 6           1102 Leste Rondoniense             110006              Vilhena
##   regiao-intermediaria-id regiao-intermediaria-nome UF-id UF-sigla  UF-nome
## 1                    1102                 Ji-Paraná    11       RO Rondônia
## 2                    1101               Porto Velho    11       RO Rondônia
## 3                    1102                 Ji-Paraná    11       RO Rondônia
## 4                    1102                 Ji-Paraná    11       RO Rondônia
## 5                    1102                 Ji-Paraná    11       RO Rondônia
## 6                    1102                 Ji-Paraná    11       RO Rondônia
##   regiao-id regiao-sigla regiao-nome  municipio_nome_clean
## 1         1            N       Norte alta floresta d'oeste
## 2         1            N       Norte             ariquemes
## 3         1            N       Norte                cabixi
## 4         1            N       Norte                cacoal
## 5         1            N       Norte            cerejeiras
## 6         1            N       Norte     colorado do oeste

Agora, vamos tratar as linhas de CEP, focando na faixa de CEP que engloba toda a área do município. Isso é necessário porque o tipo “Exclusiva da sede urbana” não inclui a zona rural e alguns distritos. Observamos que o DF ceps tem 5573 linhas, enquanto o DF df_municipios tem 5570 linhas, o que ocorre, devido ao fato de que algumas cidades têm duas faixas de CEP, como mostra abaixo.

ceps <- ceps %>% filter(`Tipo de Faixa` == "Total do município")
ceps <- ceps %>% select(-`Faixa de CEP`, -Situação, -`Tipo de Faixa`) #Eliminando colunas não relevantes.
nrow(ceps) #Contagem de linhas depois da transformação
## [1] 5573
print(ceps %>%
  group_by(Localidade, Estado) %>%
  filter(n() > 1)) #Linhas não únicas com mesma cidades e estado.
## # A tibble: 6 × 4
## # Groups:   Localidade, Estado [3]
##   Estado Localidade  `CEP Inicial` `CEP Final`
##   <chr>  <chr>               <dbl>       <dbl>
## 1 DF     Brasília         70000001    72799999
## 2 DF     Brasília         73000001    73699999
## 3 RJ     Nova Iguaçu      26000001    26099999
## 4 RJ     Nova Iguaçu      26200000    26299999
## 5 SP     São Paulo         1000001     5999999
## 6 SP     São Paulo         8000000     8499999

A seguir, são listadas as cidades que têm nomes diferentes nos conjuntos ceps e df_municipios. Foi feito um filtro que compara ambos, removendo acentos e convertendo as letras para minúsculas.

filter_ceps <- ceps %>%
  filter(!(tolower(stri_trans_general(ceps$Localidade, "Latin-ASCII")) %in% tolower(stri_trans_general(df_municipios$`municipio-nome`, "Latin-ASCII"))
         & Estado %in% df_municipios$`UF-sigla`)) 
unique(filter_ceps$Localidade)
##  [1] "Muquém de São Francisco"   "Pindaré Mirim"            
##  [3] "Amparo da Serra"           "Olhos D'Água"             
##  [5] "Santa Rita do Ibitipoca"   "São Thomé das Letras"     
##  [7] "Sem Peixe"                 "Santo Antônio do Leverger"
##  [9] "Eldorado dos Carajás"      "Santa Isabel do Pará"     
## [11] "Iguaraci"                  "Lagoa do Itaenga"         
## [13] "Arez"                      "Boa Saúde"                
## [15] "Olho-D'Água do Borges"     "Espigão do Oeste"         
## [17] "Santana do Livramento"     "Grão Pará"                
## [19] "Amparo de São Francisco"   "Graccho Cardoso"          
## [21] "Biritiba-Mirim"            "Couto de Magalhães"       
## [23] "Fortaleza do Tabocão"      "São Valério da Natividade"
filter_mun <- df_municipios %>%
  filter(!(tolower(stri_trans_general(`municipio-nome`, "Latin-ASCII")) %in% tolower(stri_trans_general(ceps$Localidade, "Latin-ASCII"))
         & `UF-sigla` %in% ceps$Estado))
unique(filter_mun$`municipio-nome`) 
##  [1] "Espigão D'Oeste"           "Eldorado do Carajás"      
##  [3] "Santa Izabel do Pará"      "Couto Magalhães"          
##  [5] "Tabocão"                   "São Valério"              
##  [7] "Pindaré-Mirim"             "Arês"                     
##  [9] "Januário Cicco"            "Olho d'Água do Borges"    
## [11] "Iguaracy"                  "Lagoa de Itaenga"         
## [13] "Amparo do São Francisco"   "Gracho Cardoso"           
## [15] "Muquém do São Francisco"   "Amparo do Serra"          
## [17] "Olhos-d'Água"              "Santa Rita de Ibitipoca"  
## [19] "São Tomé das Letras"       "Sem-Peixe"                
## [21] "Biritiba Mirim"            "Grão-Pará"                
## [23] "Sant'Ana do Livramento"    "Santo Antônio de Leverger"
## [25] "Bom Jesus de Goiás"

Observa-se que há 24 cidades com nomes diferentes entre os conjuntos, além disso, uma cidade chamada “Bom Jesus de Goiás” foi encontrada apenas no segundo filtro, o que indica que no DF ceps há uma cidade com o nome incorreto, mas com o estado correto. Ao filtrarmos, encontramos apenas uma cidade com o nome “Bom Jesus”, das quais, podemos observar que há 6 cidades com esse nome, e uma em Goiás, e verificamos que existe apenas isso. Quando comparamos o df_municipios, podemos ver que essa cidade não existe. Portanto, o nome correto da cidade no conjunto ceps deveria ser “Bom Jesus de Goiás” e não apenas “Bom Jesus”.

filter(ceps, Localidade == "Bom Jesus")
## # A tibble: 6 × 4
##   Estado Localidade `CEP Inicial` `CEP Final`
##   <chr>  <chr>              <dbl>       <dbl>
## 1 GO     Bom Jesus       75570000    75579999
## 2 PB     Bom Jesus       58930000    58932999
## 3 PI     Bom Jesus       64900000    64904999
## 4 RN     Bom Jesus       59270000    59274999
## 5 RS     Bom Jesus       95290000    95299999
## 6 SC     Bom Jesus       89824000    89824999
filter(ceps, Localidade == "Bom Jesus de Goiás")
## # A tibble: 0 × 4
## # ℹ 4 variables: Estado <chr>, Localidade <chr>, CEP Inicial <dbl>,
## #   CEP Final <dbl>
filter(df_municipios, `municipio-nome` == "Bom Jesus" & `UF-sigla` == "GO") %>% select( `municipio-nome`, `UF-sigla`)
## [1] municipio-nome UF-sigla      
## <0 linhas> (ou row.names de comprimento 0)
filter(df_municipios, `municipio-nome` == "Bom Jesus de Goiás" & `UF-sigla` == "GO") %>% select( `municipio-nome`, `UF-sigla`)
##       municipio-nome UF-sigla
## 1 Bom Jesus de Goiás       GO

Agora vamos corrigir esses dados de ceps.

corrections_ <- list(
  "Muquém de São Francisco" = "Muquém do São Francisco",
  "Pindaré Mirim" = "Pindaré-Mirim",
  "Amparo da Serra" = "Amparo do Serra",
  "Olhos D'Água" = "Olhos-d'Água",
  "Santa Rita do Ibitipoca" = "Santa Rita de Ibitipoca",
  "São Thomé das Letras" = "São Tomé das Letras",
  "Sem Peixe" = "Sem-Peixe",
  "Santo Antônio do Leverger" = "Santo Antônio de Leverger",
  "Eldorado dos Carajás" = "Eldorado do Carajás",
  "Santa Isabel do Pará" = "Santa Izabel do Pará",
  "Iguaraci" = "Iguaracy",
  "Lagoa do Itaenga" = "Lagoa de Itaenga",
  "Arez" = "Arês",
  "Boa Saúde" = "Januário Cicco",
  "Olho-D'Água do Borges" = "Olho d'Água do Borges",
  "Espigão do Oeste" = "Espigão D'Oeste",
  "Santana do Livramento" = "Sant'Ana do Livramento",
  "Grão Pará" = "Grão-Pará",
  "Amparo de São Francisco" = "Amparo do São Francisco",
  "Graccho Cardoso" = "Gracho Cardoso",
  "Biritiba-Mirim" = "Biritiba Mirim",
  "Couto de Magalhães" = "Couto Magalhães",
  "Fortaleza do Tabocão" = "Tabocão",
  "São Valério da Natividade" = "São Valério"
)

Definimos duas funções para auxiliar no tratamento dos dados:

  • apply_corrections: essa função verifica se o nome da cidade está na lista de correções e, se estiver, substitui pelo nome correto.
  • get_estado_localidade: esta função verifica se o CEP/zipcode está dentro de uma faixa válida e retorna o estado e a localidade corretos.
apply_corrections <- function(city_name, corrections) {
  if (city_name %in% names(corrections)) {
    return(corrections[[city_name]])
  } else {
    return(city_name)
  }
}

get_estado_localidade <- function(cep) {
  subset <- ceps %>% 
    filter(cep >= `CEP Inicial` & cep <= `CEP Final`)
  
  if (nrow(subset) == 1) {
    return(list(Estado = subset$Estado, Localidade = subset$Localidade))
  } else {
    return(list(Estado = NA, Localidade = NA))
  }
}

Aplicamos as correções nos nomes das cidades da coluna Localidade de ceps:

#executando função que aplica as correções
ceps <- ceps %>%
  mutate(Localidade = sapply(Localidade, apply_corrections, corrections = corrections_))
#visualização do cabeçallho de ceps
head(ceps)
## # A tibble: 6 × 4
##   Estado Localidade      `CEP Inicial` `CEP Final`
##   <chr>  <chr>                   <dbl>       <dbl>
## 1 AC     Acrelândia           69945000    69949999
## 2 AC     Assis Brasil         69935000    69939999
## 3 AC     Brasiléia            69932000    69933999
## 4 AC     Bujari               69926000    69926999
## 5 AC     Capixaba             69931000    69931999
## 6 AC     Cruzeiro do Sul      69980000    69981999

Podemos notar que as colunas CEP Incial e CEP Final de ceps estão com a númeração completa, vamos tratar isso, removendo os três últimos dígitos de cada coluna para garantir que representem corretamente a faixa de CEP:

# Verificando o tipo de dados das colunas CEP Inicial e CEP Final
str(ceps$`CEP Inicial`)
##  num [1:5573] 69945000 69935000 69932000 69926000 69931000 ...
str(ceps$`CEP Final`)
##  num [1:5573] 69949999 69939999 69933999 69926999 69931999 ...
# Convertendo as colunas para inteiro, removendo os 3 últimos dígitos e convertendo de volta para inteiro
ceps <- ceps %>%
  mutate(`CEP Inicial` = as.integer(`CEP Inicial`),
    `CEP Final` = as.integer(`CEP Final`)) %>%
  mutate(`CEP Inicial` = as.character(`CEP Inicial`),
    `CEP Final` = as.character(`CEP Final`)) %>%
  mutate(`CEP Inicial` = substr(`CEP Inicial`, 1, nchar(`CEP Inicial`) - 3),
    `CEP Final` = substr(`CEP Final`, 1, nchar(`CEP Final`) - 3)) %>%
  mutate(`CEP Inicial` = as.integer(`CEP Inicial`),
    `CEP Final` = as.integer(`CEP Final`))

# Verificando as mudanças nas colunas após o tratamento
head(ceps)
## # A tibble: 6 × 4
##   Estado Localidade      `CEP Inicial` `CEP Final`
##   <chr>  <chr>                   <int>       <int>
## 1 AC     Acrelândia              69945       69949
## 2 AC     Assis Brasil            69935       69939
## 3 AC     Brasiléia               69932       69933
## 4 AC     Bujari                  69926       69926
## 5 AC     Capixaba                69931       69931
## 6 AC     Cruzeiro do Sul         69980       69981

Atualizamos então os conjuntos de dados order_customer e sellers utilizando o CEP da coluna customer_zip_code_prefix e seller_zip_code_prefix e a função get_estado_localidade:

#Atualizando order_customer e sellers por meio do CEP da coluna zip_code_prefix.
order_customer <- order_customer %>%
  mutate(
    Estado_Localidade = map(order_customer$customer_zip_code_prefix, get_estado_localidade),
    customer_state = sapply(Estado_Localidade, function(x) x$Estado),
    customer_city = sapply(Estado_Localidade, function(x) x$Localidade)
  ) %>%
  select(-Estado_Localidade)

sellers <- sellers %>%
  mutate(
    Estado_Localidade = map(sellers$seller_zip_code_prefix, get_estado_localidade),
    seller_state = sapply(Estado_Localidade, function(x) x$Estado),
    seller_city = sapply(Estado_Localidade, function(x) x$Localidade)
  ) %>%
  select(-Estado_Localidade)

Após o tratamento, verificamos se há cidades que ficaram nulas nos conjuntos order_customer e sellers comparando com o df_municipios. Observamos que no conjunto sellers não há cidades nulas, mas no conjunto order_customer há 24 linhas onde as cidades ficaram nulas. Porém podemos ver que os CEPS não foram anulados.

#Resetar as colunas seller_city_clean e customer_city_clean com as atualizações
sellers <- sellers %>%
  mutate(seller_city_clean = tolower(stri_trans_general(seller_city, "Latin-ASCII")))

order_customer <- order_customer %>%
  mutate(customer_city_clean = tolower(stri_trans_general(customer_city, "Latin-ASCII")))

result_seller_city <- sellers %>%
  filter(!seller_city_clean %in% df_municipios$municipio_nome_clean)

head(result_seller_city)
## [1] seller_id              seller_zip_code_prefix seller_city           
## [4] seller_state           seller_city_clean     
## <0 linhas> (ou row.names de comprimento 0)
result_customer_city <- order_customer %>%
  filter(!customer_city_clean %in% df_municipios$municipio_nome_clean)

head(result_customer_city)
##                        customer_id               customer_unique_id
## 1 fe196aba95e52d084e7bb60dbd8a23f1 93b4c8bdafa9877757c77b519415d274
## 2 81eaa0c0dfb54f5f9d7d11734f162e65 b0369cc51e0e1618a76c7174132b93cc
## 3 a059473225838f7005cf82e2f6b9a18d 1df28da6a384e9822da017c6d44b0656
## 4 4560c6d8bfdd3e61555dca85d900e1e6 ad054a269e8691d8e2727d1611f3950e
## 5 110d797e5a5055538df046c2cad8f3d4 1a4d4db5393b394803df246ca68872b4
## 6 988126b4ddf725d9724e4318872ea2ae 507dc9becd4fc65635d90682dfa9d3a3
##   customer_zip_code_prefix customer_city customer_state customer_city_clean
## 1                    89130          <NA>           <NA>                <NA>
## 2                    89130          <NA>           <NA>                <NA>
## 3                    89130          <NA>           <NA>                <NA>
## 4                    89130          <NA>           <NA>                <NA>
## 5                    98900          <NA>           <NA>                <NA>
## 6                    98900          <NA>           <NA>                <NA>
length(unique(result_customer_city$customer_zip_code_prefix))
## [1] 3
unique(result_customer_city$customer_zip_code_prefix)
## [1] 89130 98900 65935

Vamos utilizar as colunas com dados nulos, utilizando o prefixo do CEP para corrigi-los manualmente. Além disso, aproveitaremos para corrigir a cidade “Bom Jesus de Goiás”, que estava com erro no início do processo, também inclui “Santa Terezinha - BA”, pelo menos motivo.

order_customer <- order_customer %>%
  mutate(customer_city_clean = case_when(
    customer_zip_code_prefix == 65935 ~ "senador la rocque",
    customer_zip_code_prefix == 89130 ~ "indaial",
    customer_zip_code_prefix == 98900 ~ "santa rosa",
    customer_zip_code_prefix == 75570 ~ "bom jesus de goias",
    customer_zip_code_prefix == 44590 ~ "santa terezinha",
    TRUE ~ customer_city_clean # Mantém o valor original se nenhuma condição for atendida
  )) %>% mutate(customer_state = case_when(
    customer_zip_code_prefix == 65935 ~ "MA",
    customer_zip_code_prefix == 89130 ~ "SC",
    customer_zip_code_prefix == 98900 ~ "RS",
    customer_zip_code_prefix == 75570 ~ "GO",
    customer_zip_code_prefix == 44590 ~ "BA",
    TRUE ~ customer_state # Mantém o valor original se nenhuma condição for atendida
  )) %>% mutate(customer_city = case_when(
    customer_zip_code_prefix == 65935 ~ "Senador La Rocque",
    customer_zip_code_prefix == 89130 ~ "Indaial",
    customer_zip_code_prefix == 98900 ~ "Santa Rosa",
    customer_zip_code_prefix == 75570 ~ "Bom Jesus de Goiás",
    customer_zip_code_prefix == 44590 ~ "Santa Terezinha",
    TRUE ~ customer_city # Mantém o valor original se nenhuma condição for atendida
  ))

Revisamos novamente se há mais inconsistências nos dados após a correção manual. Verificaremos se há cidades que ainda não correspondem corretamente aos dados do df_municipios:

# Resetando as colunas customer_city_clean e seller_city_clean com as atualizações
order_customer <- order_customer %>%
  mutate(customer_city_clean = tolower(stri_trans_general(customer_city, "Latin-ASCII")))

# Verificando as cidades que estão em order_customer mas não estão em df_municipios
result_customer_city <- order_customer %>%
  filter(!customer_city_clean %in% df_municipios$municipio_nome_clean)

head(result_customer_city)
## [1] customer_id              customer_unique_id       customer_zip_code_prefix
## [4] customer_city            customer_state           customer_city_clean     
## <0 linhas> (ou row.names de comprimento 0)
length(unique(result_customer_city$customer_city))
## [1] 0
# Verificando as cidades que estão em sellers mas não estão em df_municipios
result_seller_city <- sellers %>%
  filter(!seller_city_clean %in% df_municipios$municipio_nome_clean)

head(result_seller_city)
## [1] seller_id              seller_zip_code_prefix seller_city           
## [4] seller_state           seller_city_clean     
## <0 linhas> (ou row.names de comprimento 0)
length(unique(result_seller_city$seller_city))
## [1] 0

Iniciamos o processo de mesclagem dos conjuntos de dados da Olist para criar o conjunto de dados principal:

df <- inner_join(order_items, order_payments, by= "order_id")
## Warning in inner_join(order_items, order_payments, by = "order_id"): Detected an unexpected many-to-many relationship between `x` and `y`.
## ℹ Row 39 of `x` matches multiple rows in `y`.
## ℹ Row 85983 of `y` matches multiple rows in `x`.
## ℹ If a many-to-many relationship is expected, set `relationship =
##   "many-to-many"` to silence this warning.
df <- inner_join(df, orders, by= "order_id")
df <- inner_join(df, order_customer, by= "customer_id")
df <- inner_join(df, reviews,by= "order_id")
## Warning in inner_join(df, reviews, by = "order_id"): Detected an unexpected many-to-many relationship between `x` and `y`.
## ℹ Row 98 of `x` matches multiple rows in `y`.
## ℹ Row 15256 of `y` matches multiple rows in `x`.
## ℹ If a many-to-many relationship is expected, set `relationship =
##   "many-to-many"` to silence this warning.
df <- inner_join(df, products, by= "product_id")
df <- inner_join(df, sellers, by= "seller_id")

Para enriquecer nosso conjunto de dados com informações adicionais sobre os municípios, definimos a função get_info_municipio. Essa função busca no dataframe df_municipios informações como o nome da microrregião, mesorregião, e a região do município baseado no nome da cidade e estado.

get_info_municipio <- function(city, state) {
  # Procurar pela correspondência no dataframe df_municipios
  match_index <- which(df_municipios$municipio_nome_clean == city & df_municipios$`UF-sigla` == state)
  
  # Verificar se encontrou uma correspondência
  if (length(match_index) > 0) {
    # Se encontrou, retornar um vetor com as informações relevantes
    return(c(df_municipios$`UF-nome`[match_index],
             df_municipios$`microrregiao-nome`[match_index],
             df_municipios$`mesorregiao-nome`[match_index],
             df_municipios$`regiao-nome`[match_index],
             df_municipios$`municipio-nome`[match_index]
             ))
  } else {
    # Se não encontrou correspondência, retornar NA
    return(rep(NA, 5))
  }
}

Utilizamos essa função para adicionar informações detalhadas sobre os municípios de vendedores e clientes aos nossos dados principais. Para isso, aplicamos a função get_info_municipio aos campos de cidade e estado dos vendedores e clientes, gerando novas colunas com as informações complementares. Transformamos todas as colunas com datas no formato POSIXct para faciitar a manipulação de dados com data e tempo E por fim removemos as colunas não relevantes.

df <- df %>%
  mutate(
    info_municipio = map2(seller_city_clean, seller_state, get_info_municipio),
    seller_UF = sapply(info_municipio, function(x) x[1]),
    seller_microrregiao = sapply(info_municipio, function(x) x[2]),
    seller_mesorregiao = sapply(info_municipio, function(x) x[3]),
    seller_regiao = sapply(info_municipio, function(x) x[4]),
    seller_city = sapply(info_municipio, function(x) x[5])
  ) %>%
  select(-info_municipio)

df <- df %>%
  mutate(
    info_municipio = map2(customer_city_clean, customer_state, get_info_municipio),
    customer_UF = sapply(info_municipio, function(x) x[1]),
    customer_microrregiao = sapply(info_municipio, function(x) x[2]),
    customer_regiao = sapply(info_municipio, function(x) x[4]),
    customer_city = sapply(info_municipio, function(x) x[5])
  ) %>%
  select(-info_municipio)


names(df)
##  [1] "order_id"                      "order_item_id"                
##  [3] "product_id"                    "seller_id"                    
##  [5] "shipping_limit_date"           "price"                        
##  [7] "freight_value"                 "payment_sequential"           
##  [9] "payment_type"                  "payment_installments"         
## [11] "payment_value"                 "customer_id"                  
## [13] "order_status"                  "order_purchase_timestamp"     
## [15] "order_approved_at"             "order_delivered_carrier_date" 
## [17] "order_delivered_customer_date" "order_estimated_delivery_date"
## [19] "customer_unique_id"            "customer_zip_code_prefix"     
## [21] "customer_city"                 "customer_state"               
## [23] "customer_city_clean"           "review_id"                    
## [25] "review_score"                  "review_comment_title"         
## [27] "review_comment_message"        "review_creation_date"         
## [29] "review_answer_timestamp"       "product_category_name"        
## [31] "product_name_lenght"           "product_description_lenght"   
## [33] "product_photos_qty"            "product_weight_g"             
## [35] "product_length_cm"             "product_height_cm"            
## [37] "product_width_cm"              "seller_zip_code_prefix"       
## [39] "seller_city"                   "seller_state"                 
## [41] "seller_city_clean"             "seller_UF"                    
## [43] "seller_microrregiao"           "seller_mesorregiao"           
## [45] "seller_regiao"                 "customer_UF"                  
## [47] "customer_microrregiao"         "customer_regiao"
str(df$order_purchase_timestamp)
##  chr [1:117329] "2017-09-13 08:59:02" "2017-04-26 10:53:06" ...
df$shipping_limit_date <- as.POSIXct(df$shipping_limit_date, format="%Y-%m-%d %H:%M:%S")
df$order_purchase_timestamp <- as.POSIXct(df$order_purchase_timestamp, format="%Y-%m-%d %H:%M:%S")
df$order_approved_at <- as.POSIXct(df$order_approved_at, format="%Y-%m-%d %H:%M:%S")
df$order_delivered_carrier_date <- as.POSIXct(df$order_delivered_carrier_date, format="%Y-%m-%d %H:%M:%S")
df$order_delivered_customer_date <- as.POSIXct(df$order_delivered_customer_date, format="%Y-%m-%d %H:%M:%S")
df$order_estimated_delivery_date <- as.POSIXct(df$order_estimated_delivery_date, format="%Y-%m-%d")
df$review_creation_date <- as.POSIXct(df$review_creation_date, format="%Y-%m-%d %H:%M:%S")
df$review_answer_timestamp <- as.POSIXct(df$review_answer_timestamp, format="%Y-%m-%d %H:%M:%S")

df$order_purchase_year <- as.integer(format(df$order_purchase_timestamp, "%Y"))
df$order_purchase_month <- as.integer(format(df$order_purchase_timestamp, "%m"))
df$order_purchase_day <- as.integer(format(df$order_purchase_timestamp, "%d"))

df <- df %>%
  select(, -product_photos_qty, -product_description_lenght, -product_name_lenght, -customer_city_clean, -seller_city_clean)

Gráficos e Análise

Dataframe

Esse é o dataframe resultante após os tratamentos dos dados.

datatable(head(df, n=400), options = list(scrollX = TRUE))

O dataframe resultante possui 117329 linhas e 74 categorias únicas de produtos.

nrow(df)
## [1] 117329
length(unique(df$product_category_name))
## [1] 74

Gráficos Gerais

Abaixo está o gráfico que mostra as categorias de produtos mais compradas no geral, em todas as regiões, dos anos 2016 à 2018:

O gráfico de barras ilustra as 10 categorias de produtos mais compradas no período analisado. Observa-se que as categorias de “Cama, Mesa e Banho” lideram as compras, seguidas por “Beleza/Saúde” e “Esporte/Lazer”. As demais categorias no ranking são:

  • Cama, Mesa e Banho: 11.847 produtos
  • Beleza/Saúde: 9.944 produtos
  • Esporte/Lazer: 8.942 produtos
  • Móveis/Decoração: 8.743 produtos
  • Informática/Acessórios: 8.105 produtos
  • Utilidades Domésticas: 7.331 produtos
  • Relógios/Presentes: 6.161 produtos
  • Telefonia: 4.692 produtos
  • Ferramentas/Jardim: 4.558 produtos
  • Automotivo: 4.356 produtos
df_hist <- df %>%
  group_by(product_category_name) %>%
  summarise(num_products = n()) %>%
  arrange(desc(num_products)) %>% 
  top_n(10)  # Selecionar as 10 maiores categorias
## Selecting by num_products
df_hist <- df_hist %>%
  arrange(desc(num_products))

# Histograma de número de produtos por categoria
ggplot(df_hist, aes(x = reorder(product_category_name, num_products), y = num_products)) +
  geom_bar(stat = "identity", fill = "steelblue") +
  geom_text(aes(label = num_products), vjust = -0.5, size = 3, color = "black") + 
  labs(x = "Categoria de Produto", y = "Número de Produtos Comprados") +
  ggtitle("Top 10 Categorias de Produtos Mais Compradas") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Este gráfico de barras ilustra as cinco categorias de produtos mais comprados em cada região do Brasil. Observa-se que a região Sudeste possui o maior número de compras, seguida pelas regiões Sul e Nordeste. As categorias de produtos mais compradas em todas as regiões incluem:

  • Cama, Mesa e Banho
  • Móveis e Decoração
  • Beleza/Saúde
  • Esporte/Lazer
  • Acessórios de Informática
  • Relógios/Presentes
  • Telefonia

Analisando cada região, notamos as seguintes tendências:

  • Sudeste e Sul: A categoria mais comprada é “Cama, Mesa e Banho”.
  • Centro-Oeste, Norte e Nordeste: A categoria predominante é “Beleza/Saúde”
df_ranking <- df %>%
  group_by(customer_regiao, product_category_name) %>%
  summarise(total_pedidos = n()) %>%
  arrange(customer_regiao, desc(total_pedidos)) %>%
  group_by(customer_regiao) %>%
  mutate(rank = row_number()) %>%
  group_by(customer_regiao) %>%
  mutate(proporcao_pedidos = total_pedidos/sum(total_pedidos) * 100) %>%
  mutate(proporcao_pedidos = round(proporcao_pedidos, 2)) %>%
  filter(rank <= 5) 
## `summarise()` has grouped output by 'customer_regiao'. You can override using
## the `.groups` argument.
p <- ggplot(df_ranking, aes(x = reorder(product_category_name, total_pedidos), y = total_pedidos, fill = customer_regiao)) +
  geom_bar(stat = "identity", position = "dodge") +
  coord_flip() +
  labs(x = "Categoria de Produto", y = "Número de Pedidos", fill = "Região") +
  ggtitle("Ranking das 5 Categorias de Produtos Mais Comprados por Região") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

p_interactive <- ggplotly(p, dynamicTicks = TRUE) %>%
  layout(
    title = "Ranking de Categorias de Produtos Mais Comprados por Região",
    yaxis = list(autorange = TRUE)
  ) %>%
  config(responsive = TRUE)

p_interactive

Este gráfico de barras, assim como o gráfico acima, ilustra as cinco categorias de produtos mais comprados em cada região do Brasil, mas considerando a proporção de compras de cada categoria dentro de todas as categorias compradas na região. A proporção é calculada como a quantidade de produtos por categoria dividida pela soma de todos os produtos de todas as categorias na região.

Proporção de Compras por Categoria e Região:

  • Cama, Mesa e Banho:
    • Centro-Oeste: 8,98%
    • Sudeste: 11,17%
    • Sul: 8,97%
  • Móveis e Decoração:
    • Centro-Oeste: 6,07%
    • Sudeste: 7,51%
    • Sul: 8,81%
  • Beleza/Saúde:
    • Centro-Oeste: 9,38%
    • Nordeste: 11,07%
    • Norte: 10,21%
    • Sudeste: 8,27%
    • Sul: 7,17%
  • Esporte/Lazer:
    • Centro-Oeste: 8,12%
    • Nordeste: 7,15%
    • Norte: 8%
    • Sudeste: 7,53%
    • Sul: 8,13%
  • Acessórios de Informática:
    • Centro-Oeste: 6,29%
    • Nordeste: 6,98%
    • Norte: 8,66%
    • Sudeste: 6,79%
    • Sul: 7,47%
  • Relógios/Presentes:
    • Nordeste: 7,61%
    • Norte: 6,92%
  • Telefonia:
    • Nordeste: 6,72%
    • Norte: 6,82%

Podemos observar que no e-commerce, as categorias mais comercializadas com base no conjunto de dados diferem para cada região. Mas no geral, as categorias “Beleza/Saúde” e “Esporte/Lazer” se destacam. Para contextos específicos como na Centro-Oeste, Sudeste e Sul, as categorias “Móveis e Decoração” e “Cama, Mesa e Banho” são bem requisitadas, sugerindo uma preferência por itens de conforto e utilidade doméstica nessas regiões. No Norte e Nordeste, as categorias “Relógios/Presentes” e “Telefonia” têm uma representação significativa, indicando uma demanda por itens relacionados a presentes e comunicação. Vale destacar que essa análise reflete exclusivamente o e-commerce da Olist.

p2 <- ggplot(df_ranking, aes(x = reorder(product_category_name, proporcao_pedidos), y = proporcao_pedidos, fill = customer_regiao)) +
  geom_bar(stat = "identity", position = "dodge") +
  coord_flip() +
  labs(x = "Categoria de Produto", y = "Proporção", fill = "Região") +
  ggtitle("Percentual das 5 Categorias de Produtos Mais Comprados por Região") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

p_interactive <- ggplotly(p2, dynamicTicks = TRUE) %>%
  layout(
    title = "Ranking de Categorias de Produtos Mais Comprados por Região",
    yaxis = list(autorange = TRUE)
  ) %>%
  config(responsive = TRUE)

p_interactive

Análise por Região

gerar_grafico_categorias <- function(df, ano_especifico, regiao_especifica) {
  df_filtrado <- df %>%
    filter(order_purchase_year == ano_especifico, customer_regiao == regiao_especifica)
  
  top_categorias_mes <- df_filtrado %>%
    group_by(order_purchase_month, product_category_name) %>%
    summarise(total_pedidos = n(), .groups = 'drop') %>%
    arrange(desc(total_pedidos)) %>%
    group_by(order_purchase_month) %>%
    slice_max(total_pedidos, n = 3) %>%
    ungroup()
  
  top_categorias_mes$order_purchase_month <- factor(top_categorias_mes$order_purchase_month, levels = 1:12, labels = month.name[1:12])
  
  p <- ggplot(top_categorias_mes, aes(x = order_purchase_month, y = total_pedidos, fill = product_category_name)) +
    geom_bar(stat = "identity", position = "dodge") +
    labs(x = "Mês", y = "Total de Pedidos", fill = "Categoria", title = paste("Categorias Mais Compradas por Mês na Região", regiao_especifica, "em", ano_especifico)) +
    theme_minimal() +
    theme(axis.text.x = element_text(angle = 45, hjust = 1))
  
    p_interactive <- ggplotly(p, dynamicTicks = TRUE) %>%
    layout(
      title = list(paste("Categorias Mais Compradas por Mês na Região", regiao_especifica, "em", ano_especifico), 
                 font = list(size = 14) 
    ),
      xaxis = list(title = "Mês"),
      yaxis = list(title = "Total de Pedidos"),
      legend = list(title = list(text = "Categoria"))
    ) %>%
    config(responsive = TRUE)
  
  return(p_interactive)
}

Centro-Oeste

Vamos usar os anos 2017 e 2018 como referência, pois os anos de 2016 estão limitados ao mês de Outubro. Nessa região, podemos verificar uma tendência maior de compras de “Beleza e Saúde” de junho a agosto e de novembro a dezembro, o que se repete em 2018 com a inclusão dos meses de março, abril e maio. “Cama, Mesa e Banho” é popular na maior parte do ano. “Esporte e Lazer” tem destaque em boa parte do ano, exceto em maio e agosto. Para “Informática”, os meses em destaque são fevereiro, maio e novembro. Relógios e Presentes se destacam em março, abril, junho e julho.

gerar_grafico_categorias(df, 2016, "Centro-Oeste")
gerar_grafico_categorias(df, 2017, "Centro-Oeste")
gerar_grafico_categorias(df, 2018, "Centro-Oeste")

Nordeste

Nessa região, “Beleza e Saúde” são constantes ao longo do ano, com uma leve redução apenas em janeiro. Esporte e Lazer é mais constante nos meses de julho a outubro e também em dezembro. Para “informática”, há uma constância no começo do ano, de janeiro a abril, e em novembro. “Relógios e Presentes” têm um pico em maio e julho. Para “Telefonia”, observa-se um foco em outubro, novembro, janeiro e março.

gerar_grafico_categorias(df, 2016, "Nordeste")
gerar_grafico_categorias(df, 2017, "Nordeste")
gerar_grafico_categorias(df, 2018, "Nordeste")

Norte

Nessa região, “Beleza e Saúde” são constantes ao longo do ano, com uma leve redução apenas em setembro e outubro. “Esporte e Lazer” possuem um pico em janeiro, abril e setembro. Para “acessórios e Informática”, os pontos altos são outubro, julho e janeiro. “Relógios e Presentes” são populares em julho, novembro e janeiro. E telefonia é popular o ano todo com exceção de abril.

gerar_grafico_categorias(df, 2016, "Norte")
gerar_grafico_categorias(df, 2017, "Norte")
gerar_grafico_categorias(df, 2018, "Norte")

Sudeste

Nessa região, não há muitos dados sobre “Beleza e Saúde”, apesar de possuir o maior índice de vendas em relação às outras regiões. Em 2017, o índice é baixo por mês, exceto por um alto pico em setembro, e é constante em 2018 entre os meses de abril e agosto. “Cama, Mesa e Banho” possui mais vendas entre julho e novembro em 2017, com um pico alto em novembro, e em 2018 possui um pico alto em janeiro, indicando que as vendas são mais populares nos fins e começos de ano. Para “Esporte e Lazer”, não há muitos dados em 2018, mas em 2017 o pico foi entre outubro e novembro. Não há dados suficientes em “Informática” em 2017, só há vendas em maio, e em 2018 apenas entre janeiro e março. O pico de “Móveis e Decoração” é em novembro, e “Utilidades Domésticas” entre maio e agosto, levando em conta todos os anos.

gerar_grafico_categorias(df, 2016, "Sudeste")
gerar_grafico_categorias(df, 2017, "Sudeste")
gerar_grafico_categorias(df, 2018, "Sudeste")

Sul

Na região Sul, a categoria “Beleza e Saúde” foi mais frequente nos meses de outubro em 2016, setembro em 2017, e de junho a agosto em 2018, indicando que as vendas provavelmente ocorrem mais nesses períodos. “Cama, Mesa e Banho” é constante o ano todo, com exceção de março, e o pico de vendas ocorre em novembro e janeiro. Para “Esporte e Lazer”, o gráfico de 2017 mostra que de julho a outubro há uma grande quantidade de vendas, voltando a aparecer em dezembro. Para “Informática”, a maior frequência de vendas é em novembro e fevereiro. “Móveis e Decoração” tiveram picos em novembro de 2017 e em março e abril de 2018. “Utilidades Domésticas” registraram frequências maiores de vendas em maio e junho de 2017, e em maio, julho e agosto de 2018, indicando que as vendas ocorrem mais no meio do ano.

gerar_grafico_categorias(df, 2016, "Sul")
gerar_grafico_categorias(df, 2017, "Sul")
gerar_grafico_categorias(df, 2018, "Sul")

Conclusão

A partir dos gráficos, nota-se que a categoria “Cama, Mesa e Banho” é a mais frequente, estando presente entre as categorias mais vendidas nas regiões, dependendo do ano. Logo após, destacam-se “Beleza e Saúde” e “Esporte e Lazer”.

Em cada estado, há peculiaridades diferentes no comportamento de compra dos consumidores. No Sudeste, por exemplo, observa-se uma disparidade significativa entre os clientes que compram as cinco categorias mais populares, indicando que é o maior mercado consumidor do e-commerce da Olist. Além disso, há um aumento nas compras no mês de novembro, provavelmente devido à Black Friday. O início e o meio do ano também são períodos de destaque, possivelmente devido a datas comemorativas como o Dia das Mães (abril e maio), Dia dos Namorados, Dia dos Pais e outras datas festivas.

Esses padrões indicam a importância de adaptar o estoque para as regiões que consomem mais determinados produtos de forma sazonal. Também é essencial otimizar a logística nas áreas onde essas categorias são mais compradas para aumentar a satisfação do cliente. Planejar campanhas de marketing e promoções conforme a demanda de cada região é uma estratégia importante. Além disso, estudar formas de aumentar as vendas de categorias menos populares em determinadas regiões pode trazer benefícios.

É importante lembrar que o conjunto de dados utilizado abrange aproximadamente 100 mil ordens de venda e 117.329 produtos vendidos, limitados aos anos de 2016 a 2018. Além disso, alguns meses de 2016 e 2018 não foram coletados. Portanto, mais dados são necessários para aprofundar a análise e também verificar outros conjuntos de dados de vendas além dos da Olist.

Referências

Filho, Ademar Arvati, and Daniel Atilio. 2019. CEPs Do Brasil.” https://www.kaggle.com/datasets/arvati/lista-de-ceps-do-brasil.
Instituto Brasileiro de Geografia e Estatástica. 2024. API do IBGE.” https://servicodados.ibge.gov.br/api/docs/localidades.
Olist, and André Sionek. 2018. “Brazilian e-Commerce Public Dataset by Olist.” Kaggle. https://doi.org/10.34740/KAGGLE/DSV/195341.
Rodrigues, Gessinária Fernandes. 2021. “Comportamento Do Consumidor Online: Fatores Que Influenciam Na Decisão de Compra.”