Exploração de dados: Ao selecionar uma base no kaggle referentes a cinco tipos de remédio, remédio A, B, C, X e Y, tem como objetivo prever qual remédio o paciente teria uma resposta melhor. As colunas presentes nesse dataset são Idade, Sexo, Pressão Arterial, nivel de colesterol, nivel de sódio para potássio no sangue e remédio que seria nossa target.
Colunas
Age (Idade): Essa coluna temos a idade dos pacientes, com a idade minima presente de 15, idade média de 44,3 e maxima de 74 sendo do tipo Integer.
Sex (Sexo): Essa coluna tem o sexo de cada paciente, divididos em 52% Masculino e 48% feminino, dados do tipo String.
Blood Pressure (Pressão Arterial): Essa coluna tem os niveis de pressão arterial de cada paciente sendo dividida em 39% HIGH, 29% NORMAL e 32% LOW, dados do tipo String.
Cholesterol (nivel de colesterol): Essa coluna tem os niveis de colesterol de cada paciente sendo divididos em 52% HIGH e 49% NORMAL, dados do tipo String.
Na_to_K (sódio para potássio): Essa coluna tem os a razão de sódio para potássio no sangue de um paciente, com a minima de 6,27, media de 16,1 e maxima de 38,2, dados do tipo Float/Decimal.
Drug (remédio): Essa coluna tem os remédio de melhor resposta para o paciente, dados do tipo String.
Age
Sex
BP
Cholesterol
Na_to_K
Drug
36
M
LOW
NORMAL
11.424
drugX
16
F
HIGH
NORMAL
15.516
drugY
18
F
NORMAL
NORMAL
8.75
drugX
59
F
LOW
HIGH
10.444
drugC
47
M
LOW
NORMAL
33.542
drugY
51
M
HIGH
HIGH
18.295
drugY
18
F
HIGH
NORMAL
24.276
drugY
28
F
NORMAL
HIGH
12.879
drugX
42
M
HIGH
NORMAL
12.766
drugA
66
F
NORMAL
NORMAL
8.107
drugX
Pré-processamento
Primeiro foi feita uma verificação em todas as colunas procurando valores faltantes e substituindo eles pela mediana em valores numéricos ou pela moda em variáveis categóricas. Como vimos na descrição das colunas temos três que possuem dados categóricos do tipo String, sendo elas Sex(Sexo), Blood Pressure(Pressão Arterial) e Cholesterol(nivel de colesterol), para conseguirmos utilizar essas informações é necessario convertelas em numeros, oque foi feito utilizando a biblioteca scikit-learn que possui a função LabelEncoder(), em seguida aplicamos dois tipos de escalonamento às colunas numéricas Age e Na_to_K: padronização (z-score) e normalização min-max.
N-Age
Sex
BP
Cholesterol
N-Na_to_K
Drug
0.4
1
1
1
0.130411
drugX
0
0
0
1
0.291292
drugY
0.04
0
2
1
0.0252801
drugX
0.86
0
1
0
0.0918813
drugC
0.62
1
1
1
1
drugY
0.7
1
0
0
0.40055
drugY
0.04
0
0
1
0.635699
drugY
0.24
0
2
0
0.187615
drugX
0.52
1
0
1
0.183173
drugA
1
0
2
1
0
drugX
importpandasaspdfromsklearn.preprocessingimportLabelEncoder# Preprocess the datadefpreprocess(df):# Fill missing valuesdf['Age'].fillna(df['Age'].median(),inplace=True)df['Sex'].fillna(df['Sex'].mode()[0],inplace=True)df['BP'].fillna(df['BP'].mode()[0],inplace=True)df['Cholesterol'].fillna(df['Cholesterol'].mode()[0],inplace=True)df['Na_to_K'].fillna(df['Na_to_K'].median(),inplace=True)df['Drug'].fillna(df['Drug'].mode()[0],inplace=True)# Convert categorical variableslabel_encoder=LabelEncoder()df['Sex']=label_encoder.fit_transform(df['Sex'])df['BP']=label_encoder.fit_transform(df['BP'])df['Cholesterol']=label_encoder.fit_transform(df['Cholesterol'])# Select featuresfeatures=['Age','Sex','BP','Cholesterol','Na_to_K','Drug']returndf[features]# Load the datasetdf=pd.read_csv('https://raw.githubusercontent.com/alexandremartinelli11/machine-learning/refs/heads/main/data/kaggle/drug200.csv')df=df.sample(n=10,random_state=42)# Preprocessingdf=preprocess(df)# Display the first few rows of the datasetprint(df.to_markdown(index=False))
Age
N-Age
Z-Age
Na_to_K
N-Na_to_K
Z-Na_to_K
95
36
0.4
-0.117416
11.424
0.130411
-0.526121
15
16
0
-1.23566
15.516
0.291292
-0.0105705
30
18
0.04
-1.12384
8.75
0.0252801
-0.863018
158
59
0.86
1.16857
10.444
0.0918813
-0.649591
128
47
0.62
0.49762
33.542
1
2.26052
115
51
0.7
0.72127
18.295
0.40055
0.339555
69
18
0.04
-1.12384
24.276
0.635699
1.0931
170
28
0.24
-0.564715
12.879
0.187615
-0.342806
174
42
0.52
0.218058
12.766
0.183173
-0.357043
45
66
1
1.55996
8.107
0
-0.944029
importpandasaspdfromsklearn.preprocessingimportLabelEncoderdefstandardization(df):df['Z-Age']=df['Age'].apply(lambdax:(x-df['Age'].mean())/df['Age'].std())df['N-Age']=df['Age'].apply(lambdax:(x-df['Age'].min())/(df['Age'].max()-df['Age'].min()))df['Z-Na_to_K']=df['Na_to_K'].apply(lambdax:(x-df['Na_to_K'].mean())/df['Na_to_K'].std())df['N-Na_to_K']=df['Na_to_K'].apply(lambdax:(x-df['Na_to_K'].min())/(df['Na_to_K'].max()-df['Na_to_K'].min()))df=df[['Age','N-Age','Z-Age','Na_to_K','N-Na_to_K','Z-Na_to_K']].dropna()print(df.head(10).to_markdown())defpreprocess(df):# Fill missing valuesdf['Age'].fillna(df['Age'].median(),inplace=True)df['Sex'].fillna(df['Sex'].mode()[0],inplace=True)df['BP'].fillna(df['BP'].mode()[0],inplace=True)df['Cholesterol'].fillna(df['Cholesterol'].mode()[0],inplace=True)df['Na_to_K'].fillna(df['Na_to_K'].median(),inplace=True)df['Drug'].fillna(df['Drug'].mode()[0],inplace=True)# Convert categorical variableslabel_encoder=LabelEncoder()df['Sex']=label_encoder.fit_transform(df['Sex'])df['BP']=label_encoder.fit_transform(df['BP'])df['Cholesterol']=label_encoder.fit_transform(df['Cholesterol'])# Select featuresfeatures=['Age','Sex','BP','Cholesterol','Na_to_K','Drug']returndf[features]# Load the datasetdf=pd.read_csv('https://raw.githubusercontent.com/alexandremartinelli11/machine-learning/refs/heads/main/data/kaggle/drug200.csv')df=df.sample(n=10,random_state=42)# Preprocessingdf=preprocess(df)standardization(df)
Divisão dos Dados
O conjunto de dados foi dividido em 70% para treino e 30% para validação, garantindo que o modelo fosse treinado em uma parte significativa das observações, mas ainda avaliado em dados não vistos. O uso do conjunto de validação tem como objetivo detectar e reduzir o risco de overfitting.
Treinamento do Modelo
Accuracy: 1.0
Relatório de Classificação:
precision
recall
f1-score
support
drugA
1.0
1.0
1.0
6.0
drugB
1.0
1.0
1.0
3.0
drugC
1.0
1.0
1.0
5.0
drugX
1.0
1.0
1.0
11.0
drugY
1.0
1.0
1.0
15.0
accuracy
1.0
1.0
1.0
1.0
macro avg
1.0
1.0
1.0
40.0
weighted avg
1.0
1.0
1.0
40.0
Matriz de Confusão:
drugA
drugB
drugC
drugX
drugY
drugA
6
0
0
0
0
drugB
0
3
0
0
0
drugC
0
0
5
0
0
drugX
0
0
0
11
0
drugY
0
0
0
0
15
Feature Importances:
Feature
Importance
3
N-Na_to_K
0.493261
1
BP
0.265658
0
N-Age
0.135510
2
Cholesterol
0.105571
Accuracy: 1.0
Relatório de Classificação:
precision
recall
f1-score
support
drugA
1.0
1.0
1.0
7.0
drugB
1.0
1.0
1.0
3.0
drugC
1.0
1.0
1.0
6.0
drugX
1.0
1.0
1.0
18.0
drugY
1.0
1.0
1.0
26.0
accuracy
1.0
1.0
1.0
1.0
macro avg
1.0
1.0
1.0
60.0
weighted avg
1.0
1.0
1.0
60.0
Matriz de Confusão:
drugA
drugB
drugC
drugX
drugY
drugA
7
0
0
0
0
drugB
0
3
0
0
0
drugC
0
0
6
0
0
drugX
0
0
0
18
0
drugY
0
0
0
0
26
Feature Importances:
Feature
Importance
3
N-Na_to_K
0.476110
1
BP
0.267512
0
N-Age
0.148169
2
Cholesterol
0.108209
Accuracy: 1.0
Relatório de Classificação:
precision
recall
f1-score
support
drugA
1.0
1.0
1.0
10.0
drugB
1.0
1.0
1.0
6.0
drugC
1.0
1.0
1.0
6.0
drugX
1.0
1.0
1.0
25.0
drugY
1.0
1.0
1.0
33.0
accuracy
1.0
1.0
1.0
1.0
macro avg
1.0
1.0
1.0
80.0
weighted avg
1.0
1.0
1.0
80.0
Matriz de Confusão:
drugA
drugB
drugC
drugX
drugY
drugA
10
0
0
0
0
drugB
0
6
0
0
0
drugC
0
0
6
0
0
drugX
0
0
0
25
0
drugY
0
0
0
0
33
Feature Importances:
Feature
Importance
3
N-Na_to_K
0.481166
1
BP
0.258655
0
N-Age
0.138054
2
Cholesterol
0.122125
Accuracy: 1.0
Relatório de Classificação:
precision
recall
f1-score
support
drugA
1.0
1.0
1.0
16.0
drugB
1.0
1.0
1.0
8.0
drugC
1.0
1.0
1.0
7.0
drugX
1.0
1.0
1.0
27.0
drugY
1.0
1.0
1.0
42.0
accuracy
1.0
1.0
1.0
1.0
macro avg
1.0
1.0
1.0
100.0
weighted avg
1.0
1.0
1.0
100.0
Matriz de Confusão:
drugA
drugB
drugC
drugX
drugY
drugA
16
0
0
0
0
drugB
0
8
0
0
0
drugC
0
0
7
0
0
drugX
0
0
0
27
0
drugY
0
0
0
0
42
Feature Importances:
Feature
Importance
3
N-Na_to_K
0.507161
1
BP
0.246185
2
Cholesterol
0.134811
0
N-Age
0.111843
Accuracy: 0.9916666666666667
Relatório de Classificação:
precision
recall
f1-score
support
drugA
1.000000
1.000000
1.000000
17.000000
drugB
1.000000
1.000000
1.000000
11.000000
drugC
1.000000
1.000000
1.000000
8.000000
drugX
1.000000
0.970588
0.985075
34.000000
drugY
0.980392
1.000000
0.990099
50.000000
accuracy
0.991667
0.991667
0.991667
0.991667
macro avg
0.996078
0.994118
0.995035
120.000000
weighted avg
0.991830
0.991667
0.991646
120.000000
Matriz de Confusão:
drugA
drugB
drugC
drugX
drugY
drugA
17
0
0
0
0
drugB
0
11
0
0
0
drugC
0
0
8
0
0
drugX
0
0
0
33
1
drugY
0
0
0
0
50
Feature Importances:
Feature
Importance
3
N-Na_to_K
0.512857
1
BP
0.265714
2
Cholesterol
0.117384
0
N-Age
0.104045
importmatplotlib.pyplotaspltimportpandasaspdfromioimportStringIOfromsklearnimporttreefromsklearn.model_selectionimporttrain_test_splitfromsklearn.preprocessingimportLabelEncoderfromsklearn.metricsimportaccuracy_score,classification_report,confusion_matrixdefstandardization(df):df['Z-Age']=df['Age'].apply(lambdax:(x-df['Age'].mean())/df['Age'].std())df['N-Age']=df['Age'].apply(lambdax:(x-df['Age'].min())/(df['Age'].max()-df['Age'].min()))df['Z-Na_to_K']=df['Na_to_K'].apply(lambdax:(x-df['Na_to_K'].mean())/df['Na_to_K'].std())df['N-Na_to_K']=df['Na_to_K'].apply(lambdax:(x-df['Na_to_K'].min())/(df['Na_to_K'].max()-df['Na_to_K'].min()))features=['N-Age','Sex','BP','Cholesterol','N-Na_to_K','Drug']returndf[features]defpreprocess(df):# Fill missing valuesdf['Age'].fillna(df['Age'].median(),inplace=True)df['Sex'].fillna(df['Sex'].mode()[0],inplace=True)df['BP'].fillna(df['BP'].mode()[0],inplace=True)df['Cholesterol'].fillna(df['Cholesterol'].mode()[0],inplace=True)df['Na_to_K'].fillna(df['Na_to_K'].median(),inplace=True)df['Drug'].fillna(df['Drug'].mode()[0],inplace=True)# Convert categorical variableslabel_encoder=LabelEncoder()df['Sex']=label_encoder.fit_transform(df['Sex'])df['BP']=label_encoder.fit_transform(df['BP'])df['Cholesterol']=label_encoder.fit_transform(df['Cholesterol'])# Select featuresfeatures=['Age','Sex','BP','Cholesterol','Na_to_K','Drug']returndf[features]# Load the datasetdf=pd.read_csv('https://raw.githubusercontent.com/alexandremartinelli11/machine-learning/refs/heads/main/data/kaggle/drug200.csv')# Preprocessingd=preprocess(df.copy())d=standardization(d)plt.figure(figsize=(12,10))# Carregar o conjunto de dadosx=d[['N-Age','BP','Cholesterol','N-Na_to_K']]y=d['Drug']# Dividir os dados em conjuntos de treinamento e testex_train,x_test,y_train,y_test=train_test_split(x,y,test_size=0.6,random_state=42)# Criar e treinar o modelo de árvore de decisãoclassifier=tree.DecisionTreeClassifier()classifier.fit(x_train,y_train)y_pred=classifier.predict(x_test)cm=confusion_matrix(y_test,y_pred)labels=classifier.classes_cm_df=pd.DataFrame(cm,index=labels,columns=labels)report_dict=classification_report(y_test,y_pred,output_dict=True)report_df=pd.DataFrame(report_dict).transpose()# Avaliar o modeloaccuracy=classifier.score(x_test,y_test)print("Accuracy:",accuracy_score(y_test,y_pred))print("<h3>Relatório de Classificação:</h3>")print(report_df.to_html(classes="table table-bordered table-striped",border=0))print("<h3>Matriz de Confusão:</h3>")print(cm_df.to_html(classes="table table-bordered table-striped",border=0))# Optional: Print feature importancesfeature_importance=pd.DataFrame({'Feature':['N-Age','BP','Cholesterol','N-Na_to_K'],'Importance':classifier.feature_importances_})print("<br>Feature Importances:")print(feature_importance.sort_values(by='Importance',ascending=False).to_html())tree.plot_tree(classifier)# Para imprimir na página HTMLbuffer=StringIO()plt.savefig(buffer,format="svg")print(buffer.getvalue())
Avaliação do Modelo
Como vimos na seção de treinamento, a base selecionada para o treinamento da árvore não é ideal, apresentando 100% de accuracy, precision, recall e F1-score em diferentes divisões dos dados (80%/20%, 70%/30%, 60%/40%, 50%/50%), apresentando pouca variação apenas quando a divisão chega em 40% treino e 60% teste. Isso indica que a árvore de decisão provavelmente está sofrendo de overfitting, ou seja, aprendeu particularidades específicas da base em vez de padrões gerais. Assim, embora apresente resultados excelentes nos testes, o modelo pode não generalizar bem para dados realmente novos. Uma possível melhoria seria aumentar a quantidade de dados para reduzir o overfitting.