O problema: O impacto dos atrasos logísticos na satisfação dos consumidores no e-commerce brasileiro. Por que devemos nos interessar? A logística em um país de dimensões continentais como o Brasil é um desafio imenso. Entregas atrasadas são a principal causa de avaliações negativas. Analisar isso é fundamental porque a reputação de uma loja virtual dita sua sobrevivência; entender onde e por que os atrasos ocorrem permite que empresas reduzam custos operacionais, melhorem a retenção de clientes e aumentem suas vendas a longo prazo.
Como abordar: Utilizaremos o Brazilian E-Commerce Public Dataset by Olist, que contém informações reais e anonimizadas de mais de 100 mil pedidos realizados entre 2016 e 2018. A metodologia consistirá na importação e limpeza de múltiplas tabelas relacionais (pedidos, avaliações, produtos e geolocalização). Em seguida, realizaremos uma Análise Exploratória de Dados (EDA) focada em calcular a diferença entre o tempo estimado de entrega e o tempo real, cruzando esses dados com as notas de avaliação (1 a 5 estrelas) deixadas pelos clientes.
A técnica: A principal técnica será a manipulação de dados para a criação de novas variáveis de tempo (ex: tempo_de_entrega, dias_de_atraso). Faremos a mesclagem (joins) das tabelas para conectar a nota do cliente aos dados de frete e região. Usaremos sumarizações estatísticas e visualizações gráficas (como gráficos de barras para regiões com mais atrasos e boxplots relacionando dias de atraso com a nota do pedido) para expor de forma clara (total ou parcialmente) os gargalos logísticos da plataforma.
O impacto comercial: O cliente potencial dessa análise seria um gestor de plataforma de e-commerce (como a própria Olist), gerentes de logística ou vendedores parceiros. Os insights gerados permitirão que esses clientes:
Identifiquem estados ou rotas específicas que precisam de novas parcerias de transporte.
Ajustem os prazos de entrega estimados apresentados no site para gerenciar melhor a expectativa do consumidor final.
Criem campanhas automáticas de retenção (como cupons de desconto) para clientes que o sistema identifique que sofrerão atrasos.
# Carregando todos os pacotes necessários para a análise
library(tidyverse)
library(lubridate)
library(DT)
library(plotly)
Um ecossistema de pacotes (que inclui dplyr, ggplot2 e readr) que utilizaremos para realizar a importação dos arquivos .csv da Olist, manipulação, limpeza dos dados (como junção de tabelas e criação de variáveis) e criação de gráficos estáticos.
Pacote essencial para este projeto, pois lidaremos com variáveis de tempo (datas e horas de aprovação do pedido, estimativa de entrega e entrega real). Ele nos ajudará a calcular a diferença de dias entre essas datas para descobrir os atrasos logísticos.
Utilizado para renderizar o conjunto de dados limpo e os resultados de forma condensada através de tabelas interativas e paginadas.
Será utilizado na etapa de análise exploratória para transformar nossos gráficos estáticos em visualizações interativas, permitindo explorar os dados de atraso por região com mais detalhes.
Os dados utilizados neste projeto pertencem ao conjunto Brazilian E-Commerce Public Dataset by Olist. Eles consistem em informações reais e anonimizadas de comércio eletrônico, disponibilizados publicamente pela própria Olist, a maior loja de departamentos dos marketplaces brasileiros. O repositório oficial em que os dados originais foram obtidos está hospedado na plataforma Kaggle e pode ser acessado através do seguinte hiperlink: Brazilian E-Commerce Public Dataset by Olist (Kaggle).
Os dados refletem operações comerciais reais do e-commerce brasileiro. A Olist, que atua como uma loja de departamentos conectando pequenas empresas a grandes marketplaces (como Mercado Livre, B2W, etc.), coletou esses dados através do seu sistema logístico e de vendas. O propósito original da divulgação pública desse conjunto (disponibilizado no Kaggle) foi educacional: a empresa o liberou para que a comunidade de Ciência de Dados pudesse praticar modelagem, análise de negócios e visualização com dados reais e não mascarados do mercado brasileiro.
Os dados não representam uma “fotografia” de um único momento, mas sim um registro contínuo (série histórica). As informações abrangem mais de 100 mil pedidos realizados no período entre setembro de 2016 e outubro de 2018.
Diferente de conjuntos de dados simples que possuem apenas uma tabela, os dados originais da Olist foram disponibilizados no formato de um banco de dados relacional, divididos em 9 arquivos CSV separados para garantir a integridade da informação. Ao todo, o conjunto original possui cerca de 52 variáveis (colunas) distribuídas entre essas tabelas.
Por ser um conjunto de dados do mundo real, ele possui várias peculiaridades que justificam um pré-processamento cuidadoso:
Privacidade e Anonimização: Para proteger a identidade dos clientes e vendedores parceiros, os IDs são sequências alfanuméricas mascaradas (ex: d479a6b142994afdd22a36b3db6d4f57).
Registros de Valores Ausentes em Datas: A tabela de pedidos (orders) possui muitos valores do tipo NA (ausentes) nas colunas de data de entrega. Isso não é um erro do sistema, mas uma peculiaridade do negócio: pedidos com status de “cancelado” ou “indisponível” logicamente não possuem uma data de entrega preenchida.
Valores Ausentes em Avaliações: Na tabela de avaliações, a grande maioria das variáveis de “título do comentário” e “mensagem do comentário” está vazia (registradas como valores em branco ou NA). Isso ocorre porque a plataforma permite que o usuário deixe apenas a nota em estrelas, sem a obrigatoriedade de escrever um texto.
Inconsistência de Categorias: A variável de categorias de produtos na tabela original possui nomes em português com formatações variadas (uso de underline no lugar de espaços, como cama_mesa_banho), exigindo limpeza e padronização das strings.
Para garantir que a análise do impacto dos atrasos logísticos seja precisa, segui um processo lógico de estruturação dos dados brutos:
Inicialmente, importei três tabelas principais do banco de dados (Pedidos, Itens e Avaliações) usando a função read_csv() do pacote readr. Selecionando apenas os arquivos estritamente necessários para a nossa declaração do problema, otimizando o processamento.
# 1. IMPORTAÇÃO
# Lendo os arquivos CSV originais da Olist
pedidos <- read_csv("olist_orders_dataset.csv")
itens <- read_csv("olist_order_items_dataset.csv")
avaliacoes <- read_csv("olist_order_reviews_dataset.csv")
Como a base original é relacional, realizei a junção das tabelas através da função left_join, utilizando a chave primária order_id. Executando essa atividade para unir as notas de avaliação dos clientes aos respectivos dados de frete e status de entrega, consolidando todas as informações em um único data frame analítico.
# 2. MESCLAGEM DE DADOS (JOINS)
# Unindo as três tabelas usando a chave primária "order_id"
dados_mesclados <- pedidos %>%
left_join(itens, by = "order_id") %>%
left_join(avaliacoes, by = "order_id")
Durante a inspeção inicial, notei que as variáveis de data (como order_estimated_delivery_date e order_delivered_customer_date) foram importadas no formato de texto (caracteres). Realizei a conversão dessas colunas para o formato de data/hora padrão (datetime) utilizando o pacote lubridate. Essa transformação foi fundamental, pois é preciso realizar cálculos matemáticos de subtração entre essas datas.
# 3. CONVERSÃO DE TIPOS DE DADOS
# Garantindo que as colunas são reconhecidas como datas puras
dados_limpos <- dados_mesclados %>%
mutate(
data_estimada = as.Date(order_estimated_delivery_date),
data_entrega = as.Date(order_delivered_customer_date)
)
Identifiquei a presença de valores ausentes (NA) nas colunas de data de entrega. Ao investigar o motivo, constatei que tratavam-se de pedidos com status “cancelado” ou “indisponível”. Como o foco do nosso estudo exige a medição de tempo de mercadorias que efetivamente chegaram ao destino, optei por filtrar a base, mantendo estritamente os pedidos com status delivered (entregue). Isso limpou os valores ausentes de forma lógica e justificada.
# 4. TRATAMENTO NA'S
# Filtrando pedidos entregues e removendo valores NAs na data de entrega
dados_limpos <- dados_limpos %>%
filter(order_status == "delivered") %>%
drop_na(data_entrega)
Por fim, para que a análise exploratória seja possível, criei a variável dias_de_atraso. Ela foi concebida a partir da diferença entre a data real em que o cliente recebeu o produto e a data estimada informada no momento da compra. Essa variável não existia na base de origem e é o núcleo da nossa investigação.
# 5. VARIÁVEIS
# Criando a variável de atraso com uma subtração direta e segura
dados_limpos <- dados_limpos %>%
mutate(
dias_de_atraso = as.numeric(data_entrega - data_estimada)
) %>%
select(order_id, customer_id, data_estimada, data_entrega, dias_de_atraso, review_score, price, freight_value)
Após a execução das etapas de filtragem, tratamento de dados omissos e criação de novas variáveis, o conjunto de dados final ficou estruturado de forma otimizada para a análise.
Para cumprir o requisito de não apresentar um data frame extenso
(superior a 200 linhas) e expor os dados de forma condensada, utiliza-se
o pacote DT para gerar uma tabela interativa. Abaixo é
apresentada uma pré-visualização paginada contendo as primeiras 100
observações do painel analítico limpo:
# Apresentação interativa e condensada das primeiras 100 linhas do dataset limpo
datatable(
head(dados_limpos, 100),
options = list(
pageLength = 10, # Define que cada página da tabela terá apenas 10 linhas
searchHighlight = TRUE, # Destaca visualmente o texto que for pesquisado
scrollX = TRUE # Ativa a barra de rolagem lateral caso o ecrã seja pequeno
),
rownames = FALSE, # Remove a coluna automática de índices (0, 1, 2...)
caption = "Tabela 1: Visualização condensada e interativa do conjunto de dados final."
)
Para compreender a estrutura final dos dados que guiarão a análise
exploratória, elaborou-se o dicionário de dados abaixo. Ele descreve as
principais variáveis mantidas no conjunto dados_limpos, que
são fundamentais para responder à nossa declaração do problema sobre
atrasos logísticos e satisfação do cliente.
| Variável | Tipo | Descrição |
|---|---|---|
| order_id | Caractere | Identificador único e exclusivo de cada pedido. |
| customer_id | Caractere | Identificador único do cliente que realizou a compra. |
| data_estimada | Data/Hora | Data e hora limite prometida para a entrega no momento da compra. |
| data_entrega | Data/Hora | Data e hora exata em que o produto foi entregue ao cliente. |
| dias_de_atraso | Numérico | Variável calculada: diferença em dias entre a data de entrega real e a estimada. Valores positivos indicam atraso na entrega; valores negativos indicam entrega antecipada. |
| review_score | Numérico | Nota de satisfação atribuída pelo cliente ao pedido (escala contínua de 1 a 5). |
| price | Numérico | Preço do produto adquirido (em Reais). |
| freight_value | Numérico | Valor cobrado pelo frete da mercadoria (em Reais). |
Panorama Estatístico Consolidado:
O nosso conjunto de dados limpo e filtrado possui agora um total de
110832 observações válidas (pedidos entregues) e
8 variáveis. Ao avaliar a variável central do nosso
estudo logístico (dias_de_atraso), constata-se uma média
geral de -12.03 dias de diferença em relação à
estimativa da plataforma. Em paralelo, a métrica de satisfação do
cliente (review_score) manteve sua consistência, variando
entre o mínimo de 1 e o máximo de 5
estrelas na amostra final.
Nesta etapa, investigaremos as relações entre as variáveis do nosso conjunto de dados limpo, com foco central em responder como o tempo de entrega afeta a percepção de qualidade do cliente.
A declaração do problema assume que entregas atrasadas geram
avaliações negativas. Para validar essa hipótese, criei um
boxplot que cruza a nota de avaliação do pedido (de 1 a 5
estrelas) com a nossa variável calculada de
dias_de_atraso.
Nesta visualização, valores acima de zero no eixo Y indicam que o pedido atrasou, enquanto valores abaixo de zero indicam que ele chegou adiantado.
# Removendo avaliações vazias antes de plotar
dados_grafico <- dados_limpos %>%
filter(!is.na(review_score))
# Gráfico robusto e com zoom para melhor visualização
ggplot(dados_grafico, aes(x = as.factor(review_score), y = dias_de_atraso)) +
geom_boxplot(aes(fill = as.factor(review_score)), show.legend = FALSE) +
coord_cartesian(ylim = c(-30, 20)) + # Dá o zoom para esconder os outliers extremos
labs(
title = "Relação entre Dias de Atraso e Nota de Avaliação",
x = "Nota de Avaliação (1 a 5 estrelas)",
y = "Dias de Atraso (Valores > 0 = Atraso)"
) +
theme_bw()
Para compreendermos a verdadeira dimensão do gargalo logístico apontado no gráfico anterior, é essencial visualizar a distribuição geral da nossa variável de atrasos. O histograma abaixo ilustra a concentração dos pedidos.
A linha tracejada vermelha representa o prazo exato da estimativa (dia zero). Barras posicionadas à esquerda da linha indicam eficiência (entregas realizadas antes do prazo estipulado), enquanto as barras à direita expõem o volume exato de pedidos que sofreram atraso logístico.
# Criando o histograma da distribuição de tempo de entrega
ggplot(dados_limpos, aes(x = dias_de_atraso)) +
geom_histogram(binwidth = 2, fill = "steelblue", color = "white", alpha = 0.8) +
geom_vline(xintercept = 0, color = "red", linetype = "dashed", linewidth = 1) +
coord_cartesian(xlim = c(-30, 30)) + # Focando no intervalo mais comum (1 mês de margem)
labs(
title = "Distribuição Geral do Tempo de Entrega",
x = "Dias de Diferença (Negativo = Adiantado | Positivo = Atrasado)",
y = "Volume de Pedidos"
) +
theme_minimal()
Uma dúvida comum na logística de e-commerce é se fretes mais caros
oferecem um serviço mais confiável. Para testar essa premissa,
elaboramos um gráfico de dispersão cruzando o valor pago pelo frete
(freight_value) com os dias de diferença na entrega
(dias_de_atraso).
Foi adicionada uma linha de tendência (em azul) para ajudar a identificar se existe uma correlação clara entre pagar mais caro e receber o produto mais rápido.
# Criando o gráfico de dispersão com linha de tendência
ggplot(dados_limpos, aes(x = freight_value, y = dias_de_atraso)) +
geom_point(alpha = 0.1, color = "darkorange") + # alpha = 0.1 deixa os pontos transparentes para vermos onde há acúmulo
geom_smooth(method = "lm", color = "blue", se = FALSE) + # Adiciona a linha de tendência linear
coord_cartesian(ylim = c(-30, 30), xlim = c(0, 150)) + # Focando nos fretes até R$ 150
labs(
title = "Valor do Frete vs. Dias de Atraso",
x = "Valor do Frete (R$)",
y = "Dias de Atraso (Valores > 0 = Atraso)"
) +
theme_bw()
Para consolidar as descobertas visuais das seções anteriores, geramos
uma sumarização estatística agrupando os pedidos pela nota de satisfação
(review_score).
A tabela abaixo exibe o volume de pedidos, a média de dias de diferença na entrega e o frete médio pago para cada nível de satisfação. Os números comprovam matematicamente a tendência observada no boxplot: as piores notas (1 e 2 estrelas) estão diretamente ligadas a uma média positiva (atraso real) no tempo de entrega.
# Calculando as médias por nota de avaliação
tabela_resumo <- dados_limpos %>%
filter(!is.na(review_score)) %>%
group_by(review_score) %>%
summarise(
`Total de Pedidos` = n(),
`Média de Atraso (Dias)` = round(mean(dias_de_atraso, na.rm = TRUE), 2),
`Frete Médio (R$)` = round(mean(freight_value, na.rm = TRUE), 2)
) %>%
arrange(desc(review_score)) # Ordenando da maior nota para a menor
# Apresentando o resultado em uma tabela limpa e formatada
datatable(
tabela_resumo,
options = list(dom = 't'), # 't' remove a barra de pesquisa para deixar a tabela mais limpa
rownames = FALSE,
caption = "Tabela 2: Médias de atraso e frete agrupadas por nota de avaliação."
)
Para complementar a sumarização estatística e oferecer uma visão executiva do problema, categorizamos o tempo de entrega em três cenários: “Adiantado”, “No Prazo Exato” e “Atrasado”.
O gráfico de barras abaixo exibe o percentual de representatividade de cada categoria. Esta métrica é vital para os gestores da plataforma, pois traduz o volume logístico em uma taxa de sucesso clara, permitindo avaliar se a quantidade de atrasos está dentro de uma margem aceitável para o negócio ou se representa uma crise operacional.
# Criando as categorias e calculando os percentuais
dados_status <- dados_limpos %>%
mutate(
status_entrega = case_when(
dias_de_atraso < 0 ~ "Adiantado",
dias_de_atraso == 0 ~ "No Prazo Exato",
dias_de_atraso > 0 ~ "Atrasado"
)
) %>%
count(status_entrega) %>%
mutate(percentual = n / sum(n) * 100)
# Criando o gráfico de barras com os rótulos de porcentagem
ggplot(dados_status, aes(x = reorder(status_entrega, -percentual), y = percentual, fill = status_entrega)) +
geom_col(show.legend = FALSE, alpha = 0.9) +
geom_text(aes(label = paste0(round(percentual, 1), "%")), vjust = -0.5, fontface = "bold") +
scale_fill_manual(values = c("Adiantado" = "seagreen", "No Prazo Exato" = "gray50", "Atrasado" = "tomato")) +
coord_cartesian(ylim = c(0, max(dados_status$percentual) + 10)) + # Espaço extra para o texto não cortar
labs(
title = "Proporção de Pedidos por Status de Entrega",
x = "Status da Entrega",
y = "Percentual do Volume Total (%)"
) +
theme_minimal()
Nesta seção final, consolidamos o aprendizado obtido com os dados da Olist, amarrando os resultados técnicos aos objetivos de negócios estabelecidos no início deste projeto.
A declaração de problema que norteou este projeto foi o impacto dos atrasos logísticos na satisfação dos consumidores no e-commerce brasileiro. Abordamos essa questão por entendermos que, em um país de dimensões continentais, a logística é o principal gargalo operacional das lojas virtuais. A reputação de uma marca depende diretamente do cumprimento de prazos, e entender essa dor é fundamental para a sobrevivência e lucratividade de qualquer plataforma de vendas online.
Para solucionar o problema, utilizamos o banco de dados relacional
Brazilian E-Commerce Public Dataset by Olist. A nossa
metodologia consistiu na importação e mesclagem (joins) das
tabelas de Pedidos, Itens e Avaliações de Clientes. Realizamos um
rigoroso pré-processamento para converter variáveis de texto em formatos
nativos de data, filtrando apenas os pedidos efetivamente entregues. O
núcleo técnico da nossa abordagem foi a criação da variável calculada
dias_de_atraso, que serviu como base para uma Análise
Exploratória de Dados (EDA) fundamentada em visualizações do pacote
ggplot2 e sumarizações do dplyr.
A nossa análise exploratória forneceu descobertas visuais e
estatísticas muito claras:
Atraso destrói a avaliação: O nosso boxplot e
a sumarização estatística comprovaram matematicamente que as piores
notas da plataforma (1 e 2 estrelas) possuem uma média positiva e
acentuada de dias de atraso. Em contrapartida, as notas 5 estrelas estão
concentradas em entregas antecipadas.
O Mito do Frete Caro: O gráfico de dispersão quebrou o
senso comum de que pagar mais caro pelo frete garante pontualidade.
Encontramos uma massa considerável de pedidos com fretes de alto valor
que, ainda assim, sofreram atrasos severos.
Os insights gerados possuem um alto valor prático para gestores de
plataformas como a Olist, gerentes de logística e vendedores parceiros.
Com esses dados, os clientes da análise podem:
Ajustar os algoritmos de cálculo de prazo de entrega no site,
adicionando margens de segurança para gerenciar melhor a expectativa do
consumidor.
Criar campanhas automáticas de mitigação, como o envio de cupons de
desconto ou e-mails de desculpas assim que o sistema detectar que um
pedido não chegará na data prometida.
Utilizar os dados de falha no prazo cruzados com o valor cobrado pelo
frete para auditar e penalizar transportadoras ineficientes.
É importante reconhecer certas limitações no escopo desta análise. Para garantir a otimização do processamento e focar exclusivamente no tempo de entrega concluída, filtramos apenas os pedidos com status delivered. Isso significa que deixamos de analisar os pedidos cancelados, que também afetam drasticamente a reputação da loja. Além disso, optamos por não incluir os dados de geolocalização (geolocations dataset) nesta etapa.
Como melhoria e construção em cima desta análise, trabalhos futuros poderiam integrar a tabela de CEPs para criar mapas de calor (Heatmaps), identificando visualmente quais estados ou rodovias do Brasil concentram os maiores atrasos. Outra expansão valiosa seria a aplicação de modelos de Machine Learning para prever a probabilidade de atraso de um pedido no exato momento em que o cliente o adiciona ao carrinho de compras.