#!/usr/bin/env python
# coding: utf-8
#
ANÁLISE DAS VENDAS DOS PRODUTOS DA HOTMART EM 2016
# Guilherme J. S. Passos
# Belo Horizonte, 12 de Agosto de 2019
#
# 1. INTRODUÇÃO
#
#
#
A Hotmart é uma empresa de tecnologia brasileira que possui uma plataforma online de comercialização de conhecimento. A plataforma permite que as pessoas comercializem conteúdo de diversas áreas do conhecimento ou revendem o conteúdo produzido por terceiros. Este notebook apresenta um estudo detalhado das vendas dos produtos da Hotmart no ano de 2016 e tem como objetivo explorar algumas técnicas de análise preponderantes para no ofício da ciência de dados.
#
A base de dados a ser analisada possui mais de 1,5 milhões de registros e aproximadamente 227 MB de espaço em disco, portanto não se fez necessário a utilização de um serviço distribuído de arquivos em clusters de máquinas para o processamento e abertura dos dados. Ademais, o fato da base possuir apenas 227 MB de espaço permite que ela seja facilmente carregada na memória de laptop comum.
#
Inicialmente, apresentar-se-á brevemente os dados do data set e em seguida será realizada uma transformação no valor de cada venda para que seja possível analisar o faturamento gerado pela empresa; uma vez que essa informação está codificada em termos do zscore. Posteriormente, a quarta seção deste estudo se dedicará a apresentar uma detalhadada exploração dos dados segmentada por produtos e produtores de conteúdo. Por fim, na quinta e última seção se aplicará um modelo de machine learning na série temporal das vendas a fim de se estimar o volume futuro das mesmas. Enjoy!!!
#
# 2. APRESENTAÇÃO DOS DADOS
#
#
# A base de dados em estudo possui os seguintes campos:
#
#
purchase_id Identificação da compra na Hotmart;
# product_id Identificação do produto na Hotmart;
# affiliate_id: Identificação do afiliado na Hotmart, pessoa responsável por revender um determinado produto;
# producer_id: Identificação do produtor na Hotmart;
# buyer_id: Identificação do comprador na Hotmart;
# purchase_date: Data e hora em que a compra foi realizada;
# product_creation_date: Data e hora em que o produto foi criado na Hotmart;
# product_category: Categoria do produto na Hotmart. Exemplo: e-book, software,
# curso online, e-tickets, etc;
# product_niche: Nicho de mercado que o produto faz parte. Exemplo: educação, saúde
# e bem-estar, sexualidade, etc.;
# purchase_value: Valor da compra codificado em zscore do valor real.
# affiliate_commission_percentual: percentual de comissão que o afiliado receberá da
# compra;
# purchase_device: Tipo de dispositivo utilizado no momento da compra, como:
# Desktop, Mobile, Tablet, ou Outros;
# purchase_origin: Endereço do site do qual a pessoa veio antes da compra (Facebook, Youtube, etc);
# is_origin_page_social_network: informa se essa compra veio de uma URL do
# Facebook, Youtube, Instagram, Pinterest, ou Twitter;
#
#
#
#
# As informações apresentadas nos campos product_niche, product_category e purchase_value foram codificadas por razões de confidencialidade.
#
#
#
#
# In[1]:
import pandas as pd
# Carregamento do data set
sales = pd.read_csv('https://query.data.world/s/enrwqqtwlxc55skwsx735hp2yldis4', sep=';',
encoding='utf-8',header=0)
# Visualização dos dados
sales.sample(5)
# In[2]:
# Exclusão dos registros que possuem valores nulos
sales = sales.dropna()
len(sales)
# 3. TRANSFORMAÇÃO DOS DADOS
#
#
# Como pode ser visto no resultado acima, tem-se exatamente
1.599.828 registros de vendas na base de dados. Contudo, os valores de venda de cada produto estão codificados em termos do zscore. Em estatística, o
zscore é uma medida que indica a distância em desvios padrão que um determinado valor está da média do conjunto. Em teoria, o domínio do zscore são todos os números reais, mas na prática aproximadamente 99,73% dos valores de um conjunto normalmente distribuído possuem zscore dentro do intervalo fechado [-3,3]. Desta forma, ir-se-á normalizar os zscores dos valores de venda dos produtos dentro do intervalo [0,1] usando a
normalização min-max. As equações a seguir provam que aplicar a normalização min-max no conjunto de zscores de uma variável é equivalente à se aplicar a mesma normalização no conjunto dos valores reais da variável de interesse.
#
# Sejam
m, s, l, u e x a média, o desvio padrão, o valor mínimo, o valor máximo e um valor qualquer do conjunto X respectivamente. Tem-se:
#
#
# $$ z = \frac{x - mean(X)}{sd(X)} \space (1); $$
#
#
# Onde z representa o zscore de x. Sejam y e y', o valor x e do zscore de x normalizados respectivamente:
#
# $$ y = \frac{x - l}{u - l} \space (2); $$
#
# $$y' = \frac{z - min(Z)}{max(Z) - min(Z)} \space (3); $$
#
#
# Substituindo a equação (1) em (3), obtém-se:
#
# $$y' = \frac{z - min(Z)}{max(Z) - min(Z)} = \frac{\frac{x-m}{s} - \frac{l-m}{s}}{\frac{u-m}{s} - \frac{l-m}{s}} = \frac{ \frac{x - \not{m} - (l- \not{m})}{\not{s}} }{ \frac{u- \not{m}-(l- \not{m})}{\not{s}}} = \frac{x - l}{u - l} = y$$
#
#
# O resultado anterior mostra que normalizar o conjunto de zscores de uma variável é equivalente à se normalizar os valores da própria variável. Assim, é possível captar percentualmente o faturamento que cada produto, produtor, categoria ou qualquer outro segmento gerou para a Hotmart sem a necessidade de se utilizar os valores reais das vendas.
#
# In[3]:
from sklearn import preprocessing
# Transformação Min-Max na coluna purchase_value
min_max_scaler = preprocessing.MinMaxScaler()
sales['purchase_value_norm'] = min_max_scaler.fit_transform(sales[['purchase_value']].values)
sales[['product_id','purchase_value','purchase_value_norm']].sample(6)
# 4. EXPLORAÇÃO DOS DADOS
#
#
#
# Nesta etapa do estudo, analisar-se-á o total de vendas realizadas e o faturamento gerado por cada produto e produtor de conteúdo.
#
# 4.1 SEGMENTAÇÃO DAS VENDAS POR PRODUTO
#
#
# Apresenta-se nesta seção a análise das vendas e do faturamento da Hotmart segmentada por produto.
#
# In[4]:
# Define um método sales_by_id responsável por segmentar as vendas por um dado ID
def sales_by_id(sales,column_id,column_name):
df = sales.groupby(column_id).count()[['purchase_id']].sort_values(by=['purchase_id'],
ascending=False)
df.columns = [column_name]
# Rankeamento dos itens por quantidade de vendas
df['sales_rank'] = df[column_name].rank(ascending=False)
# Percentual do total de vendas por item
df['%_sales'] = sales[column_id].value_counts(normalize=True) *100
return df
#
Ranking dos produtos que mais vendem
# In[5]:
# Obtém as vendas segmentadas por produtos
by_products = sales_by_id(sales,'product_id','sales_by_product')
# ------------------------------------------ Cálculo do faturamento gerado por cada produto
# Lista de produtos
unique = sales.drop_duplicates(subset=['product_id'])
by_products = pd.merge(by_products,
unique[['product_id','purchase_value_norm','product_niche','product_category',
'purchase_device']],
how='inner',on='product_id')
# Cálculo da receita de cada produto
by_products['revenue_by_product'] = by_products['sales_by_product'] * by_products['purchase_value_norm']
del by_products['purchase_value_norm']
# Rankeamento dos produtos por faturamento
by_products['revenue_rank'] = by_products['revenue_by_product'].rank(ascending=False)
# Percentual do faturamento por produtos
by_products['%_revenue'] = 100*(by_products['revenue_by_product']/by_products['revenue_by_product'].sum())
# Ordenação das colunas
by_products = by_products[['product_id','sales_rank','sales_by_product','%_sales',
'revenue_rank','revenue_by_product','%_revenue',
'product_niche','product_category','purchase_device']]
# Resultado
by_products.head(10)
# In[6]:
# Total de produtos
len(by_products)
#
Ranking dos produtos que mais faturam
# In[7]:
# Ordena os produtos por faturamento
by_products = by_products.sort_values(by=['%_revenue'],ascending=False)
# Reordenação das colunas
by_products[['product_id','revenue_rank','revenue_by_product','%_revenue',
'sales_rank','sales_by_product','%_sales',
'product_niche','product_category','purchase_device']].head(10)
# A tabela anterior reúne todos os produtos rankeados pelo número total de vendas e por faturamento. Os campos mostrados significam o seguinte:
#
#
product_id: Identificação do produto na Hotmart;
#
sales_rank: Posição do produto no ranking de vendas;
#
sales_by_product: Quantidade de vendas do produto;
#
%_sales Percentual de venda do produto em relação as vendas totais;
#
revenue_rank: Posição do produto no ranking de faturamento;
#
revenue_by_product: Faturamento gerado pelo produto normalizado;
#
%_revenue: Percentual do faturamento do produto em relação ao faturamento total;
#
#
#
# A tabela acima mostra que 9 dos 20 produtos mais vendidos foram também os 20 produtos que mais geraram receita para a Hotmart em 2016.
# Uma vez obtido o ranking de produtos por número de vendas e faturamento, é possível determinar qual é a relevância percentual de um determinado produto para a Hotmart.
# In[8]:
# Define um método sales_relevance responsável por computar a revelância de conjuntos de ranking dos produtos
def sales_relevance(df,rankings,text):
# Percentual cumulativo do total de vendas de cada produto do ranking
rank_cum_sales = pd.DataFrame({'%':df['%_sales'].cumsum()})
# Percentual cumulativo do faturamento de cada produto do ranking
rank_cum_revenue = pd.DataFrame({'%':df['%_revenue'].cumsum()})
# Output
percentage_sales = []
percentage_revenue = []
ranks = []
percentage_amount = []
for value in rankings:
# Computa a quantidade de produtos dentro dentro de determinado percentual cumlativo
ranks.append(text.format(str(value)))
percentage_sales.append(round(rank_cum_sales .iloc[value-1,0],2))
percentage_revenue.append(round(rank_cum_revenue.iloc[value-1,0],2))
# Computa a quantidade percentual de itens
percentage_amount.append(round(100*(value/len(df)),2))
return pd.DataFrame({'Rankings':ranks,'Percentage Amount (%)':percentage_amount,
'Relevance - Sales (%)':percentage_sales,
'Relevance - Revenue (%)':percentage_revenue})
#
Relevância de alguns rankings de vendas dos produtos para a Hotmart
# In[9]:
# Rankeamento dos produtos por faturamento
by_products = by_products.sort_values(by=['%_sales'],ascending=False)
# Rankings a serem analasidados
rankings = [1,5,10,20,60,120,240,450,800,1500,3000,5000]
text = "TOP {} produtos mais vendidos"
# Relevância dos rankings
sales_relevance(by_products,rankings,text)
#
# Os campos mostrados tabela acima significam o seguinte:
#
#
Rankings: Rankings dos produtos mais vendidos;
# Percentage Amount (%): Quantidade percentual de produtos dentro do ranking
# Relevance - Sales (%): Relevância percentual do ranking no total de vendas;
# Relevance - Revenue (%): Relevância percentual do ranking no faturamento total da empresa
#
#
# Os resultados mostrados na tabela acima indicam, dentre outras coisas, que os 60 produtos mais vendidos (cerca de 0,67% dos produtos disponíveis) foram responsáveis por mais de 30% de todas as vendas e, aproximadamente, 16% do faturamento da Hotmart em 2016.
#
Análise do nicho, categoria e aparelho de compra dos produtos mais vendidos
#
# In[10]:
# Define um método by_topProducts responsável por analisar os produtos de um terminado rank por um determinado
# filtro
def by_topProducts(filter, rank):
output = by_products[[filter,'product_id']].head(rank).groupby([filter]).count().\
sort_values(by=['product_id'],ascending=False)
output.columns = ['Total de produtos']
return output
by_topProducts('product_niche',450).head(15)
# In[11]:
by_topProducts('product_category',450)
# In[12]:
by_topProducts('purchase_device',450)
# A partir dos resultados mostrados nas três tabelas acima é possível concluir que os nichos de negociação, controle de ansiedade, finanças pessoais e habilidades de apresentação foram os que mais ocorrem dentre os 450 produtos mais vendidos. Além disso, pode-se afirmar que os consumidores consumiram mais livros físicos através de eReaders, Desktop e SmartTV. Essas informações podem ser extremamente úteis para as estratégias futuras de marketing da empresa, pois revelam de certa forma as características que mais impactam no sucesso de um produto.
#
#
Essas conclusões podem não condizer com a realidade das vendas realizadas em 2016 pois tais campos foram codificados pela Hotmart por motivos de confidencialidade.
#
Relevância de alguns rankings de faturamento dos produtos
#
# In[13]:
# Ordenação dos dados por faturamento
by_products = by_products.sort_values(by=['%_revenue'],ascending=False)
rankings = [1,5,10,20,60,120,240,450,800,1500,3000,5000]
text = "TOP {} produtos que mais faturam"
# Relevância dos rankings
sales_relevance(by_products,rankings,text)
# Os resultados mostrados na tabela acima indicam, dentre outras coisas, que os 60 produtos que mais faturam (cerca de 0,67% dos produtos disponíveis) foram responsáveis por mais de 30% do faturamento total da Hotmart em 2016.
# A imagem a seguir apresenta o faturamento de alguns subconjuntos do ranking de faturamento.
# In[14]:
import matplotlib.pyplot as plt # Matlab-style plotting
from matplotlib import pyplot
from matplotlib import colors as mcolors
import numpy as np
# Define um método scaterr_plot reponsável por gerar um gráfico de pontos identificados por labels
def scaterr_plot(rankings,colors,df,item,title,xlim,ylim):
labels = []
data = []
i=0
for rank in rankings:
revenue_percentage = round(df.iloc[i:rank,6].sum(),2)
labels.append("{}% do faturamento | {} {} | Top {} em receita".format(str(revenue_percentage),
len(df.iloc[i:rank,6]),
item,rank))
data.append((np.log1p(df.iloc[i:rank,2]),
np.log1p(df.iloc[i:rank,5])))
i=rank
fig = plt.figure(figsize=(12, 9))
ax = fig.add_subplot(1, 1, 1, facecolor="10")
for data,color,label in zip(data,colors,labels):
x,y=data
ax.scatter(x,y, alpha=0.8, c=color, edgecolors='none', s=30, label=label)
plt.title(title, fontsize=16)
plt.xlabel('Log(Qtde de vendas)', fontsize=14)
plt.ylabel('Log(Receita gerada)', fontsize=14)
plt.xlim(xlim)
plt.ylim(ylim)
plt.legend(loc='upper left')
plt.grid(True)
plt.show()
#
Distribuição dos produtos por quantidade de vendas versus faturamento
# In[15]:
# Reordenação das colunas
by_products = by_products.sort_values(by=['%_revenue'],ascending=False)
rankings = [5,10,60,240,800,3000,5000,len(by_products)]
colors = ('red','green','brown','blue','black','purple','olive','gold')
item = 'produtos'
title = 'Distribuição dos produtos por quantidade de vendas versus faturamento'
xlim = (0.5,12)
ylim = (0.000001,5)
# Gerea o gráfico
scaterr_plot(rankings,colors,by_products,item,title,xlim,ylim)
# O gráfico mostrado acima permite extrair alguns insights interessantes. Eles são:
#
#
# - Como os produtos foram ordenados por faturamento, é possível separar perfeitamente os grupos de produtos por cores e perceber que os grupos intermediários foram os maiores geradores de receita para a empesa. Pois, ao se observar a legenda no canto superior esquerdo do gráfico, constata-se que aproximadamente 84% do faturamento da Hotmart em 2016 foi oriundo dos 2990 produtos colocados entre as posições 11° e 3000° do ranking.
# - A nuvem de pontos apresentada fornece algumas sugestões de estratégias de negócio para empresa. Por exemplo, o quão mais inclinada for a direção de crescimento da nuvem em relação ao eixo x, maior será o faturamento da Hotmart . Uma outra estratégia interessante que resulta no aumento do faturamento é mover a maior quantidade de pontos possíveis para o centro da nevem; tornando-a mais cheia na região que se encontra os pontos em preto e azul.
# - Os dois pontos roxos na parte inferior direita do gráfico indicam que esses produtos, apesar de terem sido campeões de venda, não se encontram nem mesmo dentro do ranking dos 800 maiores geradores de receita.
#
#
#
# É importante ressaltar que o eixo x e y do gráfico estão em escala logarítmica. Essa transformação permite uma melhor visualização dos dados, visto que há uma diferença de magnitude significativa entre os valores.
#
4.2 SEGMENTAÇÃO DAS VENDAS POR PRODUTOR DE CONTEÚDO
#
#
Nesta seção será realizada uma análise similar à realizada no item 4.1, mas segmentada por produtor de conteúdo.
#
Ranking dos produtores que mais vendem
#
# In[16]:
by_producers = sales_by_id(sales,'producer_id','sales_by_producer')
revenue_by_producers = sales[['producer_id','purchase_value_norm']].groupby(['producer_id']).sum().\
sort_values(by=['purchase_value_norm'],ascending=False)
revenue_by_producers.columns = ['revenue_by_producer']
revenue_by_producers['revenue_rank'] = revenue_by_producers['revenue_by_producer'].rank(ascending=False)
revenue_by_producers['%_revenue'] = 100*(revenue_by_producers['revenue_by_producer']/ \
revenue_by_producers['revenue_by_producer'].sum())
by_producers['producer_id_x'] = by_producers.index
by_producers = pd.merge(by_producers,revenue_by_producers,how='inner',on='producer_id')
by_producers = by_producers[['producer_id_x','sales_rank','sales_by_producer','%_sales',
'revenue_rank','revenue_by_producer','%_revenue']]
by_producers[['sales_rank','sales_by_producer','%_sales',
'revenue_rank','revenue_by_producer','%_revenue']].head(10)
# In[17]:
# Total de produtores
len(by_producers)
#
Ranking dos produtores que mais faturam
# In[18]:
by_producers = by_producers.sort_values(by=['%_revenue'],ascending=False)
by_producers[['revenue_rank','revenue_by_producer','%_revenue',
'sales_rank','sales_by_producer','%_sales']].head(10)
#
Relevância de alguns rankings de vendas segmentados por produtores de conteúdo
# In[19]:
by_producers = by_producers.sort_values(by=['%_sales'],ascending=False)
rankings = [1,5,10,30,60,100,180,300,500,1000,2000,5000]
text = "TOP {} produtores que mais vendem"
sales_relevance(by_producers,rankings,text)
#
Relevância de alguns rankings de faturamento segmentados por produtores de conteúdo
# In[20]:
# Ordenação dos dados pelo ranking de faturamento
by_producers = by_producers.sort_values(by=['%_revenue'],ascending=False)
rankings = [1,5,10,30,60,100,180,300,500,1000,2000,5000]
text = "TOP {} produtores que mais faturam"
# Relevância dos rankings
sales_relevance(by_producers,rankings,text)
# Os resultados mostrados nas quatro tabelas anteriores são bastante interessantes e revelam um padrão diferente da análise segmentada por produto. Observa-se, por exemplo, que o produtor campeão de vendas foi apenas o 25° no ranking de faturamento, enquanto o 13° maior vendedor foi o maior produtor de receita para a Hotmart. Uma outra conclusão importante é que apenas dois produtores do top 10 de vendas apareceram no ranking dos 10 maiores geradores de receita. Observa-se também que os 5 produtores que mais venderam (11,2% das vendas totais) geraram juntos 4,81% da receita total da empresa; esse resultado é relativamente equivalente ao faturamento gerado pelo produtor que mais gerou receita, 3,66%.
#
# Com base nas informações levantadas, os produtores que mais vendem não foram responsáveis pela maior parte do faturamento da Hotmart.
#
Distribuição dos produtores por quantidade de vendas versus faturamento
#
# In[21]:
by_producers = by_producers.sort_values(by=['%_revenue'],ascending=False)
rankings = [5,10,50,100,500,1000,5000,len(by_producers)]
colors = ('red','green','brown','blue','black','purple','olive','gold')
item = 'produtores'
title = 'Distribuição dos produtores por quantidade de vendas versus faturamento'
xlim = (0.5,11)
ylim = (0.00000001,6)
# Gerea o gráfico
scaterr_plot(rankings,colors,by_producers,item,title,xlim,ylim)
# O gráfico acima fornece informações similiares àquele apresentado na segmentação por produto. Todavia, convém ressaltar a superioridade do futaramento gerado pelo maior produtor (ponto vermelho na parte mais superior do gráfico) em relação à todos os outros produtores.
# 5. PREVISÃO DO FATURAMENTO
#
# A análise de séries temporais é uma técnica bastante engenhosa que pode ser aplicada em diversos
# ramos das ciências econômicas para predição de quantitativos financeiros e identificação de padrões
# históricos. Pode-se citar como casos de aplicações dessa técnica a identificação de padrões sazonais nas
# vendas de uma loja; a predição do número de clientes esperados em um evento; a estimativa do efeito de
# um novo produto nas vendas de uma marca; a detecção de eventos adversos e magnitude dos efeitos dos
# mesmos no padrão de consumo de uma população; entre outros.
#
# Nesta seção, objetiva-se predizer o faturamento trismestral da Hotmart no período posterior ao informado na base de dados em estudo a partir de um modelo auto regressivo integrado de médias móveis (
ARIMA, do inglês). Optou-se pela utilização do modelo ARIMA devido à fácil compreensão matemática do modelo.
#
# 5.1 CONTABILIZAÇÃO DO FATURAMENTO POR DIA
#
Faturamento da Hotmart contablizado por dia entre as datas de 31/12/2015 e 30/06/2016
# In[22]:
sales['dts'] = pd.to_datetime(sales['purchase_date'])
sales_timeSeries = sales[['dts','purchase_value_norm']].set_index('dts').\
groupby(pd.Grouper(freq='D')).sum().sort_values(by=['dts'],ascending=True)
sales_timeSeries.columns = ['revenue']
sales_timeSeries.head(10)
#
Série temporal do faturamento da Hotmart no período analisado
# In[23]:
import warnings
warnings.filterwarnings('ignore')
fig = plt.figure(figsize=(12, 9))
ax = fig.add_subplot(1, 1, 1, facecolor="10")
ax.plot(sales_timeSeries)
plt.title('Série temporal do faturamento da Hotmart no período analisado', fontsize=16)
plt.xlabel('Tempo em dias', fontsize=14)
plt.ylabel('Faturamento em unidades do produto mais caro', fontsize=14)
# 5.2 MODELAGEM
#
Divisão dos dados em conjuntos de treino e teste
# In[24]:
tr_start,tr_end = '2015-12-31','2016-05-28'
te_start,te_end = '2016-05-29','2016-06-30'
tra = sales_timeSeries['revenue'][tr_start:tr_end].dropna()
tes = sales_timeSeries['revenue'][te_start:te_end].dropna()
print({'Treino': str(len(tra)) + ' amostras','Teste':str(len(tes)) + ' amostras'})
#
Breve explicação sobre o modelo ARIMA
#
#
#
#
Referência: Introduction to Forecasting with ARIMA in R by Ruslana Dalinina
.
#
# Conforme foi mencionando na seção introdutória, a sigla ARIMA significa no idioma inglês
autoregressive integrated moving average ou modelo auto regressivo integrado de médias móveis no português. Modelos ARIMA não sazonais são geralmente especificados pelos três parâmetros (
p,d,q).
#
# O componente
auto regressivo (AR(p)) representa o grau que a série temporal Y é regressada em
# seus valores anteriores, ou seja, o parâmetro p especifica a quantidade de valores passados usados no
# modelo de regressão. Por exemplo, AR(2) ou ARIMA(2,0,0) é escrito como:
#
# $$Y_t = c + \phi_1y_{t-1} + \phi_2y_{t-2} + e_t $$
#
# Onde φ1 e φ2 representam os coeficientes do modelo regressor.
#
# O componente integrador (I(d)) representa o grau com que os valores da série serão avaliados pelos os valores passodos. A diferenciação remove as mudanças no nível de uma série temporal, eliminando a tendência e a sazonalidade; consequentemente esse processo estabiliza a média da série temporal. Matematicamente, uma operação de diferenciação de ordem 2 ou ARIMA (0,2,0) numa série Y é escrita da seguinte forma:
#
#
# $$ \nabla Y_{t}^2 = \nabla Y_{t}^1 - \nabla Y_{t-1}^1 \\
# \nabla Y_{t}^2 =(Y_t - Y_{t-1})-(Y_{t-1} - Y_{t-2}) \\
# \nabla Y_{t}^2 = Y_t - 2Y_{t-1} + Y_{t-2} $$
#
# Onde ∇Ydt representa a uma diferenciação de grau d a partir do ponto t.
#
# O componente média móvel (MA(q)) representa o erro de regressão da série como uma
# combinação linear dos et termos de erros anteriores. O parâmetro q especifica o número de termos a serem incluídos no modelo. Matematicamente, a média móvel de ordem 2 ou ARIMA(0,0,2) de uma
# série Y é escrita da seguinte forma:
#
# $$ Y_t = c + \theta_i\mathrm{e}_{t-1} + \theta_2\mathrm{e}_{t-2} +\mathrm{e}_t $$
#
# A auto regressão, diferenciação e a média móvel constituem um modelo ARIMA não sazonal que
# pode ser escrito como uma equação linear da seguinte forma:
#
# $$ Y_t = c + \theta_1\nabla Y_{t-1}^d + \theta_p\nabla Y_{t-1}^d + \cdots + \theta_1\mathrm{e}_{t-1} + \theta_q\mathrm{e}_{t-q} + \mathrm{e}_t$$
#
# Perceba que o modelo apresentado acima assume que a série temporal é não sazonal, o que exige
# a dessazonalização de séries temporais sazonais antes de aplicá-las no modelo. Esse processo será
# apresentado a seguir.
#
#
Parametrização do modelo ARIMA
#
# Para se obter uma boa perfomance do modelo ARIMA, é imprescindível que a série temporal seja estacionária, ou seja, que a média, a
# variância e a auto covariância da série sejam invariantes no tempo. Tal premissa faz sentido ao se verificar que
# as equações que regem o modelo ARIMA utilizam valores anteriores da série temporal para modelar seu
# comportamento; assim, modelar uma série estável e com propriedades consistentes garante um resultado em menos
# incertezas.
#
# O teste de
Dickey-Fuller Aumentado ADF (
Augmented Dickey-Fuller) é um teste estatístico que
# determina se uma série temporal é estacionária ou não. Formalmente, o ADF testa a hipótese nula
H0 de 1 ser a raiz da equação característica do modelo auto regressivo. Se a hipótese não for rejeitada, significa que o modelo auto regressivo possui raiz unitária e tal série é não estacionária. O termo
p-value da resposta do teste ADF indica as chances do modelo possuir raiz unitária.
#
#
# In[25]:
import statsmodels.api as sm
res = sm.tsa.adfuller(sales_timeSeries['revenue'].dropna(),regression='ct')
print('p-value:{}'.format(res[1]))
# Por exemplo, o resultado anterior retornou p-value = 0.027, isso siginifca que há 2,7% de chances da série possuir uma raiz unitária e não ser estacionária. A priori este resultado é bom, mas pode ser melhorado se for realizada uma
# diferenciação de ordem 1 na série original. Conforme foi mencionado anteriormente, a diferenciação
# remove as mudanças no nível de uma série temporal, eliminando tendência e sazonalidade e
# consequentemente estabilizando a média da mesma. A diferenciação de ordem m de uma série pode ser
# facilmente realizada utilizando-se a função diff() da bibloteca pandas.
# In[26]:
#ADF-test(differenced-time-series)
res = sm.tsa.adfuller(sales_timeSeries['revenue'].diff().dropna(),regression='c')
print('p-value:{}'.format(res[1]))
# Ao realizar novamente o ADF teste para a série diferenciada, obtém-se um excelente valor de p-valeu, o que indica que a série temporal do faturamento diferenciada em primeira ordem é estacionária. Assim, o parâmetro d do modelo ARIMA já está determinado e deve ser igual à 1.
#
# Após a série temporal ter sido “estacionarizada” por meio da diferenciação, o próximo passo para ajustar o modelo ARIMA é determinar o grau dos termos AR(p) e MA(q) necessários para corrigir as autocorrelações que restaram na série diferenciada. Uma alternativa para a determinação desses termos é tentar algumas combinações aleatórias de e q e verificar qual tem a melhor performance. Entretanto, há uma maneira mais sofisticada de realizar tal tarefa por meio da análise dos gráficos da função de autacorrelação ACF (Autocorrelation function) e função de auto correlação parcial PAFC (Partial Autocorrelation function) da série diferenciada. Os gráficos ACF e PACF são gráficos de barras que representam o grau de associação entre o valor atual da série temporal e seus anteriores. Os gráficos mostram quais são os valores anteriores da série que são úteis para prever valores futuros. Com esse conhecimento, é possível determinar a ordem dos termos p e q do modelo ARIMA. Resumidamente, o
# gráfico AFC está relacionado à média móvel (MA(q)) do modelo e indica a correlação entre Yt e Yk, onde o índice k representa o atraso (lag) temporal de Yk em relação à Yt; enquanto o gráfico PAFC está relacionado à parte auto regressiva (AR(p)) do modelo ARIMA e indica a correlação entre Yt e Yk.
#
# A seguir, exibe-se os gráficos AFC e PAFC da série temporal diferenciada.
#
# In[27]:
fig,ax = plt.subplots(2,1,figsize=(20,10))
fig = sm.graphics.tsa.plot_acf(tra.diff().dropna(), lags=50, ax=ax[0])
fig = sm.graphics.tsa.plot_pacf(tra.diff().dropna(), lags=50, ax=ax[1])
plt.show()
# Picos nos gráficos de AFC e PAFC podem ajudar na determinação dos parâmetros (p,q) do modelo ARIMA, conforme discutido anteriormente. O gráfico AFC indica pontos de correlação significativa nos graus 1 e 7. O gráfico PAFC mostra um relevante pico de grau 1. Esses resultados sugerem que 1 ou 7 são bons valores para q e que 1 é possivelmente o melhor valor para p.
#
# Entretanto, utilizar-se-á o métedo arma_order_select_ic da bibloteca statsmodels.api para buscar os melhores valores de (p,q) dentro do intervalo [1,7] dos números naturais.
# In[28]:
import warnings
warnings.filterwarnings('ignore')
resDiff = sm.tsa.arma_order_select_ic(tra, max_ar=7, max_ma=7, ic='aic', trend='c')
print('ARMA(p,q) =',resDiff['aic_min_order'],'is the best.')
# O resultado anterior indica que 7 e 1 são os melhores valores para p e q respectivamente.
#
#
FItting do modelo ARIMA
# In[29]:
arima = sm.tsa.statespace.SARIMAX(tra,order=(7,1,1),freq='D',seasonal_order=(0,0,0,0),
enforce_stationarity=False, enforce_invertibility=False,).fit()
arima.summary()
# O resultado a seguir indica que o erro residual do modelo possui baixa correlação. Isso indica um bom ajuste do modelo aos dados.
# In[30]:
res = arima.resid
fig,ax = plt.subplots(2,1,figsize=(15,8))
fig = sm.graphics.tsa.plot_acf(res, lags=50, ax=ax[0])
fig = sm.graphics.tsa.plot_pacf(res, lags=50, ax=ax[1])
plt.show()
#
Predição dos dados
# In[31]:
pred = arima.predict(tr_start,te_end)[1:]
fig = plt.figure(figsize=(12, 9))
ax = fig.add_subplot(1, 1, 1, facecolor="10")
plt.plot(tes.index,tes, 'r', label='Série Real')
plt.plot(tes.index,pred[te_start:te_end],'g', label='Série prevista pelo modelo')
plt.legend(loc='upper left')
plt.xlim(te_start,te_end)
plt.title('Série temporal do faturamento da Hotmart no período analisado', fontsize=16)
plt.xlabel('Tempo em dias', fontsize=14)
plt.ylabel('Faturamento em unidades do produto mais caro', fontsize=14)
# In[32]:
from sklearn.metrics import mean_squared_error
pred = arima.predict(tr_start,te_end)[1:]
real = sales_timeSeries.iloc[1:]
print('ARIMA model MSE:{}'.format(mean_squared_error(tes,pred[te_start:te_end])))
# A predição do faturamento da Hotmart para os dias do conjunto de teste, vendas realizadas entre 29/05/2016 e 30/06/2016, demonstra que
não é possível estimar de maneira assertiva o faturamento que empresa teve nos três
# meses seguintes ao último mês mostrado no dataset utilizando a o modelo ARIMA e os dados disponibilizados. Obersave-se que o
MSE (Mean squared error), erro médio quadrático está extramente alto, indicando assim uma baixa qualidade da predição. Essa conclusão pode ser feita ao se analisar o gráfico acima. Ressalta-se que modelos de
machine learning complexos, como as redes neurais recorrentes, podem obter melhores resultados; entranto, a melhoria da predição da série temporal do faturamento por meio de modelos de ML foge do escopo deste estudo.