Sobre o Projeto

Este projeto foi desenvolvido a partir de um desafio do clube de assinaturas Universidade dos Dados, utilizando o dataset ’Delivery Center: Food & Goods orders in Brazil, disponibilizado no kaggle por Nosbielcs.

Descrição do Projeto

Atuamos em uma empresa do setor de delivery, dentro de um time de dados centralizado que atende diversas áreas da companhia. Recentemente, recebemos quadro demandas específicas:

  1. A primeira delas veio do time de marketing, que irá lançar uma campanha para atrair mais entregadores. Nessa ação, será concedida uma bonificação aos 20 profissionais que mais percorreram distância no período analisado. Essa bonificação será variável, de acordo com o tipo de profissional (como parceiro ou fixo) e o modal de entrega utilizado (moto ou bicicleta). Nossa tarefa é identificar esses entregadores e levantar as informações necessárias para a execução da campanha.

  2. A segunda demanda partiu do time de Pricing, que está revisando a política de remuneração dos entregadores. Para embasar esse ajuste, foi solicitado um levantamento da distribuição da distância média percorrida pelos entregadores, segmentada por estado. Essa análise permitirá definir faixas de pagamento regionalizadas com base na realidade operacional de cada localidade.

  3. A terceira solicitação foi feita pelo CFO, que irá apresentar à diretoria executiva alguns indicadores financeiros da operação. Para isso, devemos fornecer quatro tabelas com os seguintes dados: a receita média e a receita total segmentadas por tipo de entrega (Food x Good), e a receita média e a receita total segmentadas por estado.

  4. A quarta demanda envolve o cálculo do bônus corporativo a ser distribuído igualmente entre os 2 mil funcionários da empresa, com base no desempenho do período coberto pelo dataset. Sabe-se que cada entrega gera um custo fixo de R$5,00, que a empresa retém 15% do valor total de cada entrega como receita, e que 20% do lucro líquido é revertido em bônus para o quadro de funcionários. A partir dessas premissas, deve-se estimar o valor que cada colaborador receberá.

Contexto do Projeto

Com seus diversos hubs operacionais espalhados pelo Brasil, o Delivery Center é uma plataforma integra lojistas e marketplaces, criando um ecossistema saudável para vendas de good (produtos) e food (comidas) no varejo brasileiro.

Atualmente temos um cadastro (catálogo + cardápio) com mais de 900 mil itens, milhares de pedidos e entregas são operacionalizados diariamente com uma rede de milhares lojistas e entregadores parceiros espalhados por todas as regiões do país.

Tudo isso gera dados e mais dados a todo momento!

Diante disso, nosso negócio está cada vez data driven, ou seja, utilizando dados para tomar decisões e numa visão de futuro sabemos que utilizar os dados de forma inteligente pode ser o nosso grande diferencial no mercado.

Além disso, dados não possuem a completude de toda operação do Delivery Center e algumas informações foram anonimizadas devido ao nosso tratamento com a Lei Geral de Proteção de Dados (LGPD).

Chamando os pacotes necessários para realizar o desafio

library(tidyverse)
## Warning: pacote 'tidyverse' foi compilado no R versão 4.4.3
## Warning: pacote 'ggplot2' foi compilado no R versão 4.4.3
## Warning: pacote 'readr' foi compilado no R versão 4.4.3
## Warning: pacote 'dplyr' foi compilado no R versão 4.4.3
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.5.2     ✔ tibble    3.2.1
## ✔ lubridate 1.9.3     ✔ tidyr     1.3.1
## ✔ purrr     1.0.2     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(ggplot2)
library(forcats)
library(RColorBrewer)
library(patchwork)
## Warning: pacote 'patchwork' foi compilado no R versão 4.4.2
library(scales)
## Warning: pacote 'scales' foi compilado no R versão 4.4.2
## 
## Anexando pacote: 'scales'
## 
## O seguinte objeto é mascarado por 'package:purrr':
## 
##     discard
## 
## O seguinte objeto é mascarado por 'package:readr':
## 
##     col_factor
library(extrafont)
## Registering fonts with R

Importando as bases de dados

drivers = read.csv("C:/Users/TutuSurfer/Desktop/Desafio Universidade dos Dados/drivers.csv", sep = ',')

chanels = read.csv("C:/Users/TutuSurfer/Desktop/Desafio Universidade dos Dados/channels.csv", sep = ',')

deliveries = read.csv("C:/Users/TutuSurfer/Desktop/Desafio Universidade dos Dados/deliveries.csv", sep = ',')

hubs = read.csv("C:/Users/TutuSurfer/Desktop/Desafio Universidade dos Dados/hubs.csv", sep = ',')

orders = read.csv("C:/Users/TutuSurfer/Desktop/Desafio Universidade dos Dados/orders.csv", sep = ',')

payments = read.csv("C:/Users/TutuSurfer/Desktop/Desafio Universidade dos Dados/payments.csv", sep = ',')

stores = read.csv("C:/Users/TutuSurfer/Desktop/Desafio Universidade dos Dados/stores.csv", sep = ',')

Análise Exploratória dos Dados

Canais

Este dataset possui informações sobre os canais de venda (marketplaces) onde são vendidos os good e food de nossos lojistas.

## Analisando a estrutura dos dados (dimensão da base de dados e tipo das colunas)
glimpse(chanels)
## Rows: 40
## Columns: 3
## $ channel_id   <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 17, 20, 21…
## $ channel_name <chr> "OTHER PLACE", "PHONE PLACE", "WHATS PLACE", "FACE PLACE"…
## $ channel_type <chr> "OWN CHANNEL", "OWN CHANNEL", "OWN CHANNEL", "OWN CHANNEL…
## vendo os dados
head(chanels, 5)
## Verificando se existem dados ausentes
colSums(is.na(chanels))
##   channel_id channel_name channel_type 
##            0            0            0
## Verificando se existem dados duplicados
sum(duplicated(chanels))
## [1] 0
## verificando a distribuição dos dados
chanels %>%
  group_by(channel_type) %>%
  summarise(distribuição_canais = n()) %>%
  mutate(proporção_canal = distribuição_canais / sum(distribuição_canais))

Entregas

Este dataset possui informações sobre as entregas realizadas por nossos entregadores parceiros.

## Analisando a estrutura dos dados (dimensão da base de dados e tipo das colunas)
glimpse(deliveries)
## Rows: 378,843
## Columns: 5
## $ delivery_id              <int> 2174658, 2174660, 2174661, 2174663, 2174675, …
## $ delivery_order_id        <int> 68413340, 68414309, 68416230, 68412721, 68414…
## $ driver_id                <int> 8378, 2473, 7615, 8378, 10463, 16430, 14513, …
## $ delivery_distance_meters <int> 5199, 410, 3784, 5714, 3746, 3924, 2489, 2564…
## $ delivery_status          <chr> "DELIVERED", "DELIVERED", "DELIVERED", "DELIV…
## visualizando os dados
head(deliveries, 10)
## Verificando se existem dados ausentes
colSums(is.na(deliveries))
##              delivery_id        delivery_order_id                driver_id 
##                        0                        0                    15886 
## delivery_distance_meters          delivery_status 
##                       73                        0
## Verificando se existem dados duplicados
sum(duplicated(deliveries))
## [1] 0
## Verificando se existem ids duplicados
sum(duplicated((deliveries$delivery_order_id)))
## [1] 20189
## Verificando a estatística descritiva das variáveis
summary(deliveries)
##   delivery_id      delivery_order_id    driver_id     delivery_distance_meters
##  Min.   :2174658   Min.   :68409030   Min.   :  133   Min.   :      0         
##  1st Qu.:2405589   1st Qu.:76312978   1st Qu.: 7615   1st Qu.:   1184         
##  Median :2637111   Median :83083209   Median :18754   Median :   2073         
##  Mean   :2634216   Mean   :82204223   Mean   :21002   Mean   :  10721         
##  3rd Qu.:2860335   3rd Qu.:87879715   3rd Qu.:31050   3rd Qu.:   3507         
##  Max.   :3144739   Max.   :93139817   Max.   :66459   Max.   :7251291         
##                                       NA's   :15886   NA's   :73              
##  delivery_status   
##  Length:378843     
##  Class :character  
##  Mode  :character  
##                    
##                    
##                    
## 
## verificando a presença de outliers
ggplot(deliveries, aes(x = delivery_distance_meters)) +
  geom_boxplot(fill = "skyblue", color = "black") +
  labs(
    title = "Boxplot da Distância Percorrida Pelos Entregadores",
    x = "Distância em Metros"
  ) +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(hjust = 0.5, face = "bold", size = 15, margin = margin(b = 12))
  )
## Warning: Removed 73 rows containing non-finite outside the scale range
## (`stat_boxplot()`).

* Não faz nenhum sentido que entregadores tenham percorrido mais de 1000 km (1e+06 metros). Portanto, irei remover os outliers de ‘delivery_distance_meters’

## Obtendo os intervalos interquartis
Q1 = quantile(deliveries$delivery_distance_meters, 0.25, na.rm = TRUE)
Q3 = quantile(deliveries$delivery_distance_meters, 0.75, na.rm = TRUE)
IQR = Q3 - Q1

## Calculando os limites inferior e superior
limite_inf = Q1 - (1.5 * IQR)
limite_sup = Q3 + (1.5 * IQR)

## Obtendo os outliers
outliers = deliveries %>%
  filter(delivery_distance_meters < limite_inf | delivery_distance_meters > limite_sup)

## verificando os outliers
head(outliers, 10)
## Removendo os outliers
entregas_sem_outliers = deliveries %>%
  filter(delivery_distance_meters >= limite_inf & delivery_distance_meters <= limite_sup)

## Verificando quantas linhas foram excluídas
print(paste('Número de Linhas Excluídas (outliers):', nrow(deliveries) - nrow(entregas_sem_outliers)))
## [1] "Número de Linhas Excluídas (outliers): 19476"
## Verificando a estatística descritiva da base de entregas sem outliers
summary(entregas_sem_outliers)
##   delivery_id      delivery_order_id    driver_id     delivery_distance_meters
##  Min.   :2174658   Min.   :68409030   Min.   :  133   Min.   :   0            
##  1st Qu.:2401609   1st Qu.:76244690   1st Qu.: 7549   1st Qu.:1138            
##  Median :2633036   Median :82953958   Median :18487   Median :1966            
##  Mean   :2631835   Mean   :82147886   Mean   :20831   Mean   :2293            
##  3rd Qu.:2859558   3rd Qu.:87874756   3rd Qu.:30950   3rd Qu.:3196            
##  Max.   :3144739   Max.   :93139817   Max.   :66459   Max.   :6991            
##                                       NA's   :11474                           
##  delivery_status   
##  Length:359367     
##  Class :character  
##  Mode  :character  
##                    
##                    
##                    
## 
## Visualizando a distribuição das distâncias percorridas sem os outliers
ggplot(entregas_sem_outliers, aes(x = delivery_distance_meters)) +
  geom_boxplot(fill = "orange", color = "black") +
  labs(
    title = "Boxplot da Distância Percorrida Pelos Entregadores",
    x = "Distância em Metros"
  ) +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(hjust = 0.5, face = "bold", size = 15, margin = margin(b = 12))
  )

* Agora os dados de distância percorrida estão fazendo muito mais sentido dentro do contexto da análise. Se tratando de cidades grandes, é totalmente plausível que um entregador percorra cerca de 7km.

## Verificando a distribuição de entregas realizadas e entregas não realizadas
entregas_sem_outliers %>%
  group_by(delivery_status) %>%
  summarise('número_entregas' = n())
## Mantendo na base apenas as entregas que foram realizadas
entregas_sem_outliers = entregas_sem_outliers %>%
  filter(delivery_status == 'DELIVERED')

## VISUALIZANDO O RESULTADO
head(entregas_sem_outliers, 5)

Motoristas

Este dataset possui informações sobre os entregadores parceiros. Eles ficam em nossos hubs e toda vez que um pedido é processado, são eles fazem as entregas na casa dos consumidores.

## Analisando a estrutura dos dados (dimensão da base de dados e tipo das colunas)
glimpse(drivers)
## Rows: 4,824
## Columns: 3
## $ driver_id    <int> 133, 138, 140, 143, 148, 165, 172, 174, 187, 196, 202, 21…
## $ driver_modal <chr> "MOTOBOY", "MOTOBOY", "MOTOBOY", "BIKER", "MOTOBOY", "MOT…
## $ driver_type  <chr> "LOGISTIC OPERATOR", "FREELANCE", "FREELANCE", "FREELANCE…
## visualizando os dados
head(drivers, 10)
## Verificando se existem dados ausentes
colSums(is.na(drivers))
##    driver_id driver_modal  driver_type 
##            0            0            0
## Verificando se existem dados duplicados
sum(duplicated(drivers))
## [1] 0
## Verificando a distribuição por tipo de veículo
drivers %>%
  group_by(driver_modal) %>%
  summarise('quantidade_motoristas_veículo' = n())
## Verificando a distribuição por tipo de contrato
drivers %>%
  group_by(driver_type) %>%
  summarise('quantidade_motoristas_tipo' = n())

Centros Logísticos

Este dataset possui informações sobre os hubs do Delivery Center. Entenda que os Hubs são os centros de distribuição dos pedidos e é dali que saem as entregas.

## Analisando a estrutura dos dados (dimensão da base de dados e tipo das colunas)
glimpse(hubs)
## Rows: 32
## Columns: 6
## $ hub_id        <int> 2, 3, 4, 5, 8, 13, 16, 17, 18, 20, 21, 22, 25, 26, 27, 2…
## $ hub_name      <chr> "BLUE SHOPPING", "GREEN SHOPPING", "RED SHOPPING", "FUNK…
## $ hub_city      <chr> "PORTO ALEGRE", "PORTO ALEGRE", "PORTO ALEGRE", "RIO DE …
## $ hub_state     <chr> "RS", "RS", "RS", "RJ", "RJ", "RJ", "RJ", "SP", "RJ", "R…
## $ hub_latitude  <dbl> -30.04741, -30.03741, -30.02195, -23.00075, -22.92148, -…
## $ hub_longitude <dbl> -51.21351, -51.20352, -51.20838, -43.31828, -43.23477, -…
## visualizando os dados
head(hubs, 10)
## Verificando se existem dados ausentes
colSums(is.na(hubs))
##        hub_id      hub_name      hub_city     hub_state  hub_latitude 
##             0             0             0             0             0 
## hub_longitude 
##             0
## Verificando se existem dados duplicados
sum(duplicated(hubs))
## [1] 0
## Verificando a quantidade de Centros por Estado
hubs %>%
  group_by(hub_state) %>%
  summarise('Centros_por_Estado' = n()) %>%
  arrange(desc(Centros_por_Estado))
## Removendo as colunas que não são relevantes para o projeto
hubs = hubs %>%
  select(hub_id, hub_state)

## Verificando o resultado
head(hubs, 5)

Pedidos

Este dataset possui informações sobre as vendas processadas através da plataforma do Delivery Center.

## Analisando a estrutura dos dados (dimensão da base de dados e tipo das colunas)
glimpse(orders)
## Rows: 368,999
## Columns: 29
## $ order_id                          <int> 68405119, 68405123, 68405206, 684054…
## $ store_id                          <int> 3512, 3512, 3512, 3401, 3401, 786, 1…
## $ channel_id                        <int> 5, 5, 5, 5, 5, 5, 5, 35, 35, 5, 5, 5…
## $ payment_order_id                  <int> 68405119, 68405123, 68405206, 684054…
## $ delivery_order_id                 <int> 68405119, 68405123, 68405206, 684054…
## $ order_status                      <chr> "CANCELED", "CANCELED", "CANCELED", …
## $ order_amount                      <dbl> 62.70, 62.70, 115.50, 55.90, 37.90, …
## $ order_delivery_fee                <dbl> 0.00, 0.00, 0.00, 0.00, 0.00, 9.90, …
## $ order_delivery_cost               <dbl> NA, NA, NA, NA, NA, NA, NA, 0.00, 6.…
## $ order_created_hour                <int> 0, 0, 0, 0, 0, 0, 0, 1, 2, 13, 13, 1…
## $ order_created_minute              <int> 1, 4, 13, 19, 26, 56, 56, 56, 32, 57…
## $ order_created_day                 <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …
## $ order_created_month               <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …
## $ order_created_year                <int> 2021, 2021, 2021, 2021, 2021, 2021, …
## $ order_moment_created              <chr> "1/1/2021 12:01:36 AM", "1/1/2021 12…
## $ order_moment_accepted             <chr> "", "", "", "", "", "", "", "1/1/202…
## $ order_moment_ready                <chr> "", "", "", "", "", "", "", "", "1/2…
## $ order_moment_collected            <chr> "", "", "", "", "", "", "", "", "1/2…
## $ order_moment_in_expedition        <chr> "", "", "", "", "", "", "", "", "1/2…
## $ order_moment_delivering           <chr> "", "", "", "", "", "", "", "", "1/2…
## $ order_moment_delivered            <chr> "", "", "", "", "", "", "", "", "", …
## $ order_moment_finished             <chr> "", "", "", "", "", "", "", "", "1/2…
## $ order_metric_collected_time       <dbl> NA, NA, NA, NA, NA, NA, NA, NA, 6.63…
## $ order_metric_paused_time          <dbl> NA, NA, NA, NA, NA, NA, NA, NA, 4.55…
## $ order_metric_production_time      <dbl> NA, NA, NA, NA, NA, NA, NA, NA, 2391…
## $ order_metric_walking_time         <dbl> NA, NA, NA, NA, NA, NA, NA, NA, 7.17…
## $ order_metric_expediton_speed_time <dbl> NA, NA, NA, NA, NA, NA, NA, NA, 11.7…
## $ order_metric_transit_time         <dbl> NA, NA, NA, NA, NA, NA, NA, NA, 21.7…
## $ order_metric_cycle_time           <dbl> NA, NA, NA, NA, NA, NA, NA, NA, 2424…
## visualizando os dados
head(orders, 10)
## Mantendo apenas as colunas relevantes na base 'orders'
orders = orders %>%
  select(order_id, store_id, channel_id, payment_order_id, delivery_order_id, order_status, order_moment_created, order_moment_finished)

## visualizando o resultado
head(orders, 5)
## Verificando se existem dados ausentes
colSums(is.na(orders))
##              order_id              store_id            channel_id 
##                     0                     0                     0 
##      payment_order_id     delivery_order_id          order_status 
##                     0                     0                     0 
##  order_moment_created order_moment_finished 
##                     0                     0
## Verificando se existem dados duplicados
sum(duplicated(orders))
## [1] 0
## Analisando a distribuição do Status de Pedidos
orders %>%
  group_by(order_status) %>%
  summarise('Pedidos_por_Status' = n())
## Mantendo apenas os pedidos finalizados
pedidos_finalizados = orders %>%
  filter(order_status == 'FINISHED')

## visualizando
pedidos_finalizados
## Verificando a estatística descritiva das variáveis
summary(orders)
##     order_id           store_id      channel_id     payment_order_id  
##  Min.   :68405119   Min.   :   3   Min.   : 1.000   Min.   :68405119  
##  1st Qu.:76355221   1st Qu.: 415   1st Qu.: 5.000   1st Qu.:76355221  
##  Median :83245994   Median : 707   Median : 5.000   Median :83245994  
##  Mean   :82307261   Mean   :1198   Mean   : 7.838   Mean   :82307261  
##  3rd Qu.:88030551   3rd Qu.:1528   3rd Qu.: 5.000   3rd Qu.:88030551  
##  Max.   :93139817   Max.   :4679   Max.   :49.000   Max.   :93139817  
##  delivery_order_id  order_status       order_moment_created
##  Min.   :68405119   Length:368999      Length:368999       
##  1st Qu.:76355221   Class :character   Class :character    
##  Median :83245994   Mode  :character   Mode  :character    
##  Mean   :82307261                                          
##  3rd Qu.:88030551                                          
##  Max.   :93139817                                          
##  order_moment_finished
##  Length:368999        
##  Class :character     
##  Mode  :character     
##                       
##                       
## 

Pagamentos

Este dataset possui informações sobre os pagamentos realizados ao Delivery Center.

## Analisando a estrutura dos dados (dimensão da base de dados e tipo das colunas)
glimpse(payments)
## Rows: 400,834
## Columns: 6
## $ payment_id       <int> 4427917, 4427918, 4427941, 4427948, 4427955, 4427956,…
## $ payment_order_id <int> 68410055, 68410055, 68412721, 68413340, 68414018, 684…
## $ payment_amount   <dbl> 118.44, 394.81, 206.95, 58.80, 45.80, 106.80, 57.80, …
## $ payment_fee      <dbl> 0.00, 7.90, 5.59, 1.59, 0.92, 2.88, 1.56, 0.40, 3.12,…
## $ payment_method   <chr> "VOUCHER", "ONLINE", "ONLINE", "ONLINE", "ONLINE", "O…
## $ payment_status   <chr> "PAID", "PAID", "PAID", "PAID", "PAID", "PAID", "PAID…
## Visualizando os dados
payments
## Verificando a existência de dados ausentes
colSums(is.na(payments))
##       payment_id payment_order_id   payment_amount      payment_fee 
##                0                0                0              175 
##   payment_method   payment_status 
##                0                0
## Verificando dados duplicados
sum(duplicated(payments))
## [1] 0
## verificando se existem ids duplicados
sum(duplicated(payments$payment_order_id))
## [1] 50500
## Verificando as estatísticas descritivas dos pagamentos
summary(payments)
##    payment_id      payment_order_id   payment_amount       payment_fee      
##  Min.   :4427917   Min.   :68410055   Min.   :     0.00   Min.   :   0.000  
##  1st Qu.:4690206   1st Qu.:76517588   1st Qu.:    32.70   1st Qu.:   0.440  
##  Median :4955490   Median :83251962   Median :    65.88   Median :   1.040  
##  Mean   :4951200   Mean   :82341814   Mean   :    93.09   Mean   :   1.881  
##  3rd Qu.:5211231   3rd Qu.:88025313   3rd Qu.:   121.40   3rd Qu.:   2.710  
##  Max.   :5540806   Max.   :93139817   Max.   :100000.11   Max.   :2000.000  
##                                                           NA's   :175       
##  payment_method     payment_status    
##  Length:400834      Length:400834     
##  Class :character   Class :character  
##  Mode  :character   Mode  :character  
##                                       
##                                       
##                                       
## 
## verificando a presença de outliers
ggplot(payments, aes(x = payment_amount)) +
  geom_boxplot(fill = "#A8D5BA", color = "black") +
  labs(
    title = "Boxplot do Valor dos Pagamentos",
    x = "Pagamento (R$)"
  ) +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(hjust = 0.5, face = "bold", size = 15, margin = margin(b = 12))
  )

## Obtendo os intervalos interquartis
Q1 = quantile(payments$payment_amount, 0.25, na.rm = TRUE)
Q3 = quantile(payments$payment_amount, 0.75, na.rm = TRUE)
IQR = Q3 - Q1

## Calculando os limites inferior e superior
limite_inf = Q1 - (1.5 * IQR)
limite_sup = Q3 + (1.5 * IQR)

## Obtendo os outliers
outliers_pagamentos = payments %>%
  filter(payment_amount < limite_inf | payment_amount > limite_sup)

## verificando os outliers
outliers_pagamentos %>%
  arrange(desc(payment_amount))
## Verificando a distribuição de classes da coluna 'payment_status'
payments %>%
  group_by(payment_status) %>%
  summarise(numero_pagamentos = n())
## Mantendo apenas os pagamentos que foram de fato pagos
pagamentos_feitos = payments %>%
  filter(payment_status == 'PAID')

## Visualizando
head(pagamentos_feitos, 5)

Lojas

Este dataset possui informações sobre os lojistas. Eles utilizam a Plataforma do Delivery Center para vender seus itens (good e/ou food) nos marketplaces.

## Analisando a estrutura dos dados (dimensão da base de dados e tipo das colunas)
glimpse(stores)
## Rows: 951
## Columns: 7
## $ store_id         <int> 3, 6, 8, 53, 54, 56, 58, 82, 83, 84, 85, 88, 89, 90, …
## $ hub_id           <int> 2, 3, 3, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 5, 8, 8,…
## $ store_name       <chr> "CUMIURI", "PIMGUCIS DA VIVA ", "RASMUR S ", "PAPA SU…
## $ store_segment    <chr> "FOOD", "FOOD", "FOOD", "FOOD", "FOOD", "FOOD", "FOOD…
## $ store_plan_price <dbl> 0.0, 0.0, 0.0, 0.0, 0.0, 49.0, 49.0, 0.0, 0.0, 49.0, …
## $ store_latitude   <dbl> NA, -30.03741, -30.03741, -22.92148, -22.92148, -22.9…
## $ store_longitude  <dbl> NA, -51.20352, -51.20352, -43.23482, -43.23482, -43.2…
## visualizando os dados
stores
## Verificando se existem dados ausentes
colSums(is.na(stores))
##         store_id           hub_id       store_name    store_segment 
##                0                0                0                0 
## store_plan_price   store_latitude  store_longitude 
##              115               16               16
## Verificando se existem dados duplicados
sum(duplicated(stores))
## [1] 0
## Verificando a quantidade de Centros por Estado
stores %>%
  group_by(store_segment) %>%
  summarise('Lojas por Tipo' = n())
## Removendo as colunas que não são relevantes para o projeto
stores = stores %>%
  select(store_id, hub_id, store_segment, store_latitude, store_longitude)

## Verificando o resultado
head(stores, 5)

Agregando as bases

## agregando os dados de pagamentoque contém o mesmo payment_order_id
pagamentos_agrupados = pagamentos_feitos %>%
  group_by(payment_order_id) %>%
  summarise(
    valor_total = sum(payment_amount, na.rm = TRUE),
    taxa_pagamento = sum(payment_fee, na.rm = TRUE),
    payment_status = first(payment_status),  # ou unique() se quiser validar depois
    .groups = 'drop'
  )


## Agregando os dados de entregas que contém o mesmo delivery_order_id
entregas_agrupadas = entregas_sem_outliers %>%
  group_by(delivery_order_id, driver_id) %>%
  summarise(
    viagens = n(),
    distancia_total_percorrida = sum(delivery_distance_meters, na.rm = TRUE),
    delivery_status = first(delivery_status),  
    .groups = "drop"
  )


## Calculando o total de viagens por delivery_order_id (somando todos os motoristas)
total_viagens_por_order = entregas_agrupadas %>%
  group_by(delivery_order_id) %>%
  summarise(total_viagens = sum(viagens), .groups = "drop")

# Adicionando esse total na base de entregas
entregas_com_total = entregas_agrupadas %>%
  left_join(total_viagens_por_order, by = "delivery_order_id")

# Calculando o peso de cada motorista (proporção das viagens)
entregas_com_total = entregas_com_total %>%
  mutate(peso_motorista = viagens / total_viagens)

# Juntando a base de entregas com com pagamentos
base_entregas = entregas_com_total %>%
  left_join(pagamentos_agrupados, by = c("delivery_order_id" = "payment_order_id")) %>%
  mutate(
    valor_proporcional = valor_total * peso_motorista,
    taxa_proporcional = taxa_pagamento * peso_motorista
  )

## juntando a base de entregadores na base de entregas
base_entregas = base_entregas %>%
  left_join(drivers, by = c('driver_id' = 'driver_id'))

## Juntando a base orders na base_entregas
base_entregas = base_entregas %>%
  left_join(pedidos_finalizados, by = c('delivery_order_id' = 'delivery_order_id'))

## juntando com a base de lojas
base_entregas = base_entregas %>%
  left_join(stores, by = ('store_id' = 'store_id'))

## juntando com a base de hubs
base_entregas = base_entregas %>%
  left_join(hubs, by = ('hub_id' = 'hub_id'))

## calculando receita, custo e lucro de cada entrega
base_entregas = base_entregas %>%
  mutate(
    receita = (valor_proporcional * 0.15),
    custo = (5 * total_viagens),
    lucro = receita - custo
  )

## ## Removendo as colunas que não são mais necessárias e organizando as colunas restantes
base_entregas = base_entregas %>%
  select(delivery_order_id, driver_id, payment_order_id,
         delivery_status, order_status, payment_status, 
         distancia_total_percorrida, total_viagens, valor_proporcional, taxa_proporcional, receita, custo, lucro,
         order_moment_created, order_moment_finished,
         driver_modal, driver_type, store_segment, hub_state, store_latitude, store_longitude
         ) %>%
  rename(valor = valor_proporcional, 
         taxa = taxa_proporcional)


## visualizando o resultado
head(base_entregas)
## Verificando se existem dados ausentes
colSums(is.na(base_entregas))
##          delivery_order_id                  driver_id 
##                          0                       5239 
##           payment_order_id            delivery_status 
##                       2102                          0 
##               order_status             payment_status 
##                       2102                       4274 
## distancia_total_percorrida              total_viagens 
##                          0                          0 
##                      valor                       taxa 
##                       4274                       4274 
##                    receita                      custo 
##                       4274                          0 
##                      lucro       order_moment_created 
##                       4274                       2102 
##      order_moment_finished               driver_modal 
##                       2102                       5239 
##                driver_type              store_segment 
##                       5239                       2102 
##                  hub_state             store_latitude 
##                       2102                       3030 
##            store_longitude 
##                       3030
## Removendo dados que não possuem status de pagamento e o valor pago (valor)
base_entregas = base_entregas %>%
  filter(!is.na(payment_status))

Top 20 Motoboys que percorreram as maiores distâncias

## criando uma base com os 20 motoristas que mais rodaram
top_20_moto = base_entregas %>%
  filter(!is.na(driver_id) & driver_modal == 'MOTOBOY') %>%
  group_by(driver_id) %>%
  summarise(
    distancia_total_motorista = sum(distancia_total_percorrida , na.rm = TRUE),
    driver_modal = first(driver_modal),
    driver_type = first(driver_type),
    .groups = 'drop'
  ) %>%
  arrange(desc(distancia_total_motorista)) %>%
  slice_head(n = 20)


## visualizando o resultado
top_20_moto

Top 20 Bikers que percorreram as maiores distâncias

## Visualizando o top 20 para bicicletas
top_20_bike = base_entregas %>%
  filter(!is.na(driver_id) & driver_modal == 'BIKER') %>%
  group_by(driver_id) %>%
  summarise(
    distancia_total_motorista = sum(distancia_total_percorrida , na.rm = TRUE),
    driver_modal = first(driver_modal),
    driver_type = first(driver_type),
    .groups = 'drop'
  ) %>%
  arrange(desc(distancia_total_motorista)) %>%
  slice_head(n = 20)

## visualizando o resultado
top_20_bike

Distâncias Motoboys

## calculando a distância média percorrida por motoqueiros em cada estado
motoqueiros_estado = base_entregas %>%
  filter(driver_modal == "MOTOBOY" & !is.na(distancia_total_percorrida) & !is.na(hub_state)) %>%
  group_by(hub_state) %>%
  summarise(
    distancia_media_motoqueiros = mean(distancia_total_percorrida, na.rm = TRUE),
    distancia_total_motoqueiros = sum(distancia_total_percorrida, na.rm = TRUE),
    n_entregas = sum(total_viagens),
    .groups = 'drop'
  ) %>%
   mutate(
    distancia_total_km = distancia_total_motoqueiros / 1000,
    posicao_label = ifelse(distancia_total_km < 100000, -0.5, 1.1),  # ajusta com base no comprimento
    hjust_label = ifelse(distancia_total_km < 100000, -0.1, 1.1)  # direita ou esquerda
  ) 


## calculando a distância média percorrida por motoqueiros em cada estado
bikers_estado = base_entregas %>%
  filter(driver_modal == "BIKER" & !is.na(distancia_total_percorrida) & !is.na(hub_state)) %>%
  group_by(hub_state) %>%
  summarise(
    distancia_media_bikers = mean(distancia_total_percorrida, na.rm = TRUE),
    distancia_total_bikers = sum(distancia_total_percorrida, na.rm = TRUE),
    n_entregas = sum(total_viagens),
    .groups = 'drop'
  ) %>%
  mutate(
    distancia_total_km = distancia_total_bikers / 1000,
    posicao_label = ifelse(distancia_total_km < 10000, -0.5, 1.1),  # ajusta com base no comprimento
    hjust_label = ifelse(distancia_total_km < 10000, -0.1, 1.1)  # direita ou esquerda
  )
## Gerando o gráfico de barras para as distâncias médias percorridas de moto em cada estado
g1 = ggplot(motoqueiros_estado, 
       aes(x = fct_reorder(hub_state, distancia_media_motoqueiros), 
           y = distancia_media_motoqueiros / 1000, 
           fill = hub_state)) +  # <-- divisão feita aqui
  geom_col() +
  geom_text(aes(label = paste0(round(distancia_media_motoqueiros / 1000, 1), " km")),  # <-- aqui também
            hjust = 1.1, size = 4) +
  labs(
    title = "Distância Média (Quilômetros)",
    x = NULL,
    y = NULL
  ) +
  coord_flip() + 
  scale_fill_brewer(palette = "Set2") +
  theme_classic(base_size = 14) +
  theme(
    legend.position = 'none',
    axis.text.x = element_blank(),
    axis.ticks.x = element_blank(),
    plot.title = element_text(face = "bold", size = 13, hjust = 0.5, lineheight = 1.1),
    axis.title = element_text(size = 12),
    axis.text = element_text(size = 12)
  )


## Gerando o gráfico de barras para as distâncias totais percorridas de moto em cada estado
g2 = ggplot(motoqueiros_estado, 
       aes(x = fct_reorder(hub_state, distancia_total_motoqueiros), 
           y = distancia_total_motoqueiros / 1000,  # conversão aqui
           fill = hub_state)) +
  geom_col() +
  geom_text(aes(label = paste0(round(distancia_total_motoqueiros / 1000, 1), " km"),  # e aqui também
            hjust = hjust_label),
            size = 4) +
  labs(
    title = "Distância Total (Quilômetros)",
    x = NULL,
    y = NULL
  ) +
  coord_flip() + 
  scale_fill_brewer(palette = "Set2") +
  theme_classic(base_size = 14) +
  theme(
    legend.position = 'none',
    axis.text.x = element_blank(),
    axis.ticks.x = element_blank(),
    plot.title = element_text(face = "bold", size = 13, hjust = 0.5, lineheight = 1.1),
    axis.title = element_text(size = 12),
    axis.text = element_text(size = 12)
  )

## Visualizando os dois gráficos juntos
(g1 | g2) +
  plot_annotation(
    title = "Distâncias Percorridas por Motoboys",
    theme = theme(
      plot.title = element_text(face = "bold", size = 18, hjust = 0.5)
    )
  )

* A leitura conjunta dos dois gráficos mostra que, enquanto RS e PR possuem maiores distâncias médias por entrega, são SP e RJ que concentram o volume operacional dos motoboys, com muito mais entregas sendo realizadas. SP, em particular, se destaca como o principal polo de entregas com motocicleta, mesmo com menor distância média — o que sugere alta densidade de pedidos em regiões urbanas compactas.

## Gerando o gráfico de barras para as distâncias médias percorridas de bike em cada estado
g3 = ggplot(bikers_estado, 
       aes(x = fct_reorder(hub_state, distancia_media_bikers), 
           y = distancia_media_bikers / 1000,  # conversão para km
           fill = hub_state)) +
  geom_col() +
  geom_text(aes(label = paste0(round(distancia_media_bikers / 1000, 1), " km")), 
            hjust = 1.1, size = 4) +
  labs(
    title = "Distância Média (Quilômetros)",
    x = NULL,
    y = NULL
  ) +
  coord_flip() + 
  scale_fill_brewer(palette = "Accent") +
  theme_classic(base_size = 14) +
  theme(
    legend.position = 'none',
    axis.text.x = element_blank(),
    axis.ticks.x = element_blank(),
    plot.title = element_text(face = "bold", size = 16, hjust = 0, lineheight = 1.1),
    axis.title = element_text(size = 13),
    axis.text = element_text(size = 12)
  )


## Gerando o gráfico de barras para as distâncias totais percorridas de bike em cada estado
g4 = ggplot(bikers_estado, 
       aes(x = fct_reorder(hub_state, distancia_total_km), 
           y = distancia_total_km, 
           fill = hub_state)) +
  geom_col() +
  geom_text(aes(label = paste0(round(distancia_total_km, 1), " km"),
                hjust = hjust_label),
            size = 4) +
  labs(
    title = "Distância Total (Quilômetros)",
    x = NULL,
    y = NULL
  ) +
  coord_flip() + 
  scale_fill_brewer(palette = "Accent") +
  theme_classic(base_size = 14) +
  theme(
    legend.position = 'none',
    axis.text.x = element_blank(),
    axis.ticks.x = element_blank(),
    plot.title = element_text(face = "bold", size = 16, hjust = 0, lineheight = 1.1),
    axis.title = element_text(size = 13),
    axis.text = element_text(size = 12)
  )



## Visualizando os dois gráficos juntos
(g3 | g4) + 
  plot_annotation(
    title = "Distâncias Percorridas por Bikers",
    theme = theme(
      plot.title = element_text(face = "bold", size = 18, hjust = 0.5)
    )
  )

* A leitura cruzada dos dois gráficos indica que, embora PR e RS tenham distâncias médias mais altas por entrega, seu volume total de entregas com bikers é muito inferior ao de RJ e SP. Isso pode significar uma operação de menor escala nesses estados ou menor adesão ao modal bicicleta.

Indicadores de receita por segmento

## criando os indicadores de receita média e total separada por segmento (food x good)
receita_segmento = base_entregas %>%
  filter(!is.na(store_segment)) %>%
  group_by(store_segment) %>%
  summarise(
    receita_total = sum(receita, na.rm = TRUE),
    receita_media = mean(receita, na.rm = TRUE),
    n_entregas = n(),
    .groups = 'drop'
  ) %>%
  mutate(
    prop_receita = receita_total / sum(receita_total),
    rotulo = paste0(
      "R$ ", round(receita_total / 1e6, 2), "M",
      "\n(", round(prop_receita * 100, 1), "%)"
    ),
    posicao_label = ifelse(receita_total < 1000000, -0.1, 1.1),
    hjust_label = ifelse(receita_total < 1000000, -0.1, 1.1),
    receita_total_milhoes = receita_total / 1e6
  ) %>%
  mutate(
    store_segment = factor(store_segment, levels = c("FOOD", "GOOD"))
  )
## Gráfico 1: Receita média por segmento
g_receita_media = ggplot(receita_segmento, 
       aes(x = store_segment, 
           y = receita_media, 
           fill = store_segment)) +
  geom_col() +
  geom_text(aes(label = paste0("R$ ", round(receita_media, 2))), 
            vjust = 2, size = 4) +
  labs(
    title = "Receita Média (Reais)",
    x = NULL,
    y = NULL
  ) +
  scale_fill_brewer(palette = "Pastel1") +
  theme_classic(base_size = 14) +
  theme(
    legend.position = 'none',
    axis.ticks.x = element_blank(),
    axis.ticks.y = element_blank(),
    axis.text.y = element_blank(),
    plot.title = element_text(face = "bold", size = 13, hjust = 0.5, lineheight = 0.5),
    axis.title = element_text(size = 12),
    axis.text = element_text(size = 12)
  )

## Gráfico 2: Receita total por segmento
g_receita_total = ggplot(receita_segmento, 
       aes(x = store_segment, 
           y = receita_total_milhoes, 
           fill = store_segment)) +
  geom_col() +
  geom_text(aes(label = rotulo), 
            vjust = 1.2, size = 4) +
  labs(
    title = "Receita Total (Milhões de Reais)",
    x = NULL,
    y = NULL
  ) +
  scale_fill_brewer(palette = "Pastel1") +
  theme_classic(base_size = 14) +
  theme(
    legend.position = 'none',
    axis.ticks.x = element_blank(),
    axis.ticks.y = element_blank(),
    axis.text.y = element_blank(),
    plot.title = element_text(face = "bold", size = 13, hjust = 0.5, lineheight = 0.5),
    axis.title = element_text(size = 12),
    axis.text = element_text(size = 12)
  )


## Unificando os dois gráficos com título geral
(g_receita_total | g_receita_media) +
  plot_annotation(
    title = "Receitas por Segmento de Loja",
    theme = theme(
      plot.title = element_text(face = "bold", size = 18, hjust = 0.5)
    )
  )

* O gráfico comparativo entre os segmentos FOOD e GOOD revela dois padrões distintos de desempenho que devem orientar estratégias específicas para cada um.

Recomendações Estratégicas com base na análise das receitas segmentadas

Indicadores de receita por estado

## criando os indicadores de receita média e total separada por segmento (food x good)
receita_por_estado = base_entregas %>%
  filter(!is.na(hub_state)) %>%
  group_by(hub_state) %>%
  summarise(
    receita_total = sum(receita, na.rm = TRUE),
    receita_media = mean(receita, na.rm = TRUE),
    n_entregas = n(),
    .groups = 'drop'
  ) %>%
  mutate(
    receita_total_milhoes = receita_total / 1e6,
    rotulo_total = paste0("R$ ", round(receita_total / 1e6, 2), " M"),
    rotulo_media = paste0("R$ ", round(receita_media, 2)),
    hjust_total = ifelse(receita_total_milhoes < 1.5, -0.1, 1.1),
  ) %>%
  arrange(desc(receita_total))
## Gráfico 1: Receita Total
g_total_estado = ggplot(receita_por_estado,
       aes(x = fct_reorder(hub_state, receita_total_milhoes), 
           y = receita_total_milhoes, fill = hub_state)) +
  geom_col() +
  geom_text(aes(
    label = rotulo_total,
    hjust = hjust_total
  ), size = 4) +
  labs(
    title = "Receita Total (Milhões de Reais)",
    x = NULL,
    y = NULL
  ) +
  coord_flip() +
  scale_fill_brewer(palette = "Pastel1") +
  theme_classic(base_size = 14) +
  theme(
    legend.position = 'none',
    axis.text.x = element_blank(),
    axis.ticks.x = element_blank(),
    plot.title = element_text(face = "bold", size = 13, hjust = 0.5, lineheight = 1.1),
    axis.title = element_text(size = 12),
    axis.text = element_text(size = 12)
  )

## Gráfico 2: Receita Média
g_media_estado = ggplot(receita_por_estado,
       aes(x = fct_reorder(hub_state, receita_media), 
           y = receita_media, fill = hub_state)) +
  geom_col() +
  geom_text(aes(
    label = rotulo_media,
    hjust = 1.1
  ), size = 4) +
  labs(
    title = "Receita Média (Reais)",
    x = NULL,
    y = NULL
  ) +
  coord_flip() +
  scale_fill_brewer(palette = "Pastel1") +
  theme_classic(base_size = 14) +
  theme(
    legend.position = 'none',
    axis.text.x = element_blank(),
    axis.ticks.x = element_blank(),
    plot.title = element_text(face = "bold", size = 13, hjust = 0.5, lineheight = 1.1),
    axis.title = element_text(size = 12),
    axis.text = element_text(size = 12),
  )


## Unificando os dois gráficos com título geral
(g_total_estado | g_media_estado) +
  plot_annotation(
    title = "Receitas Estaduais",
    theme = theme(
      plot.title = element_text(face = "bold", size = 18, hjust = 0.5)
    )
  )

* São Paulo se destaca como o principal mercado da operação, apresentando tanto a maior receita total (R$ 2,64 milhões) quanto a maior receita média por entrega (R$ 17,56). Isso indica um alto volume de pedidos combinados a um ticket médio elevado, sugerindo uma operação mais consolidada e madura no estado.

*Por fim, o Paraná registra os menores valores tanto em receita total (R$ 0,27 milhões) quanto em receita média por entrega (R$ 9,83). A combinação de baixo volume com ticket médio mais baixo pode indicar uma operação em estágio inicial ou com menor penetração de mercado, representando uma possível oportunidade de crescimento ou a necessidade de reavaliar estratégias locais.

Bonificação dos Funcionários

## Calculando o lucro total
lucro_total = sum(base_entregas$lucro)

## Calculando o bônus total
bonus_total = lucro_total * 0.2

## Calculando o bônus individual de cada funcionário
bonus_individual = bonus_total / 2000

## Visualizando o resultado
print(paste("A Bonificação de Cada Funcionário Será de R$", round(bonus_individual, 2)))
## [1] "A Bonificação de Cada Funcionário Será de R$ 329.04"