<jupyter_start><jupyter_text>Bibliotecas<jupyter_code># Importar as bibliotecas
# Análise Exploratória de dados 
import pandas as pd  
import numpy as np

# Visualização
from matplotlib import pyplot as plt  
import seaborn as sns 
 
# Pré-processamento e modelos    
from sklearn.preprocessing import MinMaxScaler 
from imblearn.over_sampling  import SMOTE
from sklearn.ensemble import RandomForestClassifier
from sklearn.feature_selection import RFE
from sklearn.metrics import classification_report, confusion_matrix, roc_auc_score
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import StratifiedKFold
from sklearn.model_selection import cross_val_score
from sklearn.metrics import f1_score
from sklearn.feature_selection import SelectKBest, chi2, f_classif
from sklearn import metrics
from sklearn.model_selection import train_test_split, cross_validate, GridSearchCV
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.naive_bayes import GaussianNB
from lightgbm import LGBMClassifier
import lightgbm as lgbm


pd.set_option("display.max_columns",None) # mostrar todas as colunas 
pd.set_option("display.max_rows",None) # mostrar todas as linhas<jupyter_output><empty_output><jupyter_text>1. Entendimento do negócio 1.1. Situação- Os clientes são a base de uma instituição financeira. Portanto, é muito importante para uma instituição prever se os seus clientes irão ou não abandonar (churn) o seu serviço. Assim, quanto melhor for a predição, melhor será o direcionamento, por exemplo, em ações financeiras e em ações de marketing para evitar que o cliente abandone a instituição. 1.2. Objetivo:- Prever o abandono de clientes (churn) de uma instituição financeira 1.3. Etapas usadas para solucionar o problema acima:- Entendimento do negócio (que foi descrito acima);- Entendimento dos dados;- Preparação dos dados; - Modelagem e Avaliação;- Previsão. 2. Entendimento dos dados (Explorar os conjuntos de dados)- 2.1. Importar os conjuntos de dados;- 2.2. Informações sobre os conjuntos de dados;- 2.3. Estatística das variáveis. 2.1. Importar os conjuntos de dados (datasets)  2.1.1. Importar o conjunto de dados (dataset) nomeado Abandono_clientes (dados de treino)<jupyter_code>df_treino = pd.read_csv("Abandono_clientes.csv", na_values="na")<jupyter_output><empty_output><jupyter_text>2.1.2. Importar a conjunto de dados (dataset) nomeado Abandono_teste (dados teste). Representam dados que nunca foram vistos<jupyter_code>df_teste = pd.read_csv("Abandono_teste.csv",na_values="na")<jupyter_output><empty_output><jupyter_text>2.2. Informações sobre os conjuntos de dados 2.2.1. Dados de treino<jupyter_code>df_treino.head() # visualizar as cinco primeiras linhas do conjunto de dados treino
display(df_treino.dtypes) # tipo de cada coluna do conjunto de dados de treino<jupyter_output><empty_output><jupyter_text>2.2.2 Dados de teste<jupyter_code>df_teste.head() # visualizar as cinco primeiras linhas do conjunto de dados<jupyter_output><empty_output><jupyter_text>Conforme mostra acima, todas as informações estão em uma única entrada de coluna. Isso ocorreu porque o read_csv usa um parâmetro sep onde o padrão é ",". Acima as colunas estão separadas por ";", assim, usar sep=";"<jupyter_code>df_teste = pd.read_csv("Abandono_teste.csv",sep=';', na_values="na")  
df_teste.head()
display(df_teste.dtypes) # tipo de cada coluna do conjunto de dados<jupyter_output><empty_output><jupyter_text>Nos conjuntos de dados acima, cada coluna representa uma informação/variável sobre o cliente. Questão 1. O que significa cada variável? Qual é o tipo de cada variável?  a) Variáveis numéricas (int64 ou float64)- RowNumber: número de linhas (int64) - CustomerId: Id do cliente (int64)- CreditScore: score (pontuação) de crédito do cliente (int64)- Age: idade do cliente (int64)- Tenure: tempo de vínculo do cliente com a instituição financeira (int64). Observação: no conjunto de dados não consta a medida de tempo (são meses ou anos). Vou considerar anos.   - Balance: montante em dinheiro disponível na conta do cliente (float64). Observação: no conjunto de dados não cosnta a moeda (é dolár, euro, libra, etc). Vou considerar euros, uma vez que os clientes estão localizados em países da Europa.- NumbOfProducts: número de produtos da instituição financeira que o cliente possui (int64)- HasCrCard: o cliente tem cartão de crédito? 0 = não e 1 = sim (int64)- IsActiveMember: o cliente é um membro ativo? 0 = não e 1 = sim (int64) - EstimatedSalary: salário estimado do cliente (float64)- Exited: o cliente abandonou a instituição financeira? 0 = não e 1 = sim (int64)b) Variáveis categóricas (object)- Surname: sobrenome do cliente- Geography: localização do cliente- Gender: gênero do cliente (male = masculino e female = feminino) Questão 2. Qual a quantidade de variáveis e clientes?<jupyter_code># quantificar as variáveis e clientes do conjunto de dados de treino
print("Número de variáveis:\t {}".format(df_treino.shape[1]))
print("Número de clientes:\t {}".format(df_treino.shape[0]))
# quantificar as variáveis e clientes do conjunto de dados de teste
print("Número de variáveis:\t {}".format(df_teste.shape[1]))
print("Número de clientes:\t {}".format(df_teste.shape[0]))<jupyter_output>Número de variáveis:	 13
Número de clientes:	 1000<jupyter_text>Questão 3. Existem valores ausentes nos conjuntos de dados? Qual a quantidade de valores únicos em cada variável?<jupyter_code># criar um dataframe informando a quantidade de valores ausentes e de valores únicos para cada varíavel do conjunto de dados de treino
pd.DataFrame({"Valores Ausentes": df_treino.isna().mean(),
              "Valores Únicos": df_treino.nunique()})
# criar um dataframe informando a quantidade de valores ausentes e de valores únicos para cada varíavel do conjunto de dados de teste
pd.DataFrame({"Valores Ausentes": df_teste.isna().mean(),
              "Valores Únicos": df_teste.nunique()})<jupyter_output><empty_output><jupyter_text>- Não existem valores ausentes nos conjuntos de dados- E quanto aos valores únicos, é sempre bom verificar, especialmente para as variáveis (i) HasCrCard, IsActiveMember e Exited que precisam conter dois tipos de informação (valor): 1 ou 0 e (ii) gender: female ou male. Logo, a quantidade de valores únicos (ou seja, total unitário) precisa ser 2. Questão 4. Existem variáveis que não trazem nenhuma informação útil para o objetivo do projeto (prever o churn "abondono de clientes" da instituição financeira)?  Sim! São as variáveis RowNumber, CustomerId e Surname. Portanto, retirar tais variáveis dos conjuntos de dados. Tais variáveis são apenas para ajudar no gerenciamento do banco de dados da instituição financeira.<jupyter_code>print("Número de variáveis:\t {}".format(df_treino.shape[1]))
df_treino= df_treino.drop(columns=["RowNumber","CustomerId","Surname"])
print("Número de variáveis:\t {}".format(df_treino.shape[1])) # verificar se as variáveis foram removidas
df_treino.head() # conjunto de dados de treino sem as variáveis RowNumber, CustomerId e Surname
print("Número de variáveis:\t {}".format(df_teste.shape[1]))
df_teste= df_teste.drop(columns=["RowNumber","CustomerId","Surname"])
print("Número de variáveis:\t {}".format(df_teste.shape[1]))
df_teste.head() # conjunto de dados de teste sem as variáveis RowNumber, CustomerId e Surname<jupyter_output><empty_output><jupyter_text>Questão 5. Existem valores duplicados?<jupyter_code>novo_df_treino = df_treino.drop_duplicates() # remover as linhas duplicadas
duplicadas_df_treino = novo_df_treino[novo_df_treino.duplicated()] # calcular o total de linhas duplicadas
print(f"Total de linhas duplicadas: {len(duplicadas_df_treino)}")
novo_df_teste = df_teste.drop_duplicates() # remover as linhas duplicadas
duplicadas_df_teste = novo_df_teste[novo_df_teste.duplicated()] # calcular o total de linhas duplicadas
print(f"Total de linhas duplicadas: {len(duplicadas_df_teste)}")<jupyter_output>Total de linhas duplicadas: 0<jupyter_text>Não! Os conjuntos de dados não possuem valores duplicados. Questão 6. Existem outliers (valores discrepantes)?<jupyter_code># Variáveis numéricas
sns.boxplot(x="CreditScore",data=df_treino)
# Variáveis numéricas
sns.boxplot(x="Age",data=df_treino)
# Variáveis numéricas
sns.boxplot(x="Tenure",data=df_treino)
# Variáveis numéricas
sns.boxplot(x="Balance",data=df_treino)
# Variáveis numéricas
sns.boxplot(x="NumOfProducts",data=df_treino)
# Variáveis numéricas
sns.boxplot(x="EstimatedSalary",data=df_treino)<jupyter_output><empty_output><jupyter_text>Observação geral sobre a presença de outliers: - EstimatedSalary, Balance, Tenure não possuem outliers;- NumOfProducts, Age e CreditScore possuem outliers. Outliers podem causar ruídos na análise de dados, ou seja, viesar a(s) análise(s) subsequentes. Então, o que fazer com os outliers?Listo algumas estratégias:- entrar em contato com a equipe responsável pela coleta dos dados para verificar se tais outliers possam ser devido algum erro durante o processo de coleta dos dados. Caso afirmativo, os outliers seriam arrumados;- remover os outliers. Isso implica perder linha(s) inteira(s) do conjunto de dados. No presente projeto seria perder clientes. Temos uma grande quantidade de observações (n = 10000), porém, a variável alvo (Exited) está desbalanceada (conforme mostrado na etapa posterior), assim, existe o risco de aumentar ainda mais esse desbalanceamento. Portanto, seria preciso avaliar o impacto da remoção dos outliers- substituir os valores dos outliers por um valor de uma medida de estatística descritiva (por exemplo, média ou moda). Ou também alterar o valor do(s) outlier(s) usando os valores da barra superior e inferior, ou seja, outlier(s) acima da barra superior receberiam o valor da barra superior e outlier(s) abaixo da barra inferior receberiam o valor da barra inferior   - Neste presente projeto, inicialmente, vou continuar com os outliers. Partindo da seguinte premissa: "objetivo é buscar padrões e/ou fazer previsões aos quais não se tem nenhuma informação a priori". Porém, se os modelos apresentarem performances ruins, retorno nesta questão  2.3. Estatísticas das variáveis 2.3.1. Variável alvo (também conhecida como target, y ou resposta) no conjunto de dados de treino. Exited é a variável alvo e possui dois valores: - "1" cliente abandonou a instituição financeira, ou seja, ocorreu churn- "0" cliente não abandonou a instituição financeira 2.3.1.1. Estatística descritiva da variável alvo<jupyter_code>y=df_treino["Exited"]<jupyter_output><empty_output><jupyter_text>Questão 7. Como se comporta a variável alvo?<jupyter_code># Quantidade de clientes que abandonaram a instituição financeira
y_1=sum(y==1)
print("Quantidade de clientes que abandonaram a instituição financeira: {}".format(y_1))
# Quantidade de clientes que não abandonaram a instituição financeira
y_0=sum(y==0)
print("Quantidade de clientes que não abandonaram a instituição financeira: {}".format(y_0))
# Proporção de clientes que abandonaram a instituição financeira
prop_y_1=sum(y==1)*100/y.shape[0]
print("Porcentagem de clientes que abandonaram a instituição financeira: {}".format(prop_y_1))

# Proporção de clientes que não abandonaram a instituição financeira
prop_y_0=sum(y==0)*100/y.shape[0]
print("Porcentagem de clientes que não abandonaram a instituição financeira: {}".format(prop_y_0))
# Criar um dataframe com os dados acima
y_df=pd.DataFrame([["Não",7963,79.63],["Sim",2037,20.37]],index=range(0,2),columns=["Abandono (Churn)","Quantidade de Clientes (N)", "Porcentagem de Clientes (%)"])
display(y_df)
# Mostrar os dados da variável alvo em um gráfico de barras (Quantidade de clientes) 
sns.set_color_codes("dark")
g = sns.barplot(x="Quantidade de Clientes (N)", y="Abandono (Churn)",data=y_df, color="green")
g.axes.set_title("Churn", fontsize=16,color="black",alpha=3)
g.set_xlabel("Quantidade de Clientes", size = 14,color="black")
g.set_ylabel("")
sns.despine(left=True, bottom=True)
plt.show()
# Mostrar os dados da variável alvo em um gráfico de barras (Quantidade de clientes) 
sns.set_color_codes("dark")
g = sns.barplot(x="Porcentagem de Clientes (%)", y="Abandono (Churn)",data=y_df, color="green")
g.axes.set_title("Churn", fontsize=16,color="black",alpha=3)
g.set_xlabel("Porcentagem de Clientes", size = 14,color="black")
g.set_ylabel("")
plt.text(81,0.1,"79.63%",fontsize=14)
plt.text(21,0.99,"20.37%",fontsize=14)         
sns.despine(left=True, bottom=True)
plt.show()<jupyter_output><empty_output><jupyter_text>Através das informações acima podemos verificar que a maioria dos clientes não abandonaram a instituição financeira. Simplesmente, em cada dez clientes, dois abandonaram a instituição financeira. Além disso, está evidente que o conjunto de dados de treino está desbalanceado.  Consequências de dados desbalanceados na construção de um modelo de aprendizado de máquina (machine learning):O modelo terá uma tendência a dar muitos "alarmes falsos". Ou seja, na prática o modelo irá responder muito para a classe majoritária (sem abandono = não churn), mas terá um desempenho inferior para a classe minoritária (com abandono = churn). Logo, esse fato merece atenção na hora do pré-processamento dos dados. Questão 8. Quem são as outras variáveis? São as variáveis explicativas (também conhecidas como preditoras, X ou explanatórias) dos conjuntos de dados. São as variáveis: CredtiScore, Geography, Gender, Age, Tenure, Balance, NumberOfProducts, HasCrCard, IsActiveMember, EstimatedSalary. Total: 10 variáveis 2.3.2. Variáveis X<jupyter_code>X_treino=df_treino.drop(columns=["Exited"])
X_treino.head()<jupyter_output><empty_output><jupyter_text>Questão 9. Qual o comportamento geral das variáveis X?    2.3.2.1. Análise univariada das variáveis X que são numéricas<jupyter_code>X_numericas=X_treino.select_dtypes(exclude=["object"])
X_numericas.head()
X_numericas.describe()<jupyter_output><empty_output><jupyter_text>- Acima constam o número de observações (count), média (mean), desvio padrão (std), valor máximo (max) e minimo (min) e os quartis (25%, 50% e 75%) para cada uma das variáveis X numéricas- As variáveis possuem escalas de grandezas distintas. Esse fato merece atenção na hora da etapa de pré-processamento 2.3.2.2. Análise univariada das variáveis X que são categóricas<jupyter_code>X_categoricas=X_treino.select_dtypes(include=["object"])
X_categoricas.head()
X_categoricas.describe(include=object)
fig, ax = plt.subplots(5,2, figsize = (8,10)) # visualizar as variáveis X
sns.countplot(X_numericas["IsActiveMember"], palette = 'Paired', ax = ax[0][0])
sns.countplot(X_numericas["HasCrCard"], palette = 'Paired', ax = ax[0][1])
sns.countplot(X_categoricas["Gender"], palette = "Paired", ax = ax[1][0])
sns.countplot(X_categoricas["Geography"], palette = "Paired", ax = ax[1][1])
sns.boxplot(X_numericas["CreditScore"], palette = 'Paired', ax = ax[2][0])
sns.boxplot(X_numericas["Age"], palette = 'Paired', ax = ax[2][1])
sns.boxplot(X_numericas["Tenure"], palette = "Paired", ax = ax[3][0])
sns.boxplot(X_numericas["Balance"], palette = "Paired", ax = ax[3][1])
sns.countplot(X_numericas["NumOfProducts"], palette = "Paired", ax = ax[4][0])
sns.boxplot(X_numericas["EstimatedSalary"], palette = "Paired", ax = ax[4][1])
plt.tight_layout()
plt.show()<jupyter_output>C:\Users\<NAME>\anaconda3\lib\site-packages\seaborn\_decorators.py:36: FutureWarning: Pass the following variable as a keyword arg: x. From version 0.12, the only valid positional argument will be `data`, and passing other arguments without an explicit keyword will result in an error or misinterpretation.
  warnings.warn(
C:\Users\<NAME>\anaconda3\lib\site-packages\seaborn\_decorators.py:36: FutureWarning: Pass the following variable as a keyword arg: x. From version 0.12, the only valid positional argument will be `data`, and passing other arguments without an explicit keyword will result in an error or misinterpretation.
  warnings.warn(
C:\Users\<NAME>\anaconda3\lib\site-packages\seaborn\_decorators.py:36: FutureWarning: Pass the following variable as a keyword arg: x. From version 0.12, the only valid positional argument will be `data`, and passing other arguments without an explicit keyword will result in an error or misinterpretation.
  warnings.warn(
C:\Users\Fabi[...]<jupyter_text>2.3.2.3. Análise das variáveis X considerando a variável alvo<jupyter_code>fig, ax = plt.subplots(5,2, figsize = (12,10))
sns.histplot(hue= 'Exited', x= 'CreditScore', multiple='stack', data = df_treino, palette = 'Paired', ax = ax[0][0])
sns.histplot(hue= 'Exited', x= 'Age', multiple='stack', data = df_treino, palette = 'Paired', ax = ax[0][1])
sns.histplot(hue= 'Exited', x= 'Tenure', multiple='stack', data = df_treino, palette = 'Paired', ax = ax[1][0])
sns.histplot(hue= 'Exited', x= 'Balance', multiple='stack', data = df_treino, palette = 'Paired', ax = ax[1][1])
sns.histplot(hue= 'Exited', x= 'EstimatedSalary', multiple='stack', data = df_treino, palette = 'Paired', ax = ax[2][0])
sns.countplot(x= 'Gender', hue= 'Exited', data = df_treino, palette = 'Paired', ax = ax[2][1])
sns.countplot(x= 'NumOfProducts', hue= 'Exited', data = df_treino, palette = 'Paired', ax = ax[3][0])
sns.countplot(x= 'HasCrCard', hue= 'Exited', data = df_treino, palette = 'Paired', ax = ax[3][1])
sns.countplot(x= 'IsActiveMember', hue= 'Exited', data = df_treino, palette = 'Paired', ax = ax[4][0])
sns.countplot(x= 'Geography', hue= 'Exited', data = df_treino, palette = 'Paired', ax = ax[4][1])
plt.tight_layout()
plt.show()<jupyter_output><empty_output><jupyter_text>Olhar com mais detalhes determinadas variáveis Xs<jupyter_code>df_treino.IsActiveMember.value_counts() # total de clientes em cada categoria de membro ativo
# número de clientes em cada categoria de membro ativo (1 ou 0)
num_yes = len(df_treino[df_treino.IsActiveMember == 1])
num_no = len(df_treino[df_treino.IsActiveMember == 0])

# número de clientes que abandonaram em cada categoria de membro ativo
ex_yes = len(df_treino[(df_treino.IsActiveMember == 1) & (df_treino.Exited == 1)])
ex_no = len(df_treino[(df_treino.IsActiveMember == 0) & (df_treino.Exited == 1)]) 

yes_ex_rate = ex_yes/num_yes
no_ex_rate = ex_no/num_no
plt.barh(y = ['Yes_Active','No_Active'] , width = [num_yes,num_no], color = 'lightblue')
plt.barh(y = ['Yes_Active','No_Active'] , width = [ex_yes,ex_no] , color = 'darkblue')

plt.xlim(0,5800)

# mostrar a porcentagem de cliente que abandonou em cada categoria de membro ativo 
plt.text(5000 , 'No_Active','{:.1f} %'.format(no_ex_rate*100))
plt.text(5200 , 'Yes_Active','{:.1f} %'.format(yes_ex_rate*100))

plt.title('Churn (%) vs Membro Ativo')
plt.xlabel('Número de clientes')

plt.show()
df_treino.HasCrCard.value_counts() # total de clientes que possuem e não possuem cartão de crédito 
# número de clientes em cada categoria de membro ativo (1 ou 0)
num_yes = len(df_treino[df_treino.HasCrCard == 1])
num_no = len(df_treino[df_treino.HasCrCard == 0])

# número de clientes que abandonaram em cada categoria de membro ativo
ex_yes = len(df_treino[(df_treino.HasCrCard == 1) & (df_treino.Exited == 1)])
ex_no = len(df_treino[(df_treino.HasCrCard == 0) & (df_treino.Exited == 1)]) 

yes_ex_rate = ex_yes/num_yes
no_ex_rate = ex_no/num_no
plt.barh(y = ['Yes_CrCard','No_CrCard'] , width = [num_yes,num_no], color = 'lightblue')
plt.barh(y = ['Yes_CrCard','No_CrCard'] , width = [ex_yes,ex_no] , color = 'darkblue')

plt.xlim(0,8000)

# mostrar a porcentagem de cliente que abandonou em cada categoria de membro ativo 
plt.text(3000 , 'No_CrCard','{:.1f} %'.format(no_ex_rate*100))
plt.text(7100 , 'Yes_CrCard','{:.1f} %'.format(yes_ex_rate*100))

plt.title('Churn (%) vs Cartão de Crédito')
plt.xlabel('Número de clientes')

plt.show()
df_treino.NumOfProducts.value_counts() # total de clientes em cada categoria de produto 
# número de clientes em cada categoria de produto (1, 2, 3 ou 4 produtos)
num_p1 = len(df_treino[df_treino.NumOfProducts == 1])
num_p2 = len(df_treino[df_treino.NumOfProducts == 2])
num_p3 = len(df_treino[df_treino.NumOfProducts == 3])
num_p4 = len(df_treino[df_treino.NumOfProducts == 4])

# número de clientes que abandonaram em cada categoria de produto
ex_p1 = len(df_treino[(df_treino.NumOfProducts == 1) & (df_treino.Exited == 1)])
ex_p2 = len(df_treino[(df_treino.NumOfProducts == 2) & (df_treino.Exited == 1)]) 
ex_p3 = len(df_treino[(df_treino.NumOfProducts == 3) & (df_treino.Exited == 1)])
ex_p4 = len(df_treino[(df_treino.NumOfProducts == 4) & (df_treino.Exited == 1)])

p1_ex_rate = ex_p1/num_p1
p2_ex_rate = ex_p2/num_p2
p3_ex_rate = ex_p3/num_p3
p4_ex_rate = ex_p4/num_p4
plt.barh(y = ['1_product','2_products','3_products','4_products'] , width = [num_p1,num_p2,num_p3,num_p4], color = 'lightblue')
plt.barh(y = ['1_product','2_products','3_products','4_products'] , width = [ex_p1,ex_p2,ex_p3,ex_p4] , color = 'darkblue')

plt.xlim(0,6000)

# mostrar a porcentagem de cliente que abandonou em cada categoria de produto 
plt.text(200 , '4_products','{:.1f} %'.format(p4_ex_rate*100)) 
plt.text(400 , '3_products','{:.1f} %'.format(p3_ex_rate*100))
plt.text(4700 , '2_products','{:.1f} %'.format(p2_ex_rate*100))
plt.text(5200 , '1_product','{:.1f} %'.format(p1_ex_rate*100))

plt.title('Churn (%) vs Número de produtos')
plt.xlabel('Número de clientes')

plt.show()
# montante médio em dinheiro disponível na conta dos clientes que abandonaram (1) e que não abandonaram a instituição financeira  
sns.catplot(data = df_treino , x = 'Exited'  , y = 'Balance' , kind = 'bar' , color = 'blue')
plt.plot([0,1.4],[df_treino[df_treino.Exited == 0]['Balance'].mean(),df_treino[df_treino.Exited == 0]['Balance'].mean()],linestyle = '--' , c ='k'
         , alpha = 0.8)
plt.show()
# Salário estimado médio dos clientes que abandonaram (1) e que não abandonaram a instituição financeira  
sns.catplot(data = df_treino , x = 'Exited'  , y = 'EstimatedSalary' , kind = 'bar' , color = 'blue')
plt.plot([0,1.4],[df_treino[df_treino.Exited == 0]['EstimatedSalary'].mean(),df_treino[df_treino.Exited == 0]['EstimatedSalary'].mean()],linestyle = '--' , c ='k'
         , alpha = 0.8)
plt.show()
# Salário estimado médio dos clientes que abandonaram (1) e que não abandonaram a instituição financeira  
sns.catplot(data = df_treino , x = 'Exited'  , y = 'CreditScore' , kind = 'bar' , color = 'blue')
plt.plot([0,1.4],[df_treino[df_treino.Exited == 0]['CreditScore'].mean(),df_treino[df_treino.Exited == 0]['CreditScore'].mean()],linestyle = '--' , c ='k'
         , alpha = 0.8)
plt.show()<jupyter_output><empty_output><jupyter_text>- A maioria dos clientes estão localizados na França. Os clientes alemães apresentaram maior número de abandono junto à instituição financeira- O maior número de abandono ocorreu entre clientes do sexo feminino- Clientes que não são membros ativos abandonaram mais a instituição financeira do que os clientes que são membros ativos - Todos os clientes (n = 60) que tinham 4 produtos abandonaram a instituição financeira. Clientes que tinham um (27.7%) e dois (7.6%) produtos abandonaram menos a instituição financeira do que os clientes tinham mais produtos- Os clientes que mais abandonaram a instituição financeira foram os que tinham mais dinheiro disponível na conta. - Além disso, o salário estimado é praticamente o mesmo entre os clientes que abandonaram e os que permaneceram na instituição financeira. Assim como, a porcentagem de clientes que possui cartão de crédito é a mesma para os clientes que abandonaram e os que não abandonaram  3. Preparação dos dados (Pré-processamento) 3.1. Tratamento das variáveis categóricas<jupyter_code># Codificando (Encoding) a variável categórica Geography
df_treino = pd.get_dummies(df_treino, columns = ['Geography'])
df_treino.head()
# Codificando (Encoding) a variável categórica "Gender"
df_treino['Gender'] = df_treino['Gender'].map({'Female':0, 'Male':1 })
df_treino.head()
# Encoding a variável categórica 
df_teste = pd.get_dummies(df_teste, columns = ['Geography'])
df_teste.head()
# Encoding the categorical data
df_teste['Gender'] = df_teste['Gender'].map({'Female':0, 'Male':1 })
df_teste.head()<jupyter_output><empty_output><jupyter_text>3.2. Seleção das variáveis X Questão 10. Existe correlação entre as variáveis?<jupyter_code>plt.figure(figsize=(20,12))
plt.rcParams.update({'font.size': 10})
sns.heatmap(df_treino.corr(), annot=True, cmap='Blues') # criar um heatmap (mapa de calor)<jupyter_output><empty_output><jupyter_text>- não existem variáveis Xs (ou seja, características dos clientes) altamente correlacionadas com a variável alvo (Exited)- mas entre todas as variáveis Xs, Age, Geography_Germany, IsActiveMember, Balance e Gender são as que estão mais correlacionadas com a variável alvo 3.2.1. Transformação da escala dos dados<jupyter_code>df_treino.head()<jupyter_output><empty_output><jupyter_text>As variáveis CreditScore, Balance e EstimatedSalary possuem escalas de grandezas distintas. Portanto, deixar as variáveis numa mesma escala para evitar que análises posteriores fiquem enviesadas para as variáveis de maior grandeza<jupyter_code>X_Treino=df_treino.drop(columns=['Exited'])
X_Treino.head()
sc = MinMaxScaler()
X_Treino[['CreditScore','Balance','EstimatedSalary']] = sc.fit_transform(X_Treino[['CreditScore','Balance','EstimatedSalary']])
X_Treino.head()
df_teste[['CreditScore','Balance','EstimatedSalary']] = sc.fit_transform(df_teste[['CreditScore','Balance','EstimatedSalary']])
df_teste.head()<jupyter_output><empty_output><jupyter_text>3.2.2. Seleção das variáveis X (Redução da dimensionalidade = redução de variáveis)   Três técnicas usadas: Random Forest, RFE e SelectKBest 3.2.2.1. Random Forest (RF)<jupyter_code>model = RandomForestClassifier()
model.fit(X_Treino,y)
sns.barplot(y= X_Treino.columns, x = model.feature_importances_)<jupyter_output><empty_output><jupyter_text>3.2.2.2. Recursive Feature Elimination (RFE) with Random Forest (RF)<jupyter_code>selector = RFE(model, n_features_to_select=6)
x_feature_selected = selector.fit_transform(X_Treino, y)
X_Treino.columns[selector.get_support(indices=True)]<jupyter_output><empty_output><jupyter_text>3.2.2.3. SelectKBest<jupyter_code>selector = SelectKBest(f_classif, k=6)
X_Treino_feature_selected = selector.fit_transform(X_Treino, y)
X_Treino.columns[selector.get_support(indices=True)]
sns.barplot(y=X_Treino.columns, x = selector.scores_)<jupyter_output><empty_output><jupyter_text>Geography_Spain e HasCrCard foram as únicas que não apareceram entre as seis variáveis mais importantes nas três abordagens usadas acima. Sendo assim, inicialmente, vou ajustar modelos  usando todas as variáveis. Caso não obtenha boa performe dos mesmos, retorno para essa questão, ou seja, , ajustar modelos sem as duas variáveis (Geography_Sapin e HasCrCard)    3.2.2. Balanceamento da variável alvo (Exited) - Gerar novas observações da classe minoritária usando o algoritmo SMOTE (Synthetic Minority Oversampling Technique). No caso do presente projeto, a classe minoritária é o abandono de clientes (churn) junto à instituição financeira<jupyter_code>smote = SMOTE()
X_Treino_balanceado, y_balanceado = smote.fit_resample(X_Treino, y)
y_balanceado = pd.Series(y_balanceado, name=y.name)
y_balanceado.head(1)<jupyter_output><empty_output><jupyter_text>Verificar se gerou novas observações da classe minoritária<jupyter_code>ax = sns.countplot(x=y_balanceado)
ax.set_ylabel("Quantidade de clientes")
ax.set_xlabel("Exited");<jupyter_output><empty_output><jupyter_text>4. Modelagem (Modelo de Classificação, pois a variável alvo (Exited) é binária 0 ou 1)4.1. Geração (criação) dos modelos e avaliação dos modelos  4.1.1. Primeiro modelo (Regressão Logística)<jupyter_code>classifierLR = LogisticRegression(max_iter=500, random_state = 42)<jupyter_output><empty_output><jupyter_text>4.1.1.1. Dados desbalanceados<jupyter_code># Creating Logistic Regression list of metrics
logistic_regression_sem_balancear = []

# Cross validating the model
cv_result = cross_validate(classifierLR, X_Treino, y, cv=10,
                            scoring=['accuracy',
                                      'precision',
                                      'recall',
                                      'f1',
                                      'roc_auc'])

# Appending metrics to Logistic Regression list
for i in ['test_accuracy','test_precision','test_recall','test_f1','test_roc_auc']:
  logistic_regression_sem_balancear.append(cv_result[i].mean())
# Printing the Logistic Regression metrics
logistic_regression_sem_balancear<jupyter_output><empty_output><jupyter_text>4.1.1.2. Dados balanceados<jupyter_code># Creating Logistic Regression list of metrics
logistic_regression_balanceado = []

# Cross validating the model
cv_result = cross_validate(classifierLR, X_Treino_balanceado, y_balanceado, cv=10,
                       # Printing the Logistic Regression metrics
                              scoring=['accuracy',
                                      'precision',
                                      'recall',
                                      'f1',
                                      'roc_auc'])

# Appending metrics to Logistic Regression list
for i in ['test_accuracy','test_precision','test_recall','test_f1','test_roc_auc']:
  logistic_regression_balanceado.append(cv_result[i].mean())
# Printing the Logistic Regression metrics
logistic_regression_balanceado<jupyter_output><empty_output><jupyter_text>4.1.2. Segundo modelo (Naive Bayes)<jupyter_code>classifierNB = GaussianNB()<jupyter_output><empty_output><jupyter_text>4.1.2.1. Dados desbalanceados<jupyter_code># Creating Gaussian Naive Bayes list of metrics
naive_bayes_sem_balancear = []

# Cross validating the model
cv_result = cross_validate(classifierNB, X_Treino, y, cv=10,
                            scoring=['accuracy',
                                      'precision',
                                      'recall',
                                      'f1',
                                      'roc_auc'])

# Appending metrics to Gaussian Naive Bayes list
for i in ['test_accuracy','test_precision','test_recall','test_f1','test_roc_auc']:
  naive_bayes_sem_balancear.append(cv_result[i].mean())
# Printing the Gaussian Naive Bayes metrics
naive_bayes_sem_balancear<jupyter_output><empty_output><jupyter_text>4.1.2.2. Dados balanceados<jupyter_code># Creating Gaussian Naive Bayes list of metrics
naive_bayes_balanceado = []

# Cross validating the model
cv_result = cross_validate(classifierNB, X_Treino_balanceado, y_balanceado, cv=10,
                            scoring=['accuracy',
                                      'precision',
                                      'recall',
                                      'f1',
                                      'roc_auc'])

# Appending metrics to Gaussian Naive Bayes list
for i in ['test_accuracy','test_precision','test_recall','test_f1','test_roc_auc']:
  naive_bayes_balanceado.append(cv_result[i].mean())
# Printing the Gaussian Naive Bayes metrics
naive_bayes_balanceado<jupyter_output><empty_output><jupyter_text>4.1.3. Terceiro modelo (Random Forest - RF)<jupyter_code>classifierRF = RandomForestClassifier(random_state = 42)<jupyter_output><empty_output><jupyter_text>4.1.3.1. Dados desbalanceados<jupyter_code># Creating Random Forest list of metrics
random_forest_sem_balancear = []

# Cross validating the model
cv_result = cross_validate(classifierRF, X_Treino, y, cv=10,
                            scoring=['accuracy',
                                      'precision',
                                      'recall',
                                      'f1',
                                      'roc_auc'])

# Appending metrics to Random Forest list
for i in ['test_accuracy','test_precision','test_recall','test_f1','test_roc_auc']:
  random_forest_sem_balancear.append(cv_result[i].mean())
# Printing the RF metrics
random_forest_sem_balancear<jupyter_output><empty_output><jupyter_text>4.1.3.2. Dados balanceados<jupyter_code># Creating Random Forest list of metrics
random_forest_balanceado = []

# Cross validating the model
cv_result = cross_validate(classifierRF, X_Treino_balanceado, y_balanceado, cv=10,
                            scoring=['accuracy',
                                      'precision',
                                      'recall',
                                      'f1',
                                      'roc_auc'])

# Appending metrics to Random Forest list
for i in ['test_accuracy','test_precision','test_recall','test_f1','test_roc_auc']:
  random_forest_balanceado.append(cv_result[i].mean())
# Printing the RF metrics
random_forest_balanceado<jupyter_output><empty_output><jupyter_text>4.1.4. Quarto modelo (Decision Tree)<jupyter_code>classifierDT = DecisionTreeClassifier(random_state = 42)<jupyter_output><empty_output><jupyter_text>4.1.4.1. Dados desbalanceados<jupyter_code># Creating Decision Tree list of metrics
decision_tree_sem_balancear = []

# Cross validating the model
cv_result = cross_validate(classifierDT, X_Treino, y, cv=10,
                            scoring=['accuracy',
                                      'precision',
                                      'recall',
                                      'f1',
                                      'roc_auc'])

# Appending metrics to Decision Tree list
for i in ['test_accuracy','test_precision','test_recall','test_f1','test_roc_auc']:
  decision_tree_sem_balancear.append(cv_result[i].mean())
# Printing the Decision Tree metrics
decision_tree_sem_balancear<jupyter_output><empty_output><jupyter_text>4.1.4.2. Dados Balanceados<jupyter_code># Creating Decision Tree list of metrics
decision_tree_balanceado = []

# Cross validating the model
cv_result = cross_validate(classifierDT, X_Treino_balanceado, y_balanceado, cv=10,
                            scoring=['accuracy',
                                      'precision',
                                      'recall',
                                      'f1',
                                      'roc_auc'])

# Appending metrics to Decision Tree list
for i in ['test_accuracy','test_precision','test_recall','test_f1','test_roc_auc']:
  decision_tree_balanceado.append(cv_result[i].mean())
# Printing the Decision Tree metrics
decision_tree_balanceado<jupyter_output><empty_output><jupyter_text>4.1.5. Quinto modelo (LGBM)<jupyter_code>LGBM_model=LGBMClassifier(random_state=42)<jupyter_output><empty_output><jupyter_text>4.1.5.1. Dados desbalanceados<jupyter_code># Creating LGBM list of metrics
LGBM_model_sem_balancear = []

# Cross validating the model
cv_result = cross_validate(LGBM_model, X_Treino, y, cv=10,
                            scoring=['accuracy',
                                      'precision',
                                      'recall',
                                      'f1',
                                      'roc_auc'])

# Appending metrics to LGBM list
for i in ['test_accuracy','test_precision','test_recall','test_f1','test_roc_auc']:
  LGBM_model_sem_balancear.append(cv_result[i].mean())
# Printing the LGBM metrics
LGBM_model_sem_balancear<jupyter_output><empty_output><jupyter_text>4.1.5.2. Dados balanceados<jupyter_code># Creating LGBM list of metrics
LGBM_model_balanceado = []

# Cross validating the model
cv_result = cross_validate(LGBM_model, X_Treino_balanceado, y_balanceado, cv=10,
                            scoring=['accuracy',
                                      'precision',
                                      'recall',
                                      'f1',
                                      'roc_auc'])

# Appending metrics to LGBM list
for i in ['test_accuracy','test_precision','test_recall','test_f1','test_roc_auc']:
  LGBM_model_balanceado.append(cv_result[i].mean())
# Printing the LGBM metrics
LGBM_model_balanceado<jupyter_output><empty_output><jupyter_text>4.2. Comparar as métricas dos modelos<jupyter_code>metricas = (pd.DataFrame([logistic_regression_sem_balancear,logistic_regression_balanceado,naive_bayes_sem_balancear,naive_bayes_balanceado,random_forest_sem_balancear, random_forest_balanceado, decision_tree_sem_balancear,decision_tree_balanceado, LGBM_model_sem_balancear, LGBM_model_balanceado],
                        index=['Logistic Regression sem balancear','Logistic Regression balanceado','Naive Bayes sem balancear','Naive Bayes balanceado','Random Forest sem balancear', 'Random Forest balanceado','Decision Tree sem balancear','Decision Tree balanceado','LGBM sem balancear','LGBM balanceado'],
                        columns=['Accuracy','Precision','Recall','F1 Score','AUROC'])*100).round(2)

metricas<jupyter_output><empty_output><jupyter_text>Para o problema de negócio, o mais importante é prever corretamente os clientes que provavelmente abandonarão a instituição financeira (verdadeiros positivos, ou seja, previsão de que ocorre abondono "churn" e, de fato, ocorre) e, também, minimizar o número de clientes erroneamente classificados como sem previsão de abandonar (falsos negativos, ou seja, previsão de que não ocorre abandono "churn", mas ocorre). Portanto, a métrica utilizada será o recall.<jupyter_code>metricas['Recall'].sort_values(ascending=False)<jupyter_output><empty_output><jupyter_text>- Melhor modelo: Random Forest balanceado- Além disso, o RF balanceado também mostrou maiores performances nas outras métrica- Modelos sem balancear: não apresentaram boas performances   4.3. Tunagem de hiperparâmetros do Melhor Modelo (RF)<jupyter_code>tuned_classifierRF = RandomForestClassifier()

# Parameters to be used
params_RF = {'n_estimators':[100,200],
             'min_samples_leaf':[1,2,3],
             'max_depth':[None,1,2],
             'criterion':['gini','entropy']}

gs_RF = GridSearchCV(estimator=tuned_classifierRF, 
                      param_grid=params_RF, 
                      cv=10, 
                      scoring='recall')

# Fitting the model to the training set  
gs_RF.fit(X_Treino_balanceado, y_balanceado)

# Printing the best score adquired from the tunning
best_recall = gs_RF.best_score_
print("Best Recall Score: {:.2f} %".format(best_recall*100))

# Printing the best parameter found
best_parameters = gs_RF.best_params_
print("Best Parameters:", best_parameters)
tuned_classifierRF = RandomForestClassifier()

# Parameters to be used
params_RF = {'n_estimators':[100,200,300],
             'min_samples_leaf':[1,2,3],
             'max_depth':[None,1,2],
             'criterion':['gini','entropy']}

gs_RF = GridSearchCV(estimator=tuned_classifierRF, 
                      param_grid=params_RF, 
                      cv=10, 
                      scoring='recall')

# Fitting the model to the training set  
gs_RF.fit(X_Treino_balanceado, y_balanceado)

# Printing the best score adquired from the tunning
best_recall = gs_RF.best_score_
print("Best Recall Score: {:.2f} %".format(best_recall*100))

# Printing the best parameter found
best_parameters = gs_RF.best_params_
print("Best Parameters:", best_parameters)<jupyter_output>Best Recall Score: 89.68 %
Best Parameters: {'criterion': 'gini', 'max_depth': None, 'min_samples_leaf': 1, 'n_estimators': 200}<jupyter_text>4.4. Treinando com todos os dados de treino- Treinar (ajustar) o melhor modelo (RF balanceado) com os melhores hiperparâmetros<jupyter_code>RF = RandomForestClassifier(n_estimators=300, max_depth=None, 
                             criterion='gini', random_state=42)

RF.fit(X_Treino_balanceado, y_balanceado)
df_teste.head()<jupyter_output><empty_output><jupyter_text>5. Previsão usando os dados de teste<jupyter_code>pred_RF_test = RF.predict(df_teste)
pred_RF_test
np.savetxt('C:/Users/<NAME>/LightHouse/test1.csv', pred_RF_test, delimiter=',')<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10651348701576209, 'fraction_numerical': 0.01679278011010835, 'mean_word_length': 4.776938716235841, 'pattern_counts': {'":': 4, '<': 189, '<?xml version=': 0, '>': 189, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 17, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 40, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '47', 'dir': 'jupyter-structured-clean-dedup', 'id': '488108', 'license': 'CC0-1.0', 'n_tokens_mistral': 14351, 'n_tokens_neox': 13578, 'n_words': 3514, 'path': 'Churn.ipynb', 'repo_name': 'FabiBarbos/Churn', 'size': '39799'}