Skip to content

Entrega Individual

  1. Alexandre Martinelli

Introdução

  1. 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

  1. 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.
  2. Sex (Sexo): Essa coluna tem o sexo de cada paciente, divididos em 52% Masculino e 48% feminino, dados do tipo String.
  3. 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.
  4. 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.
  5. 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.
  6. 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
import pandas as pd
from sklearn.preprocessing import LabelEncoder

# Preprocess the data
def preprocess(df):
    # Fill missing values
    df['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 variables
    label_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 features
    features = ['Age', 'Sex', 'BP', 'Cholesterol', 'Na_to_K', 'Drug']
    return df[features]

# Load the dataset
df = 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)

# Preprocessing
df = preprocess(df)

# Display the first few rows of the dataset
print(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
import pandas as pd
from sklearn.preprocessing import LabelEncoder

def standardization(df):

    df['Z-Age'] = df['Age'].apply(lambda x: (x-df['Age'].mean())/df['Age'].std())
    df['N-Age'] = df['Age'].apply(lambda x: (x-df['Age'].min())/(df['Age'].max()-df['Age'].min()))
    df['Z-Na_to_K'] = df['Na_to_K'].apply(lambda x: (x-df['Na_to_K'].mean())/df['Na_to_K'].std())
    df['N-Na_to_K'] = df['Na_to_K'].apply(lambda x: (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())

def preprocess(df):
    # Fill missing values
    df['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 variables
    label_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 features
    features = ['Age', 'Sex', 'BP', 'Cholesterol', 'Na_to_K', 'Drug']
    return df[features]

# Load the dataset
df = 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)

# Preprocessing
df = 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

Acurácia: 0.56%
Matriz de Confusão:

Classe Pred 0 Classe Pred 1 Classe Pred 2 Classe Pred 3 Classe Pred 4
Classe Real 0 0 0 0 0 17
Classe Real 1 0 0 0 0 13
Classe Real 2 0 0 0 0 11
Classe Real 3 0 0 0 43 0
Classe Real 4 0 0 0 30 46
2025-09-28T23:59:34.061538 image/svg+xml Matplotlib v3.10.6, https://matplotlib.org/

import numpy as np
import matplotlib.pyplot as plt
from io import StringIO
import pandas as pd
from scipy.stats import mode
from sklearn.preprocessing import LabelEncoder
from sklearn.cluster import KMeans
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, confusion_matrix
from sklearn.decomposition import PCA  


plt.figure(figsize=(12, 10))

def standardization(df):
    df['Z-Age'] = df['Age'].apply(lambda x: (x - df['Age'].mean()) / df['Age'].std())
    df['N-Age'] = df['Age'].apply(lambda x: (x - df['Age'].min()) / (df['Age'].max() - df['Age'].min()))
    df['Z-Na_to_K'] = df['Na_to_K'].apply(lambda x: (x - df['Na_to_K'].mean()) / df['Na_to_K'].std())
    df['N-Na_to_K'] = df['Na_to_K'].apply(lambda x: (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']
    return df[features]

def preprocess(df):
    # Fill missing values
    df['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 variables
    label_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'])
    df['Drug'] = label_encoder.fit_transform(df['Drug'])

    features = ['Age', 'Sex', 'BP', 'Cholesterol', 'Na_to_K', 'Drug']
    return df[features]

label_encoder = LabelEncoder()
# Load dataset
df = pd.read_csv('https://raw.githubusercontent.com/alexandremartinelli11/machine-learning/refs/heads/main/data/kaggle/drug200.csv')

# Preprocessing
d = preprocess(df.copy())
d = standardization(d)

X = d[['N-Age', 'BP', 'Cholesterol', 'N-Na_to_K']]
y = d['Drug']

#Separar em teste e validação
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# Reduzir para 2 dimensões com PCA
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_train)

# Treinar KMeans
kmeans = KMeans(n_clusters=5, init="k-means++", max_iter=100, random_state=42)
labels = kmeans.fit_predict(X_pca)

# Mapear clusters para classes reais por voto majoritário
cluster_map = {}
for c in np.unique(labels):
    mask = labels == c
    majority_class = mode(y_train[mask], keepdims=False)[0]
    cluster_map[c] = majority_class

# Reatribuir clusters como classes previstas
y_pred = np.array([cluster_map[c] for c in labels])

# Calcular acurácia e matriz de confusão
acc = accuracy_score(y_train, y_pred)
cm = confusion_matrix(y_train, y_pred)

cm_df = pd.DataFrame(
    cm,
    index=[f"Classe Real {cls}" for cls in np.unique(y_train)],
    columns=[f"Classe Pred {cls}" for cls in np.unique(y_train)]
)

print(f"Acurácia: {acc:.2f}%")
print("<br>Matriz de Confusão:")
print(cm_df.to_html())

# Também projetar os centróides no PCA
centroids_pca = kmeans.cluster_centers_


# Plot results
plt.figure(figsize=(10, 8))
plt.scatter(X_pca[:, 0], X_pca[:, 1], c=labels, cmap='viridis', s=50)
plt.scatter(centroids_pca[:, 0], centroids_pca[:, 1], 
           c='red', marker='*', s=200, label='Centroids')
plt.title('K-Means Clustering (PCA 2D)')
plt.xlabel('Principal Component 1')
plt.ylabel('Principal Component 2')
plt.legend()

# Save plot to buffer
buffer = StringIO()
plt.savefig(buffer, format="svg", transparent=True)
print(buffer.getvalue())

Avaliação do Modelo

O K-Means, mesmo após redução com PCA, não conseguiu reproduzir bem as cinco classes do conjunto de dados. A matriz de confusão mostra que os clusters formados concentram-se principalmente em duas classes, indicando que o algoritmo não separou adequadamente todas as categorias. Assim, a acurácia obtida reflete apenas a sobreposição entre clusters e classes reais. Uma possível melhoria seria aumentar a quantidade de dados.

Referências

Material for MkDocs