<jupyter_start><jupyter_text>Ensemble para identificação de doença cardíaca **Nesse projeto iremos usar o método de ensemble para indentificar em um Dataset os pacientes com problemas cardíacos.**   Heart Disease Data Set  Conjunto de dadosExistem 13 colunas no conjunto de dados, que são descritas abaixo:- slope: slope_of_peak_exercise_st_segment (type: int): a inclinação do segmento ST do pico do exercício, uma eletrocardiografia lida indicando a qualidade do fluxo sanguíneo no coração- thal (tipo: categórico): resultados do teste de estresse com tálio que mede o fluxo sanguíneo para o coração, com possíveis valores normal, defeito fixo, defeito reversível- resting_blood_pressure (type: int): pressão arterial em repouso- chest: chest_pain_type (type: int): tipo de dor no peito (4 valores)- num_major_vessels (type: int): número de vasos principais (0-3) coloridos por flourosopy- fasting_blood_sugar_gt_120_mg_per_dl (tipo: binário): açúcar no sangue em jejum> 120 mg / dl- resting_electrocardiographic_results: resting_ekg_results (type: int): resultados eletrocardiográficos de repouso (valores 0,1,2)- serum_cholestoral: soro_colesterol_mg_por_dl (tipo: int): colesterol sérico em mg / dl- oldpeak: oldpeak_eq_st_depression (tipo: float): oldpeak = depressão do ST induzida pelo exercício em relação ao repouso, uma medida de anormalidade nos eletrocardiogramas- sex: sexo (tipo: binário): 0: feminino, 1: masculino- age: idade (tipo: int): idade em anos- maximum_heart_rate_achieved (type: int): frequência cardíaca máxima atingida (batimentos por minuto)- exercise_induced_angina (tipo: binário): dor no peito induzida por exercício (0: Falso, 1: Verdadeiro)Nosso objetivo é prever a classe binária **class**, que representa se um paciente tem ou não uma doença cardíaca:- 0 representa nenhuma doença cardíaca presente- 1 representa doença cardíaca presente<jupyter_code>from pandas import set_option
%matplotlib inline
from pathlib import Path
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import log_loss
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
from sklearn.model_selection import GridSearchCV
import xgboost as xgb
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
%time df = pd.read_csv('C:/Users/skite/OneDrive/Documentos/GitHub/Ensemble2/dataset_53_heart-statlog_csv.csv', sep = ',', encoding  = 'ISO-8859-1')
df.head()
df.info()<jupyter_output><class 'pandas.core.frame.DataFrame'>
RangeIndex: 270 entries, 0 to 269
Data columns (total 14 columns):
 #   Column                                Non-Null Count  Dtype  
---  ------                                --------------  -----  
 0   age                                   270 non-null    int64  
 1   sex                                   270 non-null    int64  
 2   chest                                 270 non-null    int64  
 3   resting_blood_pressure                270 non-null    int64  
 4   serum_cholestoral                     270 non-null    int64  
 5   fasting_blood_sugar                   270 non-null    int64  
 6   resting_electrocardiographic_results  270 non-null    int64  
 7   maximum_heart_rate_achieved           270 non-null    int64  
 8   exercise_induced_angina               270 non-null    int64  
 9   oldpeak                               270 non-null    float64
 10  slope                                 270 non-null    int64  
 11  number_of_major_ves[...]<jupyter_text>*Não temos dados faltantes, esse Dataset é relativamente pequeno e já está praticamente pronto para a aplicação de ensemble*<jupyter_code>df.describe()<jupyter_output><empty_output><jupyter_text>*Temos valores muito alto para o desfio padrão de age, resting_blood_pressure, serum_cholestoral, maximum_heart_rate_achieved. vamos plotar um distribuição das variáveis para melhor visualização.*<jupyter_code>df.hist(figsize=(30,24),bins = 20)
plt.title("Distribuição das variáveis")
plt.show()<jupyter_output><empty_output><jupyter_text>**Conforme identificamos na célula acima temos variáveis desbalanceadas que precisam de ajuste, mas não iremos fazer esses ajustes agora, pois quero ver como o método de ensemble se comporta com esses dados.** *Agora vamos verificação do balanceamento da Target*<jupyter_code>df['class'].value_counts()
sns.countplot(y=df['class'] ,data=df)
plt.xlabel("Contagem dos valores da target")
plt.ylabel("Target")
plt.show()<jupyter_output><empty_output><jupyter_text>**ok, agora vamos Substituindo os valores da variável target passando ela para binário**<jupyter_code>df.replace({'absent': 0, 'present': 1},inplace=True)
df.head()<jupyter_output><empty_output><jupyter_text>Separação dos dados em treino e teste **separação em dados de treino - 189 primeiras linhas o que representa 70% dos dados para treino.**<jupyter_code>train = df.iloc[:189,:] 
train.info()
df.tail()<jupyter_output><empty_output><jupyter_text>**Agora vamos separação em dados de teste - 81 ultimas linhas que representa 30% para os dados de teste**<jupyter_code>test = df.iloc[189:270,:]
test.info()
test.tail()<jupyter_output><empty_output><jupyter_text>*Agora precisamos retirar dos dados de treino da vairável target*<jupyter_code>train_labels = train['class']
train = train.drop(['class'],axis=1)
train.shape,train_labels.shape
set_option('display.width', 100)
set_option('precision', 3)
train.describe()<jupyter_output><empty_output><jupyter_text>*Agora vamos fazer o mesmo procedimento para os dados de teste ou seja dropar a target dos dados de teste.*<jupyter_code>test_labels = test['class']
test = test.drop(['class'],axis=1)
test.shape,test_labels.shape
test.head()
train.shape,train_labels.shape,test.shape, test_labels.shape<jupyter_output><empty_output><jupyter_text>Vamos fazer uma EDA para verificando a distribuição dos dados em relação à condição de doença cardíaca.<jupyter_code>train_labels = pd.DataFrame(train_labels)
train_labels.groupby('class').size()
sns.countplot(y=train_labels['class'] ,data=train_labels)
plt.xlabel("Número com doença cardíaca")
plt.ylabel("Target")
plt.show()
train.describe().T<jupyter_output><empty_output><jupyter_text>**plotando o heatmap de correlação**<jupyter_code>sns.heatmap(train.corr(method='pearson'),annot=True, cmap='icefire', linewidths=0.1)
fig=plt.gcf()
fig.set_size_inches(12,8)
plt.show()<jupyter_output><empty_output><jupyter_text>Note que em nosso dados de treino as variáveis tem baixa correlação entre si, tirando as variaveis slope x oldpear onde:    - slope: slope_of_peak_exercise_st_segment (type: int): a inclinação do segmento ST do pico do exercício, uma eletrocardiografia lida indicando a qualidade do fluxo sanguíneo no coração.    - oldpeak: oldpeak_eq_st_depression (tipo: float): oldpeak = depressão do ST induzida pelo exercício em relação ao repouso, uma medida de anormalidade nos eletrocardiogramas.     Vamos informa a importância das features **O XGBoost possui a classe plot_importance que informa a importância das features** *será necessário primeiro rodar o XGBoost para compreender a importância das colunas*<jupyter_code>from xgboost import plot_importance
model = xgb.XGBClassifier()
model.fit(train, train_labels['class'].values)<jupyter_output><empty_output><jupyter_text>**plot feature importance**<jupyter_code># plot feature importance
plot_importance(model)
plt.show()<jupyter_output><empty_output><jupyter_text>*Essa feature é incrível e mostra as variáveis mais importantes do data set  ou seja as variáveis que mais implicam com a target.*  Feature Engineering - variáveis categóricas *Agora será necessário fazer o feature engineering das variáveis categóricas.* *vamos "dummizando" os dados de treino e teste*<jupyter_code>one_hot_encoded_training_predictors = pd.get_dummies(train)
one_hot_encoded_test_predictors = pd.get_dummies(test)
train,test = one_hot_encoded_training_predictors.align(one_hot_encoded_test_predictors,
                                                                    join='left', 
                                                                    axis=1)<jupyter_output><empty_output><jupyter_text>Rodando o modelo preditor com o XGBoost. o que é o Ensemble XGBoost ? *Antes de explicar o XGBoost primeiro preciso citar o Gradient boosting que usa a Modelagem Aditiva, na qual uma nova árvore de decisão é adicionada uma de cada vez a um modelo que minimiza a perda usando a descida de gradiente. As árvores existentes no modelo permanecem intocadas e, assim, diminuem a taxa de sobre ajuste. A saída da nova árvore é combinada com a saída das árvores existentes até que a perda seja minimizada abaixo de um limite ou seja atingido o limite especificado de árvores. A modelagem aditiva em matemática é uma divisão de uma função na adição de N sub funções. Em termos estatísticos, pode ser pensado como um modelo de regressão em que a resposta y é a soma aritmética dos efeitos individuais das variáveis preditoras x.* **Xtreme Gradient Boosting:** *O XGBoost é a Evolução do Gradient Boosting, concebido com requisitos extremos de performance e paralelismo. Conta também com otimizações que contemplam regularização e validação cruzada a cada etapa.*   Vamos explorar todas as possibilidades do XGBoost<jupyter_code># dependências
from numpy import sort
from sklearn.feature_selection import SelectFromModel

# spliting dos dados
X_train, X_test, y_train, y_test = train_test_split(train, train_labels['class'].values, test_size=0.10, random_state=7)

# ajuste do modelo de treino
model = xgb.XGBClassifier()
model.fit(X_train, y_train)

# fazendo previsões para dados de teste e avaliar
y_pred = model.predict(X_test)
predictions = [round(value) for value in y_pred]
accuracy = accuracy_score(y_test, predictions)
print("Accuracy: %.2f%%" % (accuracy * 100.0))<jupyter_output>Accuracy: 68.42%<jupyter_text>*Ok, obtemos uma acurácia de 68.42% em nosso modelo*  Validação de diferentes features selecionadas através do SelectFromModel *Basicamente o SelectFromModel é Meta-transformador para selecionar recursos com base em pesos de importância.* Documentação: https://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.SelectFromModel.html<jupyter_code># Vamos fazer o fit do modelo usando diferentes features, iterativamente
thresholds = sort(model.feature_importances_)
for thresh in thresholds:
    
    # selecionando as features usando threshold
    selection = SelectFromModel(model, threshold=thresh, prefit=True)
    select_X_train = selection.transform(X_train)
    
    # treino do modelo
    selection_model = xgb.XGBClassifier()
    selection_model.fit(select_X_train, y_train)
    
    # avaliação do modelo
    select_X_test = selection.transform(X_test)
    y_pred = selection_model.predict(select_X_test)
    predictions = [round(value) for value in y_pred]
    accuracy = accuracy_score(y_test, predictions)
    print("Thresh=%.3f, n=%d, Accuracy: %.2f%%" % (thresh, select_X_train.shape[1], accuracy*100.0))<jupyter_output>Thresh=0.000, n=13, Accuracy: 68.42%
Thresh=0.027, n=12, Accuracy: 68.42%
Thresh=0.036, n=11, Accuracy: 68.42%
Thresh=0.040, n=10, Accuracy: 68.42%
Thresh=0.042, n=9, Accuracy: 68.42%
Thresh=0.043, n=8, Accuracy: 73.68%
Thresh=0.048, n=7, Accuracy: 68.42%
Thresh=0.058, n=6, Accuracy: 73.68%
Thresh=0.074, n=5, Accuracy: 68.42%
Thresh=0.104, n=4, Accuracy: 68.42%
Thresh=0.153, n=3, Accuracy: 78.95%
Thresh=0.171, n=2, Accuracy: 68.42%
Thresh=0.206, n=1, Accuracy: 63.16%<jupyter_text>Agora vamos verificando a performance do modelo para diferente número de estimadores<jupyter_code># spliting dos dados
train_X, test_X, train_Y, test_Y = train_test_split(train, 
                                                    train_labels, 
                                                    test_size=0.10, 
                                                    stratify=train_labels, 
                                                    random_state=42)
# resultados
print(train_X.shape, test_X.shape)
print()
print('Number of rows in Train dataset:',train_X.shape[0])
print()
print('Number of rows in Test dataset:',test_X.shape[0])<jupyter_output>(170, 13) (19, 13)

Number of rows in Train dataset: 170

Number of rows in Test dataset: 19<jupyter_text>*Normalização dos dados*<jupyter_code>scaler = StandardScaler()
train_X = scaler.fit_transform(train_X)
test_X = scaler.transform(test_X)<jupyter_output><empty_output><jupyter_text>*O XGBoost usa uma métrica popular chamada logloss 'perda de log', assim como a maioria dos outros algoritmos de aumento de gradiente. Essa métrica baseada em probabilidade é usada para medir o desempenho de um modelo de classificação.* *Nesse artigo <NAME> explica muito bem como funciona o logloss* créditos: https://medium.com/datadriveninvestor/understanding-the-log-loss-function-of-xgboost-8842e99d975d<jupyter_code># running XGBoost model
model_base = xgb.XGBClassifier(max_depth=1,
                        subsample=0.33,
                        objective='binary:logistic',
                        n_estimators=50,
                        learning_rate = 0.12)
eval_set = [(train_X, train_Y.values.ravel()), (test_X, test_Y.values.ravel())]
model_base.fit(train_X, train_Y.values.ravel(), 
               early_stopping_rounds=15, 
               eval_metric=["error", "logloss"], 
               eval_set=eval_set)<jupyter_output>[0]	validation_0-error:0.305882	validation_0-logloss:0.670562	validation_1-error:0.315789	validation_1-logloss:0.672204
Multiple eval metrics have been passed: 'validation_1-logloss' will be used for early stopping.

Will train until validation_1-logloss hasn't improved in 15 rounds.
[1]	validation_0-error:0.235294	validation_0-logloss:0.643742	validation_1-error:0.105263	validation_1-logloss:0.640345
[2]	validation_0-error:0.270588	validation_0-logloss:0.625286	validation_1-error:0.105263	validation_1-logloss:0.604707
[3]	validation_0-error:0.252941	validation_0-logloss:0.603008	validation_1-error:0.105263	validation_1-logloss:0.575888
[4]	validation_0-error:0.217647	validation_0-logloss:0.581696	validation_1-error:0.157895	validation_1-logloss:0.561476
[5]	validation_0-error:0.2	validation_0-logloss:0.561776	validation_1-error:0.157895	validation_1-logloss:0.537821
[6]	validation_0-error:0.211765	validation_0-logloss:0.547827	validation_1-error:0.210526	validation_1-logloss:0.521017
[...]<jupyter_text>Predições nos dados de Teste<jupyter_code>y_pred = model_base.predict(test_X)
predictions = [round(value) for value in y_pred]
accuracy = accuracy_score(test_Y, predictions)
print("Accuracy: %.2f%%" % (accuracy * 100.0))<jupyter_output>Accuracy: 84.21%<jupyter_text>**recuperando as métricas de desempenho**<jupyter_code>results = model_base.evals_result()
epochs = len(results['validation_0']['error'])
x_axis = range(0, epochs)<jupyter_output><empty_output><jupyter_text>Plot do log loss e erro de classificação<jupyter_code>fig, ax = plt.subplots()
ax.plot(x_axis, results['validation_0']['logloss'], label='Train')
ax.plot(x_axis, results['validation_1']['logloss'], label='Test')
ax.legend()
plt.ylabel('Log Loss')
plt.title('XGBoost Log Loss')
plt.show()

fig, ax = plt.subplots()
ax.plot(x_axis, results['validation_0']['error'], label='Train')
ax.plot(x_axis, results['validation_1']['error'], label='Test')
ax.legend()
plt.ylabel('Classification Error')
plt.title('XGBoost Classification Error')
plt.show()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09183410613086038, 'fraction_numerical': 0.037029881504379185, 'mean_word_length': 4.569942611190818, 'pattern_counts': {'":': 0, '<': 85, '<?xml version=': 0, '>': 86, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 18, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '22', 'dir': 'jupyter-structured-clean-dedup', 'id': '522561', 'license': 'Apache-2.0', 'n_tokens_mistral': 5697, 'n_tokens_neox': 5233, 'n_words': 1401, 'path': 'Ensemble-identificando_doenca_cardiaca.ipynb', 'repo_name': 'wagner-skiter/Projeto-Ensemble_XGBoost_para_identificacao_doenca_cardiaca', 'size': '15534'}