<jupyter_start><jupyter_text>Desafio 1 - Maratona Behind the Code 2021 Notebook guiaEsse Jupyter Notebook te dará instruções para criar uma solução introdutória para o desafio 1 da Maratona. Sinta-se livre para editar e melhorar sua solução! **Atenção: se estiver utilizando o Watson Studio, lembre-se de deixar o Notebook como editável, clicando no botão de editar acima.**![](https://s3.br-sao.cloud-object-storage.appdomain.cloud/maratona-static/edit-notebook.png)  Exploração do datasetO primeiro passo para o desenvolvimento de um bom modelo de Machine Learning é explorar bem os dados que temos para trabalhar. Devemos entender o melhor possível a relevância de cada dado para o valor que queremos predizer. Afinal, a predição do modelo é inteiramente baseada nos dados com que treinou.Existem muitas bibliotecas em Python que podem ser utilizadas para tratamento e visualização de dados. Nesses exemplos, vamos usar Pandas, Seaborn e Matplotlib.<jupyter_code>import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
def cat_exp (feature, alvo):
        plt.figure(figsize=(30,8))
        plt.subplot(1,2,1)
        sns.countplot(feature)
        plt.subplot(1,2,2)
        sns.countplot(feature, hue=alvo);
    
        return plt.show()<jupyter_output><empty_output><jupyter_text>Primeiramente, vamos carregar o dataset do desafio neste Notebook. Vamos começar com o principal, `LOANS.csv`. Para isso, podemos usar o ícone de assets, disponível no canto superior direito da tela, e inserir o dataset como um DataFrame Pandas, como na imagem abaixo. Repita para todos os datasets que for utilizar.<jupyter_code>import os, types
import pandas as pd
from botocore.client import Config
import ibm_boto3

def __iter__(self): return 0

# @hidden_cell
# The following code accesses a file in your IBM Cloud Object Storage. It includes your credentials.
# You might want to remove those credentials before you share the notebook.

if os.environ.get('RUNTIME_ENV_LOCATION_TYPE') == 'external':
    endpoint_6991ccecda0244a5955822de5d9e513e = 'https://s3.ap.cloud-object-storage.appdomain.cloud'
else:
    endpoint_6991ccecda0244a5955822de5d9e513e = 'https://s3.private.ap.cloud-object-storage.appdomain.cloud'

client_6991ccecda0244a5955822de5d9e513e = ibm_boto3.client(service_name='s3',
    ibm_api_key_id='<KEY>',
    ibm_auth_endpoint="https://iam.cloud.ibm.com/oidc/token",
    config=Config(signature_version='oauth'),
    endpoint_url=endpoint_6991ccecda0244a5955822de5d9e513e)

body = client_6991ccecda0244a5955822de5d9e513e.get_object(Bucket='bantotal-donotdelete-pr-hrmrwbzp4wmpy1',Key='LOANS.csv')['Body']
# add missing __iter__ method, so pandas accepts body as file-like object
if not hasattr(body, "__iter__"): body.__iter__ = types.MethodType( __iter__, body )

df_data_1 = pd.read_csv(body)
df_data_1.head()


body = client_6991ccecda0244a5955822de5d9e513e.get_object(Bucket='bantotal-donotdelete-pr-hrmrwbzp4wmpy1',Key='ACCOUNTS.csv')['Body']
# add missing __iter__ method, so pandas accepts body as file-like object
if not hasattr(body, "__iter__"): body.__iter__ = types.MethodType( __iter__, body )

df_data_2 = pd.read_csv(body)
df_data_2.head()


body = client_6991ccecda0244a5955822de5d9e513e.get_object(Bucket='bantotal-donotdelete-pr-hrmrwbzp4wmpy1',Key='DEMOGRAPHICS.csv')['Body']
# add missing __iter__ method, so pandas accepts body as file-like object
if not hasattr(body, "__iter__"): body.__iter__ = types.MethodType( __iter__, body )

df_data_3 = pd.read_csv(body)
df_data_3.head()<jupyter_output><empty_output><jupyter_text>Renomeie o nome da variável criada com o dataset para `loans`, para ficar de acordo com os códigos abaixo.<jupyter_code>loans = df_data_1
accounts = df_data_2
demographics = df_data_3
loans.info()
accounts.info()
demographics.info()
df_la = pd.merge(loans,accounts,how='left', on='ID')
df_lad = pd.merge(df_la,demographics, how = 'left', on='ID' )
df_lad.describe()
df_lad.isna().sum()
df_lad.dtypes
df_lad.isna().sum()
df_lad.columns
df_clear = df_lad.dropna()
plt.figure(figsize=(15,7))

plt.subplot(1,2,1)
sns.countplot(data=df_lad, x='ALLOW', order=loans['ALLOW'].value_counts().index)

plt.subplot(1,2,2)
risk_plot = sns.countplot(data=df_clear, x='ALLOW', order=loans['ALLOW'].value_counts().index)<jupyter_output><empty_output><jupyter_text>Sinta-se livre para ver a distribuição de outras colunas do conjunto de dados, utilizar os outros conjuntos de dados, explorar as correlações entre variáveis e outros.<jupyter_code>df_clear
3999-2237<jupyter_output><empty_output><jupyter_text>Analise exploratoria univariada das features numericas<jupyter_code>df_num = df_clear.select_dtypes(include = ['int64','float64'])
df_num.head()
df1_mean = pd.DataFrame(df_num.apply(np.mean)).T
df1_median = pd.DataFrame(df_num.apply(np.median)).T

df1_std = pd.DataFrame(df_num.apply(np.std)).T
df1_max = pd.DataFrame(df_num.apply(max)).T
df1_min = pd.DataFrame(df_num.apply(min)).T
df1_range = pd.DataFrame(df_num.apply(lambda x: x.max() - x.min())).T

num = pd.concat([df1_min, df1_max, df1_range, df1_mean, df1_median, df1_std]).T.reset_index()
num.columns = (['feature','min','max','range','media','mediana','desvio padrao'])
num
df_num.hist(figsize=(20,20));
cat_exp(df_clear['FOREIGN_WORKER'],df_clear['ALLOW'])
cat_exp(df_clear['DEPENDENTS'],df_clear['ALLOW'])
cat_exp(df_clear['JOB_TYPE'],df_clear['ALLOW'])<jupyter_output>/opt/conda/envs/Python-3.8-main/lib/python3.8/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(
/opt/conda/envs/Python-3.8-main/lib/python3.8/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(<jupyter_text>Analise exploratoria das features categoricas<jupyter_code>df_cat = df_clear.select_dtypes(include = ['object'])
df_cat.head()
cat_exp(df_clear['INSTALLMENT_PLANS'],df_clear['ALLOW'])
cat_exp(df_clear['LOAN_PURPOSE'],df_clear['ALLOW'])
cat_exp(df_clear['OTHERS_ON_LOAN'],df_clear['ALLOW'])
cat_exp(df_clear['CREDIT_HISTORY'],df_clear['ALLOW'])
cat_exp(df_clear['SEX'],df_clear['ALLOW'])
cat_exp(df_clear['PROPERTY'],df_clear['ALLOW'])
cat_exp(df_clear['HOUSING'],df_clear['ALLOW'])
df_clear['CHECKING_BALANCE'].describe()
df_clear['EXISTING_SAVINGS'].describe()<jupyter_output><empty_output><jupyter_text>Tratamento dos dados Uma vez que exploramos os dados, entendemos a importância de cada coluna e podemos fazer alterações nelas para para obter um melhor resultado. Aqui, vamos fazer apenas um tratamento simples, de remover do dataset as linhas que tiverem faltando algum valor. Não necessariamente essa técnica é a melhor para se utilizar no desafio, é apenas um exemplo de como tratar o dataset.Para tratamentos mais avançados, como modificação de colunas ou criação de novas colunas, veja mais abaixo no Notebook, em que explicamos como utilizar as `Pipelines`, da biblioteca `sklearn`, para realizar transformações nos dados.<jupyter_code>df_clear.count()<jupyter_output><empty_output><jupyter_text>Podemos observar que agora temos um dataset "limpo", mas perdemos alguns dados por remover as linhas em que pelo menos uma coluna estava faltando.<jupyter_code>df_clear.info()<jupyter_output><class 'pandas.core.frame.DataFrame'>
Int64Index: 2237 entries, 1522 to 3998
Data columns (total 22 columns):
 #   Column                      Non-Null Count  Dtype  
---  ------                      --------------  -----  
 0   ID                          2237 non-null   int64  
 1   PAYMENT_TERM                2237 non-null   float64
 2   INSTALLMENT_PLANS           2237 non-null   object 
 3   INSTALLMENT_PERCENT         2237 non-null   float64
 4   LOAN_PURPOSE                2237 non-null   object 
 5   LOAN_AMOUNT                 2237 non-null   float64
 6   OTHERS_ON_LOAN              2237 non-null   object 
 7   ALLOW                       2237 non-null   int64  
 8   CHECKING_BALANCE            2237 non-null   object 
 9   CREDIT_HISTORY              2237 non-null   object 
 10  EXISTING_SAVINGS            2237 non-null   object 
 11  EXISTING_CREDITS_COUNT      2237 non-null   float64
 12  SEX                         2237 non-null   object 
 13  AGE                         22[...]<jupyter_text>Observando a execução do método `.info()` acima, podemos ver que existem três colunas do tipo `object`. O modelo do `scikit-learn` que vamos usar não é capaz de processar uma variável desse tipo. Portanto, para dar seguimento ao experimento, vamos remover essa coluna. Recomendamos que você use alguma técnica para tratamento de variáveis categóricas, como o _one-hot encoding_, em vez de remover a coluna.Vamos também remover a coluna `ID`, pois sabemos que ela não é uma informação útil para a predição (é apenas um número identificando um cliente).  Tratamento das variaveis<jupyter_code>from sklearn.preprocessing import MinMaxScaler
mms = MinMaxScaler()
# PAYMENT_TERM
sns.boxplot(df_clear['PAYMENT_TERM'])
df_clear['PAYMENT_TERM'] = mms.fit_transform(df_clear[['PAYMENT_TERM']].values)
sns.boxplot(df_clear['PAYMENT_TERM'])
# LOAN_AMOUNT
sns.boxplot(df_clear['LOAN_AMOUNT'])
df_clear['LOAN_AMOUNT'] = mms.fit_transform(df_clear[['LOAN_AMOUNT']].values)
#INSTALLMENT_PLANS

installment_plans_dict={'NONE' : 1 , 'STORES' : 2 , 'BANK' : 3 }
df_clear['INSTALLMENT_PLANS'] = df_clear['INSTALLMENT_PLANS'].map(installment_plans_dict)
df_clear['INSTALLMENT_PLANS'].head()
#LOAN_PURPOSE

loan_propose_dict={'CAR_NEW' : 1 , 'CAR_USED' : 2 , 'FURNITURE' : 3 , 'RADIO_TV':4 ,'VACATION':5 ,'REPAIRS':6 , 'RADIO_TV':7, 'RETRAINING':8, 'APPLIANCES':9 ,'EDUCATION':10 ,'BUSINESS':11}
df_clear['LOAN_PURPOSE'] = df_clear['LOAN_PURPOSE'].map(loan_propose_dict)
df_clear['LOAN_PURPOSE'].head()
#OTHERS_ON_LOAN

other_on_loan_dict={'NONE' : 1 , 'CO-APPLICANT' : 2 , 'GUARANTOR' : 3 }
df_clear['OTHERS_ON_LOAN'] = df_clear['OTHERS_ON_LOAN'].map(other_on_loan_dict)
df_clear['OTHERS_ON_LOAN'].head()
#CREDIT_HISTORY

credit_history_dict={'OUTSTANDING_CREDIT' : 1 , 'CREDITS_PAID_TO_DATE' : 2 , 'ALL_CREDITS_PAID_BACK': 3 ,'PRIOR_PAYMENTS_DELAYED': 4, 'NO_CREDITS': 5 }
df_clear['CREDIT_HISTORY'] = df_clear['CREDIT_HISTORY'].map(credit_history_dict)
df_clear['CREDIT_HISTORY'].head()
#SEX

sex_dict={'F' : 1 , 'M' : 2 }
df_clear['SEX'] = df_clear['SEX'].map(sex_dict)
df_clear['SEX'].head()
#PROPERTY

property_dict={ 'REAL_ESTATE' : 1 ,'SAVINGS_INSURANCE'  : 2 , 'CAR_OTHER' : 3 , 'UNKNOWN' : 4 }
df_clear['PROPERTY'] = df_clear['PROPERTY'].map(property_dict)
df_clear['PROPERTY'].head()
#HOUSING

housing_dict={ 'OWN' : 1 ,'RENT'  : 2 , 'FREE' : 3 }
df_clear['HOUSING'] = df_clear['HOUSING'].map(housing_dict)
df_clear['HOUSING'].head()
#EXISTING_SAVINGS
# Assumindo valores de 0 para desconhecido e transformando demais valores em numerico
df_clear['EXISTING_SAVINGS'] = df_clear['EXISTING_SAVINGS'].apply(lambda x : 0 if x == 'UNKNOWN' else x)
df_clear['EXISTING_SAVINGS'] = df_clear['EXISTING_SAVINGS'].astype('float64')
df_clear['EXISTING_SAVINGS'].head()
#CHECKING_BALANCE
# Assumindo valores de 0 para credito negativo, 1 para nao checado ou nulo e 2 para positivo
df_clear['CHECKING_BALANCE'] = df_clear['CHECKING_BALANCE'].apply(lambda x : 1 if x == 'NO_CHECKING' or 0 else x)
df_clear['CHECKING_BALANCE'] = df_clear['CHECKING_BALANCE'].astype('float64')
df_clear['CHECKING_BALANCE'] = df_clear['CHECKING_BALANCE'].apply(lambda x : 2 if x > 0 else 0)
df_clear['CHECKING_BALANCE'].head()
plt.figure(figsize=(20,20))
sns.heatmap(df_clear.corr(method='pearson'), annot=True);
df_clear.info()
# 'ID', 'INSTALLMENT_PLANS', 'LOAN_PURPOSE', 'CREDIT_HISTORY', 'EXISTING_SAVINGS', 'SEX', 'JOB_TYPE', 'DEPENDENTS', 'FOREIGN_WORKER', 'HOUSING'# excluidos
# PAYMENT_TERM', 'INSTALLMENT_PERCENT', 'OTHERS_ON_LOAN', 'CHECKING_BALANCE', 'EXISTING_CREDITS_COUNT', 'AGE', 'TELEPHONE',  'EMPLOYMENT_DURATION', 'PROPERTY', 'CURRENT_RESIDENCE_DURATION' # usados<jupyter_output><empty_output><jupyter_text>Preparação dos dados para Machine Learning<jupyter_code>object_columns = ['ID', 'INSTALLMENT_PLANS', 'LOAN_PURPOSE',  'EXISTING_SAVINGS', 'SEX', 'JOB_TYPE', 'DEPENDENTS', 'FOREIGN_WORKER', 'HOUSING']
df_ml = df_clear.drop(object_columns, axis=1)
df_ml.describe()<jupyter_output><empty_output><jupyter_text>Criação do modeloCom os dados prontos, podemos selecionar um modelo de Machine Learning para treinar com nossos dados. Nesse exemplo, vamos utilizar um modelo de classificação básico, o de Árvore de Decisão.Para conseguir avaliar o desempenho do nosso modelo, vamos dividir os dados que temos entre dados de treino e de teste, e assim, após o treinamento, verificar como ele está se saindo com as predições.<jupyter_code>from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.metrics import f1_score<jupyter_output><empty_output><jupyter_text>Abaixo, separamos os dados que queremos predizer dos dados que utilizamos como informações para a predição.<jupyter_code>features = ['PAYMENT_TERM', 'INSTALLMENT_PERCENT', 'OTHERS_ON_LOAN', 'CHECKING_BALANCE', 'EXISTING_CREDITS_COUNT', 'CREDIT_HISTORY','AGE', 'TELEPHONE',  'EMPLOYMENT_DURATION', 'PROPERTY', 'CURRENT_RESIDENCE_DURATION']
target = ['ALLOW']

X = df_ml[features]
y = df_ml[target]
seed = 12
test_pct = 0.3 # Separaremos 30% dos dados para testes
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_pct)
## Decision Tree Classifer
model = DecisionTreeClassifier(random_state=seed)
model.fit(X_train, y_train)

y_pred = model.predict(X_test)
acc = f1_score(y_test, y_pred)
print(f"Acurácia do modelo Decision Tree Classifer (número de predições assertivas sobre número total de testes): {acc}")
## Logistic Regressions
from sklearn.linear_model import LogisticRegression
model = LogisticRegression(random_state=seed)
model.fit(X_train, y_train)

y_pred = model.predict(X_test)
acc = f1_score(y_test, y_pred)
print(f"Acurácia do modelo Logistic Regressions (número de predições assertivas sobre número total de testes): {acc}")
## Naive Bayes
from sklearn.naive_bayes import GaussianNB
model = GaussianNB()
model.fit(X_train, y_train) 

y_pred = model.predict(X_test)
acc = f1_score(y_test, y_pred)
print(f"Acurácia do modelo Naive Bayes (número de predições assertivas sobre número total de testes): {acc}")
## KNN
from sklearn.neighbors import KNeighborsClassifier
model = KNeighborsClassifier(n_neighbors = 10)
model.fit(X_train, y_train) 

y_pred = model.predict(X_test)
acc = f1_score(y_test, y_pred)
print(f"Acurácia do modelo KNN (número de predições assertivas sobre número total de testes): {acc}")
## SVM
from sklearn.svm import SVC
model = SVC(kernel = 'linear', C=0.025, random_state=seed)
model.fit(X_train, y_train)

y_pred = model.predict(X_test)
acc = f1_score(y_test, y_pred)
print(f"Acurácia do modelo SVC (número de predições assertivas sobre número total de testes): {acc}")<jupyter_output>/opt/conda/envs/Python-3.8-main/lib/python3.8/site-packages/sklearn/utils/validation.py:72: DataConversionWarning: A column-vector y was passed when a 1d array was expected. Please change the shape of y to (n_samples, ), for example using ravel().
  return f(**kwargs)<jupyter_text>SVC Como modelo escolhido<jupyter_code>steps=[0.005, 0.025, 0.050, 0.075 ,0.1 , 0.125 , 0.15 , 0.2, 0.25]

for x in steps:
    model = SVC(kernel = 'linear', C=x, random_state=seed)
    model.fit(X_train, y_train)

    y_pred = model.predict(X_test)
    acc = f1_score(y_test, y_pred)
    print(f"Acurácia do modelo SVC (número de predições assertivas sobre número total de testes): {acc}")
steps=[1234,17894,6547,123,123,345,5421]
for x in steps:
    model = SVC(kernel = 'linear', C=0.2, random_state=x)
    model.fit(X_train, y_train)

    y_pred = model.predict(X_test)
    acc = f1_score(y_test, y_pred)
    print(f"Acurácia do modelo SVC (número de predições assertivas sobre número total de testes): {acc}")<jupyter_output>/opt/conda/envs/Python-3.8-main/lib/python3.8/site-packages/sklearn/utils/validation.py:72: DataConversionWarning: A column-vector y was passed when a 1d array was expected. Please change the shape of y to (n_samples, ), for example using ravel().
  return f(**kwargs)<jupyter_text>Import Answers<jupyter_code>body = client_6991ccecda0244a5955822de5d9e513e.get_object(Bucket='bantotal-donotdelete-pr-hrmrwbzp4wmpy1',Key='ANSWERS.csv')['Body']
# add missing __iter__ method, so pandas accepts body as file-like object
if not hasattr(body, "__iter__"): body.__iter__ = types.MethodType( __iter__, body )

df_data_4 = pd.read_csv(body)
df_data_4.head()

df_answers = df_data_4
df_answers = df_answers.dropna()

df_answers['PAYMENT_TERM'] = mms.fit_transform(df_answers[['PAYMENT_TERM']].values)
df_answers['LOAN_AMOUNT'] = mms.fit_transform(df_answers[['LOAN_AMOUNT']].values)

installment_plans_dict={'NONE' : 1 , 'STORES' : 2 , 'BANK' : 3 }
df_answers['INSTALLMENT_PLANS'] = df_answers['INSTALLMENT_PLANS'].map(installment_plans_dict)

loan_propose_dict={'CAR_NEW' : 1 , 'CAR_USED' : 2 , 'FURNITURE' : 3 , 'RADIO_TV':4 ,'VACATION':5 ,'REPAIRS':6 , 'RADIO_TV':7, 'RETRAINING':8, 'APPLIANCES':9 ,'EDUCATION':10 ,'BUSINESS':11}
df_answers['LOAN_PURPOSE'] = df_answers['LOAN_PURPOSE'].map(loan_propose_dict)

other_on_loan_dict={'NONE' : 1 , 'CO-APPLICANT' : 2 , 'GUARANTOR' : 3 }
df_answers['OTHERS_ON_LOAN'] = df_answers['OTHERS_ON_LOAN'].map(other_on_loan_dict)

credit_history_dict={'OUTSTANDING_CREDIT' : 1 , 'CREDITS_PAID_TO_DATE' : 2 , 'ALL_CREDITS_PAID_BACK': 3 ,'PRIOR_PAYMENTS_DELAYED': 4, 'NO_CREDITS': 5 }
df_answers['CREDIT_HISTORY'] = df_answers['CREDIT_HISTORY'].map(credit_history_dict)

sex_dict={'F' : 1 , 'M' : 2 }
df_answers['SEX'] = df_answers['SEX'].map(sex_dict)

property_dict={ 'REAL_ESTATE' : 1 ,'SAVINGS_INSURANCE'  : 2 , 'CAR_OTHER' : 3 , 'UNKNOWN' : 4 }
df_answers['PROPERTY'] = df_answers['PROPERTY'].map(property_dict)

housing_dict={ 'OWN' : 1 ,'RENT'  : 2 , 'FREE' : 3 }
df_answers['HOUSING'] = df_answers['HOUSING'].map(housing_dict)

df_answers['EXISTING_SAVINGS'] = df_answers['EXISTING_SAVINGS'].apply(lambda x : 0 if x == 'UNKNOWN' else x)
df_answers['EXISTING_SAVINGS'] = df_answers['EXISTING_SAVINGS'].astype('float64')

df_answers['CHECKING_BALANCE'] = df_answers['CHECKING_BALANCE'].apply(lambda x : 1 if x == 'NO_CHECKING' or 0 else x)
df_answers['CHECKING_BALANCE'] = df_answers['CHECKING_BALANCE'].astype('float64')
df_answers['CHECKING_BALANCE'] = df_answers['CHECKING_BALANCE'].apply(lambda x : 2 if x > 0 else 0)

allow_dict={'TO_FILL': 0}
df_answers['ALLOW'] = df_answers['ALLOW'].map(allow_dict)
df_answers.at[1,'ALLOW'] = 1
df_answers['ALLOW'].describe()
features_answers = ['PAYMENT_TERM', 'INSTALLMENT_PERCENT', 'OTHERS_ON_LOAN', 'CHECKING_BALANCE', 'EXISTING_CREDITS_COUNT', 'CREDIT_HISTORY','AGE', 'TELEPHONE',  'EMPLOYMENT_DURATION', 'PROPERTY', 'CURRENT_RESIDENCE_DURATION']
target_answers = ['ALLOW']

X_answers = df_answers[features]
y_answers = df_answers[target]
X
model = SVC(kernel = 'linear', C=0.2)
model.fit(X_train, y_train)

y_pred = model.predict(X_answers)
acc = f1_score(y_answers, y_pred)
print(f"Acurácia do modelo SVC (número de predições assertivas sobre número total de testes): {acc}")
df_answers['ALLOW'] = y_pred
df_answers.to_csv(r'Downloads\ANSWERED.CSV')<jupyter_output><empty_output><jupyter_text>Apesar de estarmos utilizando somente algumas variáveis do dataset carregado, o desafio espera um modelo que aceite todas as variáveis dos conjuntos de dados disponíveis. Portanto, vamos utilizar um transformador para transformar os dados de entrada, removendo as colunas que não queremos, antes enviá-los ao nosso modelo. Dessa forma, criaremos uma `Pipeline`, que utiliza o transformador como entrada, e o nosso modelo em seguida.Fica como tarefa para você unir os outros conjuntos de dados disponíveis e utilizá-los também para predições no modelo, em vez de remover as colunas.  Sobre PipelinesUma `Pipeline`, da biblioteca `scikit-learn`, consiste em uma série de passos onde realizamos transformações em dados. As transformações são definidas por classes que devem ter sempre **dois métodos**:- **fit**: Um método que recebe dados de treinamento, e retorna a própria instância da classe. Ele é aplicado quando se vai treinar utilizar uma Pipeline para treinar um modelo.- **transform**: Um método que recebe como entrada um conjunto de dados e deve retornar um outro conjunto de dados, transformado. Ele é aplicado em cada etapa da Pipeline, recebendo os dados do passo anterior e transformando-os.Veja abaixo uma representação gráfica do funcionamento de uma Pipeline: ![](https://s3.br-sao.cloud-object-storage.appdomain.cloud/maratona-static/pipeline.png) Nesse Notebook, vamos criar uma Pipeline muito similar ao exemplo acima, com dois estágios:- **drop_columns**: Remove as colunas indesejadas do conjunto de dados de entrada.- **classification**: Alimenta um modelo de classificação com os dados obtidos no estágio **drop_columns**, podendo ser tanto para treinamento quanto para obter uma predição.  Criação de Pipelines no scikit-learnPara criar um modelo capaz de fazer transformações nos dados de entrada, vamos criar uma `Pipeline` do `scikit-learn` e aplicar nossas transformações dentro dos estágios dela.Abaixo, definimos um transformador exemplo, que irá remover as colunas passadas como parâmetro em sua inicialização:<jupyter_code>from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.pipeline import Pipeline


# Um transformador para remover colunas indesejadas
class DropColumns(BaseEstimator, TransformerMixin):
    def __init__(self, columns):
        self.columns = columns

    def fit(self, X, y=None):
        return self

    def transform(self, X):
        # Primeiro realizamos a cópia do DataFrame 'X' de entrada
        data = X.copy()
        # Retornamos um novo dataframe sem as colunas indesejadas
        return data.drop(labels=self.columns, axis='columns')<jupyter_output><empty_output><jupyter_text>Tanto o método `fit` quanto o `transform` devem obrigatoriamente ser definidos, mesmo se não forem fazer nada de diferente, como no caso do `fit` acima.Da mesma forma, você pode criar outros transformadores, para outros propósitos, sempre herdando das classes `BaseEstimator` e `TransformerMixin`. Você pode utilizar um transformador para, por exemplo, criar novas colunas, editar tipos de dados de colunas existentes, entre outros.Agora, vamos criar uma Pipeline para utilização do nosso modelo, aceitando todas as colunas esperadas pelo desafio e removendo as que não queremos usar.<jupyter_code>challenge_columns = ['ID', 'CHECKING_BALANCE', 'PAYMENT_TERM', 'CREDIT_HISTORY',
       'LOAN_PURPOSE', 'LOAN_AMOUNT', 'EXISTING_SAVINGS',
       'EMPLOYMENT_DURATION', 'INSTALLMENT_PERCENT', 'SEX', 'OTHERS_ON_LOAN',
       'CURRENT_RESIDENCE_DURATION', 'PROPERTY', 'AGE', 'INSTALLMENT_PLANS',
       'HOUSING', 'EXISTING_CREDITS_COUNT', 'JOB_TYPE', 'DEPENDENTS',
       'TELEPHONE', 'FOREIGN_WORKER', 'ALLOW']

unwanted_columns = list((set(challenge_columns) - set(target)) - set(features)) # Remover todas as colunas que não são features do nosso modelo
# Criando uma instância do transformador, passando como parâmetro as colunas que não queremos
drop_columns = DropColumns(unwanted_columns)


# Criando a Pipeline, adicionando o nosso transformador seguido de um modelo de árvore de decisão
skl_pipeline = Pipeline(steps=[('drop_columns', drop_columns), ('classification', model)])<jupyter_output><empty_output><jupyter_text>Pronto! Essa pipeline agora está pronta para receber todas as variáveis do desafio, apesar de o modelo só usar algumas.  Deploy do modelo para o Watson Machine Learning (WML)Agora temos o modelo pronto para publicação, queremos deixá-lo online para que o sistema da Maratona possa testá-lo :)Para isso, vamos utilizar a biblioteca `IBM Watson Machine Learning`, que permite realizar o encapsulamento de modelos de Machine Learning em APIs REST.<jupyter_code># Instalar a biblioteca do WML
!pip install -U ibm-watson-machine-learning
from ibm_watson_machine_learning import APIClient<jupyter_output><empty_output><jupyter_text>Caso ainda não tiver criado, crie um serviço de Machine Learning aqui: https://cloud.ibm.com/catalog/services/machine-learning.Insira suas credenciais para o serviço na célula abaixo.Em `location`, insira o ID da região onde se encontra o seu serviço WML instanciado, de acordo com as possibilidades abaixo:- Dallas - `us-south`- London - `eu-gb`- Frankfurt - `eu-de`- Tokyo - `jp-tok`Para a API key, você deve gerá-la aqui: https://cloud.ibm.com/iam/apikeys. Não compartilhe-a com ninguém! Uma API key dá acesso à sua conta IBM Cloud.<jupyter_code>api_key = '<KEY>'
location = 'jp-tok' # Caso o WML estiver em uma região diferente, altere

wml_credentials = {
    "apikey": api_key,
    "url": 'https://' + location + '.ml.cloud.ibm.com'
}

client = APIClient(wml_credentials)<jupyter_output><empty_output><jupyter_text>Crie um espaço para salvar o seu modelo. Você pode criá-lo aqui: https://dataplatform.cloud.ibm.com/ml-runtime/spaces?context=cpdaasQuando criar o seu espaço, **associe a instância do seu serviço WML ao espaço!** Sem associar, você não conseguirá efetuar o deploy.<jupyter_code># Listar espaços criados na sua instância de WML
client.spaces.list(limit=10)<jupyter_output><empty_output><jupyter_text>Copie o ID do seu espaço criado para o desafio e cole-o abaixo para utilizá-lo. Você deverá ver a mensagem 'SUCCESS' se o espaço estiver corretamente configurado.<jupyter_code>space_id = 'ad37aa54-a561-4aa6-a977-ca4143b4f96b'
client.set.default_space(space_id)<jupyter_output><empty_output><jupyter_text>Utilização da Pipeline dentro do Watson Machine Learning (WML)Para utilizar uma Pipeline no WML com transformadores customizados, são necessários alguns passos adicionais:1. Criar um pacote no Python contendo o transformador customizado;2. Carregar esse pacote com o transformador em um repositório no WML;3. Criar uma especificação de software, com esse pacote customizado, que vai ser utilizada como tempo de execução do modelo no WML.Como exemplo, vamos utilizar um pacote já pronto, disponível aqui: https://github.com/vnderlev/watson-sklearn-transforms. Para configurar o pacote Python, são necessários alguns outros arquivos, mas a lógica do transformador criado se encontra [neste arquivo](https://github.com/vnderlev/watson-sklearn-transforms/blob/master/my_custom_sklearn_transforms/sklearn_transformers.py). No caso, esse é o mesmo transformador que definimos aqui, ele vai excluir do conjunto de dados as colunas passadas como parâmetro na sua inicialização.Abaixo, vamos baixar esse pacote do GitHub e instalá-lo no Python.<jupyter_code>!rm -rf watson-sklearn-transforms # Remover a pasta caso já exista
!git clone https://github.com/vnderlev/watson-sklearn-transforms # Clonar o repositório com o pacote
!zip -r drop-columns.zip watson-sklearn-transforms # Zipar o pacote
!pip install drop-columns.zip # Instalar o pacote zipado<jupyter_output><empty_output><jupyter_text>Vamos agora recriar nossa Pipeline utilizando esse pacote instalado.<jupyter_code>from my_custom_sklearn_transforms.sklearn_transformers import DropColumns

drop_columns = DropColumns(unwanted_columns)

pipeline = Pipeline(steps=[('drop_columns', drop_columns), ('classification', model)])<jupyter_output><empty_output><jupyter_text>Vamos agora subir o transformador customizado que baixamos para o WML.<jupyter_code># Metadados para o pacote customizado
meta_prop_pkg_extn = {
    client.package_extensions.ConfigurationMetaNames.NAME: "Drop_Columns",
    client.package_extensions.ConfigurationMetaNames.DESCRIPTION: "Extensão para remover colunas",
    client.package_extensions.ConfigurationMetaNames.TYPE: "pip_zip"
}

# Subir o pacote
pkg_extn_details = client.package_extensions.store(meta_props=meta_prop_pkg_extn, file_path="drop-columns.zip")

# Salvar as informações sobre o pacote
pkg_extn_uid = client.package_extensions.get_uid(pkg_extn_details)
pkg_extn_url = client.package_extensions.get_href(pkg_extn_details)<jupyter_output><empty_output><jupyter_text>Vamos agora criar uma especificação de software com o nosso pacote customizado, para que o WML possa utilizar. Caso estiver utilizando um software diferente de `Python 3.8` ou biblioteca diferente de `scikit-learn`, você pode dar uma olhada na lista de especificações de software suportadas pelo WML: https://dataplatform.cloud.ibm.com/docs/content/wsj/wmls/wmls-deploy-python-types.html?context=analytics&audience=wdp<jupyter_code>base_sw_spec_uid = client.software_specifications.get_uid_by_name("default_py3.8")

# Caso queira utilizar um software diferente de Python 3.8 como base, dê uma olhada nos disponíveis com a linha abaixo
# client.software_specifications.list(limit=100)
# Metadados da nova especificação de software
meta_prop_sw_spec = {
    client.software_specifications.ConfigurationMetaNames.NAME: "sw_spec_drop_columns",
    client.software_specifications.ConfigurationMetaNames.DESCRIPTION: "Software specification utilizando DropColumns",
    client.software_specifications.ConfigurationMetaNames.BASE_SOFTWARE_SPECIFICATION: {"guid": base_sw_spec_uid}
}

# Criando a nova especificação de software e obtendo seu ID
sw_spec_details = client.software_specifications.store(meta_props=meta_prop_sw_spec)
sw_spec_uid = client.software_specifications.get_uid(sw_spec_details)

# Adicionando o pacote customizado à nova especificação
client.software_specifications.add_package_extension(sw_spec_uid, pkg_extn_uid)<jupyter_output><empty_output><jupyter_text>Finalmente, vamos publicar a pipeline utilizando a especificação de software customizada que criamos.<jupyter_code># Metadados do modelo
model_props = {
    client.repository.ModelMetaNames.NAME: "Modelo com Pipeline customizada",
    client.repository.ModelMetaNames.TYPE: 'scikit-learn_0.23',
    client.repository.ModelMetaNames.SOFTWARE_SPEC_UID: sw_spec_uid
}

# Publicando a Pipeline como um modelo
published_model = client.repository.store_model(model=pipeline, meta_props=model_props)
published_model_uid = client.repository.get_model_uid(published_model)
client.repository.get_details(published_model_uid)<jupyter_output><empty_output><jupyter_text>Seu modelo agora está salvo. Vamos agora deixá-lo disponível online, para que possamos testá-lo:<jupyter_code># Metadados para publicação do modelo
metadata = {
    client.deployments.ConfigurationMetaNames.NAME: "Publicação do modelo customizado",
    client.deployments.ConfigurationMetaNames.ONLINE: {}
}

# Publicar
created_deployment = client.deployments.create(published_model_uid, meta_props=metadata)<jupyter_output><empty_output><jupyter_text>Parabéns!Seu modelo está agora publicado. Quando estiver pronto para submeter o desafio, você pode acessar https://maratona.dev/challenges/1, e utilizar as credenciais abaixo para realizar a submissão. Lembre-se de revisar todas as instruções no [README](https://github.com/maratonadev/desafio-1-2021) antes de submeter!<jupyter_code>deployment_uid = client.deployments.get_uid(created_deployment)

print(f"Credenciais para envio (não compartilhe esses dados com ninguém!)\n\nAPI key: {api_key}\nDeployment ID: {deployment_uid}")<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10331741564150407, 'fraction_numerical': 0.018753360108788465, 'mean_word_length': 6.058482142857143, 'pattern_counts': {'":': 3, '<': 108, '<?xml version=': 0, '>': 110, 'https://': 15, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '27', 'dir': 'jupyter-structured-clean-dedup', 'id': '38087', 'license': 'MIT', 'n_tokens_mistral': 11894, 'n_tokens_neox': 11266, 'n_words': 2804, 'path': 'notebook-pt.ipynb', 'repo_name': 'PDamaral/Bank_Loan_Risk_Analysis', 'size': '31699'}