"""
# Prevendo o LTV de clientes do Varejo

O LTV - “lifetime value” ou “valor vitalício” é uma métrica estatística que estima o lucro líquido da vida de um cliente dentro da empresa. É comum termos clientes mais valiosos que outros, estes se dão pela frequência  que compram ou pela grande receita que geram, e é importante investirmos mais naqueles que geram um maior valor para a empresa.


Este projeto tem como objetivo identificar padrões de comportamento dos clientes do Varejo a fim de prever o LTV do cliente para a empresa. Mais especificamente, foi dado um dataset referênte à 01/2020 até 02/2021 e devemos prever o LTV de cada cliente do dia 25/02/2021 até 24/05/2021. 

### O Dataset
Foi disponibilizado um Dataset que contém as seguintes informações:

- ID_VENDA - Id único para cada registro
- DT_VENDA - Data da venda
- LOJA - Id único da loja em que a venda foi realizada
- QTD_SKU - Quantidade de produtos vendidos
- VALOR - Valor total da venda
- ID_CLIENTE - Id único do cliente
- CANAL - Canal de venda (FIS - Físico, ECM - E-commerce, TELEVENDAS - Telefone, WHATSAPP - WhatsApp, IFOOD - iFood)

O dataset é nomeado por `sales_20_21_train.csv` e foi obtido em 01/10/2021 disponibilizado pela equipe do VLabs para o desafio de previsão do LTV. 
"""
"""
1. [Configurações iniciais](#section-zero)

2. [Obtenção e visualização dos dados](#section-one)

3. [Pré-processamento dos dados](#section-two)

4. [Analise dos dados](#section-three)
    
    4.1. [Quantidade de vendas realizadas por cada loja](#section-three-one)
    
    4.2. [Receita total gerada por cada loja](#section-three-two)
    
    4.3. [Canais de vendas mais recorrentes](#section-three-three)
    
    4.4. [Quantidade de vendas por dia](#section-three-four)
    
    4.5. [Receita total por dia](#section-three-five)
    
    4.6. [Correlação entre as variáveis](#section-three-six)

5. [Segmentando os dados](#section-four)

    5.1.1. [Cálculo da Recência](#section-four-one-one)
    
    5.1.2. [Atribuindo um Score para a Recência](#section-four-one-two)
    
    5.2.1. [Cálculo da Frequência](#section-four-two-one)
    
    5.2.2. [Atribuindo um Score para a Frequência](#section-four-two-two)
    
    5.3.1. [Cálculo da Receita](#section-three-one)
    
    5.3.2. [Atribuindo um Score para a Receita](#section-four-three-two)

6. [Cálculo do RFM](#section-five)

7. [Cálculo do LTV](#section-six)

8. [Determinando features](#section-seven)

9. [Criação do modelo](#section-eight)


"""
"""
<a id="section-zero"></a>

# 1. Configurações iniciais
"""
import pandas as pd

import matplotlib
from matplotlib import pyplot
import matplotlib.pyplot as plt

import seaborn as sns

from sklearn import preprocessing
from sklearn.cluster import KMeans

import numpy as np

import lightgbm as lgb

import plotly as py
import plotly.offline as pyoff
import plotly.graph_objs as go

from hyperopt import hp
from sklearn.metrics import mean_squared_error

from sklearn.model_selection import TimeSeriesSplit


import math
from scipy.stats import norm
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_log_error
from sklearn.model_selection import cross_val_score, cross_val_predict
from time import time

import lightgbm as lgb
import xgboost as xgb
import catboost as ctb
from hyperopt import fmin, tpe, STATUS_OK, STATUS_FAIL, Trials

# Configurando o estilo dos plots
sns.set_style("whitegrid")
!pip install -U kaleido
"""
<a id="section-one"></a>
# 2. Obtenção e visualização dos dados
"""
# Obtendo os dados de treinamento
df = pd.read_csv('../input/VLabs-DC/sales_20_21_train.csv')

df.head()
# Verificando o shape
print("O dataset contém {} linhas e {} colunas.".format(*df.shape))

# Verificando duplicatas
print("E contém {} duplicatas.".format(df.duplicated().sum()))
# Verificando a porcentagem de valores faltantes dentro do Dataframe
percent_missing = df.isnull().sum() * 100 / len(df)

missing_value_df = pd.DataFrame({
                                 'PORCENTAGEM_VALORES_FALTANTES': percent_missing})

display(missing_value_df)
# Visualizando algumas variáveis estatísticas de cada feature contínua
df.describe()
# Visualizando os valores únicos de cada feature
for col in df:
    print('A coluna {} possui {} valores únicos'.format(col, len(df[col].unique())))

"""
<a id="section-two"></a>
# 3. Pré-processamento dos dados
"""
# Convertendo o tipo da coluna DT_VENDA de string para datetime.
df['DT_VENDA'] = pd.to_datetime(df['DT_VENDA'])

df.head()
# Visualizando os dados estatísticos das datas
df['DT_VENDA'].describe(datetime_is_numeric=True)
"""
<a id="section-three"></a>
# 4. Analise dos dados
"""
"""
<a id="section-three-one"></a>

### 4.1. Visualizando a quantidade de vendas realizadas por cada loja
"""
# Dataset para visualizar quantas vendas/receita foram geradas em cada loja durante todo o período do Dataset
store_sells = pd.DataFrame(df.groupby('LOJA').count()['ID_VENDA'])

store_sells.columns = ['QTD_TOTAL_VENDAS']

store_sells.head()
fig, ax = pyplot.subplots(figsize=(50, 15))

sns.barplot(x=store_sells.index, y='QTD_TOTAL_VENDAS', data=store_sells)

plt.ylabel("")

plt.xlabel("LOJA", fontsize=16)

plt.xticks(fontsize=16, rotation=30, horizontalalignment='center')

plt.yticks(fontsize=16, rotation=0)

plt.title("Quantidade de vendas por loja", pad=25, fontsize=24)

plt.show()
"""
<a id="section-three-two"></a>

### 4.2. Visualizando a receita total gerada por cada loja
"""
# Dataset para visualizar a receita total gerada em cada loja durante todo o período do Dataset
store_sells = pd.DataFrame(df.groupby("LOJA").sum()['VALOR'])

store_sells.head()
fig, ax = pyplot.subplots(figsize=(50, 15))

sns.barplot(x=store_sells.index, y='VALOR', data=store_sells)

plt.ylabel("")

plt.xlabel("LOJA", fontsize=16)

plt.xticks(fontsize=16, rotation=30, horizontalalignment='center')

plt.yticks(fontsize=16, rotation=0)

plt.title("Receita total gerada por loja", pad=25, fontsize=24)

plt.show()
"""
<a id="section-three-three"></a>

### 4.3. Analisando os canais de vendas mais recorrentes


"""
# Dataset para visualizar quantas vendas foram realizadas por cada tipo de canal durante todo o período do Dataset
channel_sells = df['CANAL'].value_counts()

channel_sells.head()
fig, ax = pyplot.subplots(figsize=(50, 15))

store_sells_plot = sns.barplot(x=channel_sells.index, y=channel_sells.values)

plt.ylabel("")

plt.xlabel("CANAL", fontsize=16)

plt.xticks(fontsize=16, rotation=30, horizontalalignment='center')

plt.yticks(fontsize=16, rotation=0)

plt.title("Quantidade de compras por CANAL", pad=25, fontsize=24)

plt.show(store_sells_plot)
"""
<a id="section-three-four"></a>

### 4.4. Analisando a quantidade de vendas por dia

"""
# Dataset para visualizar quantas compras foram realizadas por dia durante todo o período do Dataset
date_count_df = df.groupby('DT_VENDA').count()['ID_VENDA']

date_count_df.head()
fig, ax = pyplot.subplots(figsize=(50, 15))

sns.lineplot(data=date_count_df)

plt.ylabel("")

plt.xlabel("Data", fontsize=16)

plt.xticks(fontsize=16, rotation=30, horizontalalignment='center')

plt.yticks(fontsize=16, rotation=0)

plt.title("Quantidade de compras por dia", pad=25, fontsize=24)

plt.show()
"""
<a id="section-three-five"></a>

### 4.5. Analisando a receita total por dia
"""
# Dataset para visualizar a receita gerada por dia durante todo o período do Dataset
date_revenue_sum_df = df.groupby('DT_VENDA').sum()['VALOR']

date_revenue_sum_df.head()
# Dataset para visualizar a média de receita gerada por dia durante todo o período do Dataset
date_revenue_mean_df = df.groupby('DT_VENDA').mean()['VALOR']

date_revenue_mean_df.head()
fig, ax = pyplot.subplots(figsize=(50, 15))

sns.lineplot(data=date_revenue_sum_df)

sns.lineplot(data=date_revenue_mean_df)

plt.ylabel("")

plt.xlabel("Data", fontsize=16)

plt.xticks(fontsize=16, rotation=30, horizontalalignment='center')

plt.yticks(fontsize=16, rotation=0)

plt.title("Receita total por dia", pad=25, fontsize=24)

plt.show()
"""
<a id="section-three-six"></a>

### 4.6. Analisando a correlação entre as variáveis
"""
"""
Iremos analisar a correlação entre as variáveis contínuas do Dataset. 

A **correlação** é uma análise bivariada que mede a força da associação entre duas variáveis ​​e a direção da relação. Em termos da força da relação, o valor do coeficiente de correlação varia entre +1 e -1. Um valor de ± 1 indica um grau de associação entre as duas variáveis. À medida que o valor do coeficiente de correlação vai para 0, a relação entre as duas variáveis ​​será mais fraca. 

Existem vários métodos de correlação, de Perason, Spearman e Kendall. 

Para o método de correlação iremos utilizar a correlação de Pearson.
"""
# Computa a matriz de correlação
corr = df.corr()


fifg, ax = plt.subplots(figsize=(11, 9))

# Gerando um colormap customizado
cmap = sns.diverging_palette(230, 20, as_cmap=True)

sns.heatmap(corr, cmap=cmap, center=0,
            square=True, linewidths=.5, cbar_kws={"shrink": .5})
"""
É interessante também analisarmos a relação que a variável *CANAL* tem com o Valor de cada compra.

Para que possamos utilizar esse algorítmo, precisamos criar labels para cada variável categórica. Começaremos por este passo.
"""
# Conversão das variáveis categóricas em numéricas
df_labeled = pd.get_dummies(df)

df_labeled.head()
# Computa a matriz de correlação
corr = df_labeled[['VALOR', 'CANAL_ECM', 'CANAL_FIS', 'CANAL_IFOOD', 'CANAL_TELEVENDAS', 'CANAL_WHATSAPP']].corr()


fifg, ax = plt.subplots(figsize=(11, 9))

# Gerando o mapeamento de cores
cmap = sns.diverging_palette(230, 20, as_cmap=True)

sns.heatmap(corr, cmap=cmap, center=0,
            square=True, linewidths=.5, cbar_kws={"shrink": .5})
"""
<a id="section-four"></a>

### 5. Segmentando os dados

Para o problema de LTV podemos criar várias segmentações. Caso a Retenção dos clientes nas lojas seja interessante, o dado pode ser segmentado baseado na probabilidade de Churn, entre outros métodos. Para este caso, iremos trabalhar com RFM que diz respeito à Recency - Frequency - Monetary Value que em tradução livre se diz respeito à Recência - Frequência  e Valor monetário. 

 - Baixo valor de RFM: Clientes que são menos ativos que outros e não realizam compras frequentes e que gastam pouco - normalmente são visitantes. 

 - Valor médio de RFM: Representa o cliente médio. Ele compra com certa frequência e de forma recente e gera um valor considerável de receita, mas que não chega a ser muito alto. 

 - Alto valor de RFM: Cliente que gera muita receita de forma frequente e de forma recente, esses são os clientes que você não quer perder.

Para isso, iremos calcular a Recência, Frequência e Valor monetário de cada cliente e iremos aplicar um processo de aprendizado não-supervisionado para gerar diferentes cluster para cada valor individual.

Será gerado um Score final que representa o RFM somando cada cluster das variáveis Recência, Frequência e Valor monetário.
"""
# Dataframe que contém os clientes únicos
users_df = pd.DataFrame(df["ID_CLIENTE"].unique(), columns=["ID_CLIENTE"])


users_df.head()
"""
<a id="section-four-one-one"></a>

### 5.1.1 Cálculo da Recência

Iremos calcular a recência do cliente que nada mais é que a diferença de dias em que o usuário realizou sua última transação em relação ao dia mais recente do *Dataset* - 24/02/2021. Quanto maior a recência, menos recente é as compras deste cliente. 
"""
df_max_purchase = df.groupby('ID_CLIENTE')['DT_VENDA'].max().reset_index()

df_max_purchase.columns = ['ID_CLIENTE','DT_COMPRA_MAIS_RECENTE']

df_max_purchase['DT_COMPRA_MAIS_RECENTE'] = pd.to_datetime(df_max_purchase['DT_COMPRA_MAIS_RECENTE'])

df_max_purchase.head()
# Compara a última transação do conjunto de dados com as datas da última transação dos IDs de clientes individuais.
df_max_purchase['RECENCIA'] = (df_max_purchase['DT_COMPRA_MAIS_RECENTE'].max() - df_max_purchase['DT_COMPRA_MAIS_RECENTE']).dt.days

df_max_purchase.head()
# mesclar este dataframe ao nosso dataframe de usuários únicos
users_df = pd.merge(users_df, df_max_purchase[['ID_CLIENTE','RECENCIA']], on='ID_CLIENTE')

users_df.head()
"""
<a id="section-four-one-two"></a>

### 5.1.2 Atribuindo um Score para a Recência

Iremos aplicar a clusterização por K-means para atribuir um score para a recência. Para que isto ocorra de forma otimizada, iremos verificar quantos cluster são necessários para termos o melhor trade-off do algorítmo K-means utilizando o método de Elbow. 

"O método Elbow se trata de uma técnica interessante para encontrar o valor ideal do parâmetro k. Basicamente o que o método faz é testar a variância dos dados em relação ao número de clusters. É considerado um valor ideal de k quando o aumento no número de clusters não representa um valor significativo de ganho." - Mineirando dados
"""
sse={} # Erro

recency_df = users_df[['RECENCIA']]

for k in range(1, 10):
    kmeans = KMeans(n_clusters=k, max_iter=1000).fit(recency_df)

    # Nome dos clusters relativos ao valor da recência
    recency_df["CLUSTERS"] = kmeans.labels_  

    # Erro correspondente aos clusters
    sse[k] = kmeans.inertia_ 



fig, ax = pyplot.subplots(figsize=(30, 15))

sns.lineplot(x=list(sse.keys()), y=list(sse.values()))

plt.xlabel("Número de Cluster - K")

plt.xticks(fontsize=16, rotation=0, horizontalalignment='center')

plt.yticks(fontsize=16, rotation=0)

plt.show()


"""
Com isto, podemos determinar que K = 4 nos dá uma clusterização mais otimizada.
"""
# Construindo 4 clusters para a recência e colocando no dataframe
kmeans = KMeans(n_clusters=4)

users_df['CLUSTER_RECENCIA'] = kmeans.fit_predict(users_df[['RECENCIA']])

users_df.head()
# Visualizando dados estatísticos da recência para cada cluster criado.
users_df.groupby('CLUSTER_RECENCIA')['RECENCIA'].describe()
"""
Para o cálculo do RFM é interassante que os clusters em relação à recência estejam ordenados de forma decrescente em relação a recência, em que o maior cluster corresponda ao cliente com uma boa recência (com um menor valor de recência). 
"""
# Método que ordena o cluster
def order_cluster(cluster_field_name, target_field_name, df, ascending):
    df_new = df.groupby(cluster_field_name)[target_field_name].mean().reset_index()

    df_new = df_new.sort_values(by=target_field_name,ascending=ascending).reset_index(drop=True)

    df_new['index'] = df_new.index

    df_final = pd.merge(df,df_new[[cluster_field_name,'index']], on=cluster_field_name)

    df_final = df_final.drop([cluster_field_name],axis=1)

    df_final = df_final.rename(columns={"index":cluster_field_name})
    
    return df_final
# Ordenando os clusters da recência
users_df = order_cluster('CLUSTER_RECENCIA', 'RECENCIA', users_df, False)

users_df.head()

# Visualizando dados estatísticos da recência para cada cluster criado.
users_df.groupby('CLUSTER_RECENCIA')['RECENCIA'].describe()

# Visualizando a quantidade de clientes pertencentes a cada cluster
display(users_df['CLUSTER_RECENCIA'].value_counts(ascending=True))
# Criando um label pra a melhor visualização dos dados relativos aos clusters da recência
users_df['LABEL_RECENCIA'] = 'Alta'

users_df.loc[users_df['CLUSTER_RECENCIA'] == 0, 'LABEL_RECENCIA'] = 'Muito Baixa' 

users_df.loc[users_df['CLUSTER_RECENCIA'] == 1, 'LABEL_RECENCIA'] = 'Baixa' 

users_df.loc[users_df['CLUSTER_RECENCIA'] == 2, 'LABEL_RECENCIA'] = 'Média' 

recency_values_count = users_df['LABEL_RECENCIA'].value_counts(ascending=True)
# Visualizando a quantidade de clientes pertencentes a cada cluster
display(users_df['LABEL_RECENCIA'].value_counts(ascending=True))
fig, ax = pyplot.subplots(figsize=(50, 15))

label_recency_plot = sns.barplot(x=recency_values_count.index, y=recency_values_count)

plt.ylabel("")

plt.xlabel("Cluster", fontsize=16)

plt.xticks(fontsize=16, rotation=30, horizontalalignment='center')

plt.yticks(fontsize=16, rotation=0)

plt.title("Agrupamento das categorias de Recência", pad=50, fontsize=24)

plt.show(label_recency_plot)
"""
<a id="section-four-two-one"></a>

### 5.2.1. Cálculo da Frequência

A frequência se dá com base no total de compras que cada cliente realizou durante todo o período do *Dataset*.

"""
# Recupera a quantidade de compras por cliente 
frequency_df = df.groupby('ID_CLIENTE')['DT_VENDA'].count().reset_index()

frequency_df.columns = ['ID_CLIENTE','FREQUENCIA']

frequency_df.head()
# Mescla este dataframe ao nosso dataframe de usuários únicos
users_df = pd.merge(users_df, frequency_df, on='ID_CLIENTE')

users_df.head()
"""
<a id="section-four-two-two"></a>

### 5.2.2. Atribuindo um Score para a Frequência
Semelhante ao caso da Recência, iremos clusterizar os clientes com base em sua frequência. Quanto maior a frequência, mais importante se torna este cliente em relação ao RFM.
"""
"""
Primeiro iremos determina o número K de clusters ideal utilizando o método Elbow.
"""
sse={} # Erro

recency_df = users_df[['FREQUENCIA']]

for k in range(1, 10):
    kmeans = KMeans(n_clusters=k, max_iter=1000).fit(recency_df)

    # Nome dos clusters relativos ao valor da recência
    recency_df["CLUSTERS"] = kmeans.labels_  

    # Erro correspondente aos clusters
    sse[k] = kmeans.inertia_ 



fig, ax = pyplot.subplots(figsize=(30, 15))

sns.lineplot(x=list(sse.keys()), y=list(sse.values()))

plt.xlabel("Número de Cluster - K")

plt.xticks(fontsize=16, rotation=0, horizontalalignment='center')

plt.yticks(fontsize=16, rotation=0)

plt.show()
"""
Com isto, podemos determinar que K = 4 nos dá uma clusterização mais otimizada.
"""
# Aplicando K-means na coluna de frequência
kmeans = KMeans(n_clusters = 4)

users_df['CLUSTER_FREQUENCIA'] = kmeans.fit_predict(users_df[['FREQUENCIA']])

# Visualizando os dados estatísticos de cada cluster em relação a frequência.
users_df.groupby('CLUSTER_FREQUENCIA')['FREQUENCIA'].describe()

"""
Para o cálculo do RFM geral é interassante que os clusters em relação à frequência estejam ordenados de forma crescente, em que o maior cluster corresponda ao cliente com uma boa frequência (com um maior valor de compras dentro do período determinado). 
"""
users_df = order_cluster('CLUSTER_FREQUENCIA', 'FREQUENCIA', users_df, True)

# Visualizando os dados estatísticos de cada cluster em relação a frequência.
users_df.groupby('CLUSTER_FREQUENCIA')['FREQUENCIA'].describe()
# Visualizando a quantidade de clientes pertencentes a cada cluster
display(users_df['CLUSTER_FREQUENCIA'].value_counts(ascending=True))
# Definindo um label para cada cluster para visualização
users_df['LABEL_FREQUENCIA'] = 'Alta'

users_df.loc[users_df['CLUSTER_FREQUENCIA'] == 0, 'LABEL_FREQUENCIA'] = 'Muito Baixa' 

users_df.loc[users_df['CLUSTER_FREQUENCIA'] == 1, 'LABEL_FREQUENCIA'] = 'Baixa' 

users_df.loc[users_df['CLUSTER_FREQUENCIA'] == 2,'LABEL_FREQUENCIA'] = 'Média' 
# Visualizando a quantidade de clientes pertencentes a cada cluster
display(users_df['LABEL_FREQUENCIA'].value_counts(ascending=True))
fig, ax = pyplot.subplots(figsize=(50, 15))

label_frequency_plot = sns.barplot(x=users_df['LABEL_FREQUENCIA'].value_counts(ascending=True).index, y=users_df['LABEL_FREQUENCIA'].value_counts(ascending=True))

plt.ylabel("")

plt.xlabel("Cluster", fontsize=24)

plt.xticks(fontsize=16, rotation=30, horizontalalignment='center')

plt.yticks(fontsize=16, rotation=0)

plt.title("Agrupamento das categorias de frequência", pad=50, fontsize=24)

plt.show(label_frequency_plot)
"""
<a id="section-four-three-one"></a>

### 5.3.1 Cálculo da Receita

Iremos analisar como se comportam a receita (valor total) gasto nas lojas por cliente durante todo o período do Dataset.

"""
# Recupera a receita total das compras por cliente 
client_revenue = df[['ID_CLIENTE', 'VALOR']].groupby("ID_CLIENTE").sum()

client_revenue.head()
# Cria um dataset com a receita total das compras por cliente 
client_revenue_df = client_revenue.reset_index()[['ID_CLIENTE', 'VALOR']]

client_revenue_df = client_revenue_df.rename(columns={"ID_CLIENTE": "ID_CLIENTE", "VALOR": "RECEITA"}, errors="raise")

client_revenue_df.head()
# Mescla este dataframe ao nosso dataframe de usuários únicos
users_df = pd.merge(users_df, client_revenue_df, on='ID_CLIENTE')

users_df.head()
# Analisando a distribuição de receita

fig, ax = pyplot.subplots(figsize=(50, 10))

sns.histplot(data=users_df, x="RECEITA", bins=150, kde=True)

plt.ylabel("")

plt.xlabel("Receita", fontsize=16)

plt.xticks(fontsize=16, rotation=30, horizontalalignment='center')

plt.yticks(fontsize=16, rotation=0)

plt.title("Histograma da receita", pad=25, fontsize=24)

plt.show()
"""
Em primeiro momento iremos preservar todos os dados, por mais que tenhamos um usuário com um gasto exorbitante. 
"""
"""
<a id="section-four-three-two"></a>

### 5.3.2. Atribuindo um Score para a Receita
Assim como nos casos anteriores, iremos clusterizar os clientes com base na receita gerada e determinar o número K ideal de Clusters utilizando o método Elbow.
"""
sse={} # Erro

recency_df = users_df[['RECEITA']]

for k in range(1, 10):
    kmeans = KMeans(n_clusters=k, max_iter=1000).fit(recency_df)

    # Nome dos clusters relativos ao valor da receita
    recency_df["CLUSTERS"] = kmeans.labels_  

    # Erro correspondente aos clusters
    sse[k] = kmeans.inertia_ 



fig, ax = pyplot.subplots(figsize=(30, 15))

sns.lineplot(x=list(sse.keys()), y=list(sse.values()))

plt.xlabel("Número de Cluster - K")

plt.xticks(fontsize=16, rotation=0, horizontalalignment='center')

plt.yticks(fontsize=16, rotation=0)

plt.show()
"""
Semelhante aos casos anteriores, K = 4 nos dá um número de clusters ideal. 
"""
# Aplicando a clusterização
kmeans = KMeans(n_clusters=4)

users_df['CLUSTER_RECEITA'] = kmeans.fit_predict(users_df[['RECEITA']])

#Visualizando dados estatísticos dos clusters da receita
users_df.groupby('CLUSTER_RECEITA')['RECEITA'].describe()
"""
Para este caso, quanto maior o cluster, maior deve ser a receita total gerada. 
"""
# Ordena os clusters de forma crescente, em que o último cluster terá o maior valor de receita
users_df = order_cluster('CLUSTER_RECEITA', 'RECEITA', users_df, True)


#Visualizando dados estatísticos dos clusters da receita
users_df.groupby('CLUSTER_RECEITA')['RECEITA'].describe()
# Visualizando a quantidade de clientes pertencentes a cada cluster
users_df['CLUSTER_RECEITA'].value_counts(ascending=True)
# Definindo um label para cada cluster para visualização
users_df['LABEL_RECEITA'] = 'Alta'

users_df.loc[users_df['CLUSTER_RECEITA'] == 0, 'LABEL_RECEITA'] = 'Muito Baixa' 

users_df.loc[users_df['CLUSTER_RECEITA'] == 1, 'LABEL_RECEITA'] = 'Baixa' 

users_df.loc[users_df['CLUSTER_RECEITA'] == 2,'LABEL_RECEITA'] = 'Média' 
# Visualizando a quantidade de clientes pertencentes a cada cluster
users_df['LABEL_RECEITA'].value_counts(ascending=True)
fig, ax = pyplot.subplots(figsize=(50, 15))

sns.barplot(x=users_df['LABEL_RECEITA'].value_counts(ascending=True).index, y=users_df['LABEL_RECEITA'].value_counts(ascending=True))

plt.ylabel("")

plt.xlabel("Cluster", fontsize=16)

plt.xticks(fontsize=16, rotation=0, horizontalalignment='center')

plt.yticks(fontsize=16, rotation=0)

plt.title("Agrupamento das categorias de receita", pad=25, fontsize=24)

plt.show()
"""
<a id="section-five"></a>

### 6. Cálculo do RFM

Nós possuímos um Cluster Score para recência, frequência e receita, com isto, iremos criar um score geral que engloba esses três Cluster principais gerando o nosso RFM.

 O RFM é dado pela soma de cada cluster indivualmente.
"""
# Calcula a pontuação geral e use a média para ver os detalhes
users_df['RFM'] = users_df['CLUSTER_RECENCIA'] + users_df['CLUSTER_FREQUENCIA'] + users_df['CLUSTER_RECEITA']

users_df.groupby('RFM')['RECENCIA','FREQUENCIA','RECEITA'].mean()
"""


Com isso, podemos perceber que, quanto maior o Score RFM, mais confiável é o consumidor. 
"""
# Visualizando a quantidade de clientes pertencentes a cada cluster
users_df['RFM'].value_counts(ascending=True)
users_df['SEGMENTO'] = 'Baixo'
users_df.loc[users_df['RFM'] > 3,'SEGMENTO'] = 'Médio' 
users_df.loc[users_df['RFM'] > 6,'SEGMENTO'] = 'Alto' 

users_df.head()
# Visualizando a quantidade de clientes pertencentes a cada cluster
users_df['SEGMENTO'].value_counts(ascending=True)
# Visualizando os dados por Segmento
fig, ax = pyplot.subplots(figsize=(50, 15))

sns.barplot(x=users_df['SEGMENTO'].value_counts(ascending=True).index, y=users_df['SEGMENTO'].value_counts(ascending=True))

plt.ylabel("")

plt.xlabel("Cluster", fontsize=16)

plt.xticks(fontsize=16, rotation=30, horizontalalignment='center')

plt.yticks(fontsize=16, rotation=0)

plt.title("Agrupamento das categorias do RFM", pad=25, fontsize=24)

plt.show()
"""
<a id="section-six"></a>

### 7. Cálculo do LTV
Agora entramos na parte que interessa, iremos calcular o valor de LTV de cada cliente durante todo o período do Dataset.


"""
"""
Temos que o LTV é dado pela Receita total - Custo total.

Como no dataset não temos a informação do Custo por cliente, iremos considerar que o Lifetime Value será determinado apenas pela receita total do cliente.
"""
users_df.head()
"""
Agora, iremos analisar a relação entre o RFM e as categorias de Recência, Frequência e Receita. 
"""
recency_plot_data = [
    go.Scatter(
        x=users_df.query("SEGMENTO == 'Baixo'")['RFM'],
        y=users_df.query("SEGMENTO == 'Baixo'")['CLUSTER_RECENCIA'],
        mode='markers',
        name='Low',
        marker= dict(size= 7,
            line= dict(width=1),
            color= 'blue',
            opacity= 0.8
           )
    ),
        go.Scatter(
        x=users_df.query("SEGMENTO == 'Médio'")['RFM'],
        y=users_df.query("SEGMENTO == 'Médio'")['CLUSTER_RECENCIA'],
        mode='markers',
        name='Mid',
        marker= dict(size= 9,
            line= dict(width=1),
            color= 'green',
            opacity= 0.5
           )
    ),
        go.Scatter(
        x=users_df.query("SEGMENTO == 'Alto'")['RFM'],
        y=users_df.query("SEGMENTO == 'Alto'")['CLUSTER_RECENCIA'],
        mode='markers',
        name='High',
        marker= dict(size= 11,
            line= dict(width=1),
            color= 'red',
            opacity= 0.9
           )
    ),
]

plot_layout = go.Layout(
        yaxis= {'title': "Classificação da Recência"},
        xaxis= {'title': "Score do RFM"},
        title='LTV'
    )
fig = go.Figure(data=recency_plot_data, layout=plot_layout)
fig.show(renderer='png')
"""
Para o cluster de Recência temos que clientes no perfil de Baixo RFM tendem a estar em todos os 4 níveis de Recência. Já para os clientes com perfis de Médio RFM tendem a estar nos níveis 1 a 3 de Recênica. Por outro lado, os clientes com Alto perfil de RFM tendem a estar nos níveis 2 a 3 de Recência.
"""
frequency_plot_data = [
    go.Scatter(
        x=users_df.query("SEGMENTO == 'Baixo'")['RFM'],
        y=users_df.query("SEGMENTO == 'Baixo'")['CLUSTER_FREQUENCIA'],
        mode='markers',
        name='Low',
        marker= dict(size= 7,
            line= dict(width=1),
            color= 'blue',
            opacity= 0.8
           )
    ),
        go.Scatter(
        x=users_df.query("SEGMENTO == 'Médio'")['RFM'],
        y=users_df.query("SEGMENTO == 'Médio'")['CLUSTER_FREQUENCIA'],
        mode='markers',
        name='Mid',
        marker= dict(size= 9,
            line= dict(width=1),
            color= 'green',
            opacity= 0.5
           )
    ),
        go.Scatter(
        x=users_df.query("SEGMENTO == 'Alto'")['RFM'],
        y=users_df.query("SEGMENTO == 'Alto'")['CLUSTER_FREQUENCIA'],
        mode='markers',
        name='High',
        marker= dict(size= 11,
            line= dict(width=1),
            color= 'red',
            opacity= 0.9
           )
    ),
]

plot_layout = go.Layout(
        yaxis= {'title': "Classificação da Frequência"},
        xaxis= {'title': "Score do RFM"},
        title='LTV'
    )
fig = go.Figure(data=frequency_plot_data, layout=plot_layout)
fig.show(renderer="png")
"""
Para o cluster de Frequência temos que clientes no perfil de Baixo RFM tendem a estar em todos os 3 primeiros níveis de Frequência. Já para os clientes com perfis de Médio RFM tendem a estar em todos os níveis de Frequência. Por outro lado, os clientes com Alto perfil de RFM tendem a estar nos níveis 2 a 3 de Frequência.
"""
#revenue_plot_data = [
#    go.Scatter(
#        x=users_df.query("SEGMENTO == 'Baixo'")['RFM'],
#        y=users_df.query("SEGMENTO == 'Baixo'")['CLUSTER_RECEITA'],
#        mode='markers',
#        name='Low',
#        marker= dict(size= 7,
#           line= dict(width=1),
#            color= 'blue',
#            opacity= 0.8
#           )
#    ),
#        go.Scatter(
#        x=users_df.query("SEGMENTO == 'Médio'")['RFM'],
#        y=users_df.query("SEGMENTO == 'Médio'")['CLUSTER_RECEITA'],
#        mode='markers',
#        name='Mid',
#        marker= dict(size= 9,
#            line= dict(width=1),
#            color= 'green',
#            opacity= 0.5
#           )
#    ),
#        go.Scatter(
#        x=users_df.query("SEGMENTO == 'Alto'")['RFM'],
#        y=users_df.query("SEGMENTO == 'Alto'")['CLUSTER_RECEITA'],
#        mode='markers',
#        name='High',
#        marker= dict(size= 11,
#            line= dict(width=1),
#            color= 'red',
#            opacity= 0.9
#           )
#    ),
#]

#plot_layout = go.Layout(
#        yaxis= {'title': "Classificação da Receita"},
#        xaxis= {'title': "Score do RFM"},
#        title='LTV'
#    )
#fig = go.Figure(data=revenue_plot_data, layout=plot_layout)
#fig.show(renderer="png")
"""
Para o cluster de Receita temos que clientes no perfil de Baixo RFM tendem a estar no nível 0 a 2 de Receita. Já para os clientes com perfis de Médio RFM tendem a estar em todos níveis de Receita. Enquanto isso, os clientes com Alto perfil de RFM tendem a estar nos níveis 2 a 3 de Receita.
"""
"""
<a id="section-seven"></a>

### 8. Determinando features
"""
"""
## Estratégia

Para que possamos prever com precisão o LTV de cada cliente nos próximos 3 meses do Dataset iremos calcular a tendência de consumo de cada cliente nos períodos a serem determinados. Como estratégia de segmentação dos dados iremos utilizar o método de **janela deslizante (rolling window)**.

Usaremos para o treinamento do modelo todo o Dataset menos as últimas compras realizadas nos últimos 90 dias - elas serão utilizadas como a validação do modelo (e conseguequentemente o resultado final).

## Tendência
A tendência de compra de cada cliente se dá pela quantidade de compras realizadas, o desvio padrão das compras, a média, a mediana e variância - tudo isso em um perído de X dias.  Nesta etapa usaremos períodos de 30 dias para calcular a tendência em cada perído, sendo assim, teremos colunas com dados estatísticos de para 30, 60, 90, 120, 150, 180 dias. Além disso, como iremos utilizar o método de janela deslizante, para cada cliente e para cada mês associado a este cliente haverá essas features de tendência, assim, teremos mais dados para poder trabalhar o modelo.

"""
# Seleção das features
featured_df = users_df[['ID_CLIENTE', 'RECENCIA', 'CLUSTER_RECENCIA', 'FREQUENCIA', 'CLUSTER_FREQUENCIA', 'RECEITA', 'CLUSTER_RECEITA', 'RFM']]

featured_df.head()
# Criando um dataframe com a coluna ANO_MES_VENDA para ser usado como série temporal mensal
df_month = df

df_month['ANO_MES_VENDA'] = df_month['DT_VENDA'].map(lambda date: 100*date.year + date.month)

df_month.head()
"""
Neste momento iremos criar as features relativas a tendência e sazonalidade nos dados de cada cliente além da segmentação dos dados por janela deslizante.
"""
"""
Agora iremos gerar as features relativas à tendência de consumo de cada cliente no determinado Ano/Mês analisando os próximos 3, 6, 9 e 12 meses de consumo. 
"""
# Função que gera as features relativas a tendência no dataset
def get_trend_for_each_period(reference_df, months):
    _df = reference_df[['ID_CLIENTE', 'VALOR']].groupby('ID_CLIENTE').sum().reset_index()
    
    aux_dict = {
        3 : pd.to_timedelta(30,unit='d'),
        6 : pd.to_timedelta(60,unit='d'),
        9 : pd.to_timedelta(90,unit='d'),
        12: pd.to_timedelta(360,unit='d')
    }
    
    for month in months: 
        date_condition = reference_df['DT_VENDA'].max() - aux_dict[month]

        _df[f'RECEITA_{month}m'] = reference_df.where(reference_df['DT_VENDA'] > date_condition) \
                                    .sort_values(['ID_CLIENTE','ANO_MES_VENDA'], ascending=True) \
                                    .groupby('ID_CLIENTE')['VALOR'] \
                                    .sum() \
                                    .fillna(0)

        _df[f'MEDIA_{month}m'] = reference_df.where(reference_df['DT_VENDA'] > date_condition) \
                                    .sort_values(['ID_CLIENTE','ANO_MES_VENDA'], ascending=True) \
                                    .groupby('ID_CLIENTE')['VALOR'] \
                                    .mean() \
                                    .fillna(0)

        _df[f'DESVIO_PRADRAO_{month}m'] = reference_df.where(reference_df['DT_VENDA'] > date_condition) \
                                            .sort_values(['ID_CLIENTE','ANO_MES_VENDA'], ascending=True) \
                                            .groupby('ID_CLIENTE')['VALOR'] \
                                            .std() \
                                            .fillna(0)

        _df[f'VARIANCIA_{month}m'] = reference_df.where(reference_df['DT_VENDA'] > date_condition) \
                                        .sort_values(['ID_CLIENTE','ANO_MES_VENDA'], ascending=True) \
                                        .groupby('ID_CLIENTE')['VALOR'] \
                                        .var() \
                                        .fillna(0)
    return _df.fillna(0)
list_of_months = [3, 6, 9, 12]

# client_df = get_trend_for_each_period(df_month, list_of_months)
"""
### Seleção das features

"""
features = ['RECEITA_3m', 'RECENCIA', 'CLUSTER_RECENCIA',
            'FREQUENCIA', 'CLUSTER_FREQUENCIA','RECEITA', 'CLUSTER_RECEITA','RFM',
            'MEDIA_3m', 'DESVIO_PRADRAO_3m','VARIANCIA_3m',
            'RECEITA_6m', 'MEDIA_6m', 'DESVIO_PRADRAO_6m',
            'VARIANCIA_6m', 'RECEITA_9m', 'MEDIA_9m', 'DESVIO_PRADRAO_9m',
            'VARIANCIA_9m', 'RECEITA_12m', 'MEDIA_12m', 'DESVIO_PRADRAO_12m',
            'VARIANCIA_12m']

target = ['VALOR']
# Criando as novas features no nosso dataset para 3, 6, 9 e 12 meses 
# Utilizando os 3 últimos meses como o modelo de predição da saída dos dias 24/02/2021 até 25/05/2021
predict_df = get_trend_for_each_period(df_month[df_month['ANO_MES_VENDA'] >= 202012], list_of_months)

# Com os dados anteriores ao período de predição iremos utilizar para realizar o treinamento
train_test_df = get_trend_for_each_period(df_month[df_month['ANO_MES_VENDA'] < 202012], list_of_months)
predict_df = predict_df.merge(featured_df, how="inner", on="ID_CLIENTE")

train_test_df = train_test_df.merge(featured_df, how="inner", on="ID_CLIENTE")
# Visualizando os dados
train_test_df.head()
# Visualizando os dados
predict_df.head()
"""
<a id="section-eight"></a>

### 9. Criação do modelo
"""
"""
Para a criação do modelo iremos utilizar o modelo LGBM para realizar a regressão. Como auxilio, iremos utilizar o método TimeSeriesSplit do Scikit-learn para realizamos a Cross Validation dos nossos dados. 
"""
from sklearn.ensemble import RandomForestRegressor

# Inicializando o modelo com 100 estimators
rf = RandomForestRegressor(n_estimators = 100, random_state = 42)

X_train, X_test, y_train, y_test = train_test_split(train_test_df[features], train_test_df[target], test_size=0.33, random_state=42)


# Treinando o modelo
rf.fit(X_train, y_train)
predict_df['prediction'] = rf.predict(predict_df[features])
predict_df[['VALOR', 'prediction']]
"""
### 9.2. Avaliando o modelo
"""
# Avaliando o modelo com RMSE

rmse = mean_squared_error(predict_df['VALOR'], predict_df['prediction'], squared=False)

print('O RMSE dos dados de validação é: ', rmse)

predict_df.groupby('ID_CLIENTE').sum().reset_index()
# Gerando a saída 
output_df = predict_df.groupby('ID_CLIENTE').sum().reset_index()[['ID_CLIENTE', 'prediction']]

output_df.columns = ['ID_CLIENTE', 'VALOR']

output_df[output_df['VALOR'] < 0] = 0

output_df = output_df.round(2)

output_df.head()
"""
Está na hora de gerar a saída :)
"""
sample_submission = pd.read_csv('../input/VLabs-DC/sample_submission.csv')

sample_submission.head()
result_submission = sample_submission.merge(output_df, on='ID_CLIENTE', how='left')

result_submission.head()
result_submission.shape
result_submission = result_submission[['ID_CLIENTE', 'VALOR_y']]

result_submission.columns = ['ID_CLIENTE', 'VALOR']

result_submission.head()
result_submission.fillna(0).to_csv('./result.csv', index=False)
"""
Com isto, temos o resultado da predição do LTV próximos 90 dias de cada cliente. 

Próximos passos: 

- Evoluir a análise da série temporal;
- Complementar as features com valores de tendência em cada período de compras;
- Melhorar a predição com vários algorítmos de Regressão estudando os melhores parâmetros;
"""