O objetivo desse estudo é criar análises variadas sobre uma operação de e-commerce, com a produção de resumos, plots e projeções sobre vendas.
Muitos outros indicadores além dos que estão aqui podem ser extraídos, mas vamos nos ater a alguns para não tornar o estudo muito longo e cansativo.
library(prophet)
## Carregando pacotes exigidos: Rcpp
## Carregando pacotes exigidos: rlang
library(readr)
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(tidyr)
library(ggplot2)
library(wordcloud)
## Carregando pacotes exigidos: RColorBrewer
library(tidytext)
library(tidyr)
library(stringr)
Os datasets utilizados estão disponíveis em https://www.kaggle.com/datasets/olistbr/brazilian-ecommerce. É um conjunto de datasets famoso do e-commerce Olist, usado em uma série de estudos.
Os joins feitos entre os datasets levam em consideração a estrutura informada pelo mantenedor, respeitando as chaves primárias e estrangeiras.
olist_orders_dataset <- read_csv("archive/olist_orders_dataset.csv", show_col_types = FALSE)
olist_customers_dataset <- read_csv("archive/olist_customers_dataset.csv", show_col_types = FALSE)
olist_order_payments_dataset <- read_csv("archive/olist_order_payments_dataset.csv", show_col_types = FALSE)
summary(olist_orders_dataset)
## order_id customer_id order_status
## Length:99441 Length:99441 Length:99441
## Class :character Class :character Class :character
## Mode :character Mode :character Mode :character
##
##
##
##
## order_purchase_timestamp order_approved_at
## Min. :2016-09-04 21:15:19.00 Min. :2016-09-15 12:16:38.0
## 1st Qu.:2017-09-12 14:46:19.00 1st Qu.:2017-09-12 23:24:16.0
## Median :2018-01-18 23:04:36.00 Median :2018-01-19 11:36:13.0
## Mean :2017-12-31 08:43:12.77 Mean :2017-12-31 18:35:24.0
## 3rd Qu.:2018-05-04 15:42:16.00 3rd Qu.:2018-05-04 20:35:10.0
## Max. :2018-10-17 17:30:18.00 Max. :2018-09-03 17:40:06.0
## NA's :160
## order_delivered_carrier_date order_delivered_customer_date
## Min. :2016-10-08 10:34:01.00 Min. :2016-10-11 13:46:32.00
## 1st Qu.:2017-09-15 22:28:50.25 1st Qu.:2017-09-25 22:07:22.25
## Median :2018-01-24 16:10:58.00 Median :2018-02-02 19:28:10.50
## Mean :2018-01-04 21:49:48.14 Mean :2018-01-14 12:09:19.03
## 3rd Qu.:2018-05-08 13:37:45.00 3rd Qu.:2018-05-15 22:48:52.25
## Max. :2018-09-11 19:48:28.00 Max. :2018-10-17 13:22:46.00
## NA's :1783 NA's :2965
## order_estimated_delivery_date
## Min. :2016-09-30 00:00:00.00
## 1st Qu.:2017-10-03 00:00:00.00
## Median :2018-02-15 00:00:00.00
## Mean :2018-01-24 03:08:37.73
## 3rd Qu.:2018-05-25 00:00:00.00
## Max. :2018-11-12 00:00:00.00
##
São observados alguns NAs nas datas, para os pedidos que ainda não atingiram certa fase da jornada na data da extração do dataset
Podemos criar novas variáveis com a quantidade de dias entre cada etapa do pedido, de forma a calcular a jornada do cliente.
olist_orders_dataset <- olist_orders_dataset %>%
mutate(compra_a_aprovacao = difftime(order_approved_at,
order_purchase_timestamp, units = "days"),
aprovacao_ao_transportador = difftime(order_delivered_carrier_date,
order_approved_at,
units = "days"),
transportador_a_entrega = difftime(order_delivered_customer_date,
order_delivered_carrier_date,
units = "days"),
entregou_atraso = ifelse(order_delivered_customer_date > order_estimated_delivery_date, 1, 0),
jornada_total = (compra_a_aprovacao / 24) + aprovacao_ao_transportador + transportador_a_entrega) %>%
left_join(olist_customers_dataset, by = 'customer_id')
Aqui temos as tabelas com os resumos de jornada do cliente por UF e município.
Média de tempo em dias da compra no portal até a aprovação do pagamento;
Média de tempo em dias da aprovação do pagamento até o envio do produto ao transportador;
Média de tempo em dias do envio ao transportador até a entrega ao cliente;
Média do total da jornada de compra, do início ao fim;
Número de pedidos entregues com atraso, tomando como base a estimativa informada ao cliente;
Índice de atrasos, relacionando as entregas atrasadas com o total de entregas.
jornada_cliente_UF <- olist_orders_dataset %>%
group_by(customer_state) %>%
summarise(compra_a_aprovacao = mean(compra_a_aprovacao, na.rm = TRUE),
aprovacao_ao_transportador = mean(aprovacao_ao_transportador, na.rm = TRUE),
transportador_a_entrega = mean(transportador_a_entrega, na.rm = TRUE),
jornada_total = mean(jornada_total, na.rm = TRUE),
entregou_atraso = sum(entregou_atraso, na.rm = TRUE),
indice_atraso = sum(entregou_atraso, na.rm = TRUE) / n())
jornada_cliente_municipio <- olist_orders_dataset %>%
group_by(customer_city) %>%
summarise(compra_a_aprovacao = mean(compra_a_aprovacao, na.rm = TRUE),
aprovacao_ao_transportador = mean(aprovacao_ao_transportador, na.rm = TRUE),
transportador_a_entrega = mean(transportador_a_entrega, na.rm = TRUE),
jornada_total = mean(jornada_total, na.rm = TRUE),
entregou_atraso = sum(entregou_atraso, na.rm = TRUE),
indice_atraso = sum(entregou_atraso, na.rm = TRUE) / n())
head(jornada_cliente_UF)
head(jornada_cliente_municipio)
#Remanejamento da tabela para o plot agrupado
jornada_cliente_UF_pivot <- jornada_cliente_UF %>%
pivot_longer(cols = 2:5, names_to = "etapa", values_to = "dias") %>%
select(customer_state, etapa, dias)
jornada_cliente_UF_pivot %>%
filter(!(etapa %in% 'jornada_total')) %>%
ggplot(aes(y = customer_state, fill = etapa, x = dias)) +
geom_bar(stat = "identity", position = "stack") +
theme_classic() +
labs(title = "Jornada do Cliente em Média de Dias por UF - Por Etapa") +
xlab("Dias") +
ylab("UF")
## Don't know how to automatically pick scale for object of type <difftime>.
## Defaulting to continuous.
jornada_cliente_UF_pivot %>%
filter((etapa %in% 'jornada_total')) %>%
ggplot(aes(y = customer_state, x = dias)) +
geom_bar(stat = "identity", fill = "orange") +
geom_text(aes(label = round(dias, digits = 2)),
position = position_stack(vjust = 1.1)) +
theme_classic() +
labs(title = "Jornada do Cliente em Média de Dias por UF - Total") +
xlab("Dias") +
ylab("UF")
## Don't know how to automatically pick scale for object of type <difftime>.
## Defaulting to continuous.
## Don't know how to automatically pick scale for object of type <difftime>.
## Defaulting to continuous.
Aqui vamos utilizar o algoritmo prophet para produção de
forecast de vendas com o faturamento.
O algoritmo prophet é uma espécie de projeção
HoltWinters, porém com a vantagem de necessitar de menos
parametrizações e conversões de dados e menos tempo gasto para aprimorar
a precisão do algoritmo.
prophet facilita o nosso trabalho detectando
automaticamente mudanças de tendência ao longo da série temporal, além
de funções de grande utilidade, como a geração de plot interativo
apresentando a projeção e plots secundários de tendência e
sazonalidade.
Com as vendas numa base diária, podemos parametrizar o algoritmo para levar em consideração as sazonalidades diárias, semanais e anuais presentes nos dados, de forma a obter projeções fiéis e confiáveis.
#Tratamento do dataset com os pagamentos.
#Vouchers convertidos para negativos, pois são concedidos pela loja
dataset_pagamentos <- olist_order_payments_dataset %>%
mutate(payment_value = ifelse(payment_type == 'voucher',
payment_value * -1,
payment_value)) %>%
group_by(order_id) %>%
summarise(fatura = sum(payment_value))
dataset_pedidos <- olist_orders_dataset %>%
left_join(dataset_pagamentos, by = 'order_id')
#Resumo apenas com os pedidos entregues, excluindo os cancelados e ainda não concluídos
resumo_vendas_dia <- dataset_pedidos %>%
filter(order_status %in% 'delivered') %>%
mutate(ds = as.Date(order_purchase_timestamp)) %>%
group_by(ds) %>%
summarise(y = sum(fatura))
#Modelo com prophet
modelo_forecast_dia <- prophet(resumo_vendas_dia,
growth = "linear",
yearly.seasonality = TRUE,
weekly.seasonality = TRUE,
daily.seasonality = TRUE,
seasonality.mode = "multiplicative")
periodos_futuros_dia <- make_future_dataframe(modelo_forecast_dia,
periods = 720,
freq = "day",
include_history = TRUE)
projecao_dia <- predict(modelo_forecast_dia,
periodos_futuros_dia)
plot_sazonalidade <- prophet_plot_components(modelo_forecast_dia, projecao_dia)
plot_dia <- dyplot.prophet(modelo_forecast_dia, projecao_dia,
main = "Projeção de Vendas Global",
xlab = "Linha Temporal",
ylab = "Faturamento",
width = "100%")
plot_dia
plot_sazonalidade[1]
## [[1]]
Podemos observar um tendência de crescimento das vendas ano após ano. Margem de erro também exibida.
plot_sazonalidade[2]
## [[1]]
No plot semanal, observa-se maior propensão das pessoas comprarem de segunda a sexta (faixa acima do 0%, que representa a média global), sendo segunda-feira o dia da semana líder de vendas e sexta-feira o dia mais próximo da média. Vendas abaixo da média no sábado e domingo.
plot_sazonalidade[3]
## [[1]]
No plot anual, que exibe os meses ao longo do ano, observa-se uma maior sazonalidade no final do ano, em Novembro e Dezembro, muito em razão dos presentes adquiridos para as festas, na ordem de mais de 50% acima da média.
olist_products_dataset <- read_csv("archive/olist_products_dataset.csv",
col_types = cols_only(product_id = col_guess(),
product_category_name = col_guess()))
olist_order_items_dataset <- read_csv("archive/olist_order_items_dataset.csv", show_col_types = FALSE)
dataset_pedidos_categorizados <- olist_order_items_dataset %>%
left_join(olist_products_dataset, by = 'product_id') %>%
left_join(olist_orders_dataset[, c(1, 4)], by = 'order_id')
resumo_vendas_dia_categoria <- dataset_pedidos_categorizados %>%
mutate(ds = format(order_purchase_timestamp, "%Y-%m-%d")) %>%
group_by(ds, product_category_name) %>%
summarise(y = sum(price))
## `summarise()` has grouped output by 'ds'. You can override using the `.groups`
## argument.
categoria <- 'moveis_decoracao'
modelo_categoria <- prophet(resumo_vendas_dia_categoria %>%
filter(product_category_name == categoria),
growth = "linear",
yearly.seasonality = TRUE,
weekly.seasonality = TRUE,
daily.seasonality = TRUE,
seasonality.mode = "multiplicative")
periodos_futuros <- make_future_dataframe(modelo_categoria,
periods = 720,
freq = "day")
projecao_categoria <- predict(modelo_categoria, periodos_futuros)
plot_categoria <- dyplot.prophet(modelo_categoria,
projecao_categoria,
main = paste0("Projeção de Vendas - ", categoria),
xlab = "Linha Temporal",
ylab = "Faturamento",
width = "100%")
## Adding missing grouping variables: `ds`
plot_categoria
olist_order_reviews_dataset <- read_csv("archive/olist_order_reviews_dataset.csv", show_col_types = FALSE)
olist_sellers_dataset <- read_csv("archive/olist_sellers_dataset.csv", show_col_types = FALSE)
dataset_reviews <- olist_order_items_dataset %>%
left_join(olist_sellers_dataset, by = 'seller_id') %>%
left_join(olist_order_reviews_dataset, by = 'order_id') %>%
distinct(order_id, .keep_all = TRUE)
media_notas <- dataset_reviews %>%
group_by(seller_id) %>%
summarise(media_nota = round(mean(review_score, na.rm = TRUE),
digits = 2),
quantidade_notas = n()) %>%
arrange(desc(quantidade_notas))
head(media_notas)
Visualização gráfica na modalidade “nuvem de palavras”, onde as maiores palavras são as de maior ocorrência nas avaliações.
lista_palavras <- olist_order_reviews_dataset %>%
unnest_tokens(output = 'palavra',
input = review_comment_message,
token = "words",
format = "text",
to_lower = TRUE) %>%
drop_na() %>%
filter(nchar(palavra) > 3)
frequencia_palavras <- lista_palavras %>%
count(palavra, name = "quantidade", sort = TRUE)
wordcloud(words = frequencia_palavras$palavra,
freq = frequencia_palavras$quantidade,
min.freq = 100,
colors = brewer.pal(8, "Dark2"),
random.order = FALSE,
rot.per = 0)