Skip to content

Projeto 1

Este Projeto I tem como objetivo explorar diferentes técnicas de aprendizado supervisionado e não supervisionado, aplicadas a um conjunto de dados real.

Serão utilizados algoritmos como:

  • Decision Tree: modelo interpretável e estruturado em forma de árvore de decisão, adequado para classificação e regressão.
  • K-Nearest Neighbors (KNN): algoritmo baseado em instâncias, que realiza previsões considerando a proximidade entre amostras.
  • K-Means: técnica de aprendizado não supervisionado voltada para agrupar dados em clusters de acordo com sua similaridade.

A partir da aplicação desses métodos, o projeto busca compreender os pontos fortes e limitações de cada abordagem, bem como analisar seu desempenho em diferentes cenários.

Exploração de dados

A análise inicial do conjunto de dados inclui a descrição da natureza das variáveis, estatísticas descritivas e visualizações para compreender a distribuição e relevância das informações.

Contexto da Base de Dados

O dataset utilizado neste projeto contém um grande volume de informações anonimizadas de pacientes, abrangendo tanto características gerais quanto condições pré-existentes.

No total, são disponibilizadas 21 variáveis (colunas) que representam atributos clínicos e demográficos, e 1.048.575 registros (linhas), correspondentes a pacientes únicos.

As variáveis são mistas, incluindo atributos numéricos e categóricos, além de um conjunto expressivo de features Booleanas, nas quais:
- O valor 1 indica "sim"
- O valor 2 indica "não"
- Valores como 97 e 99 representam dados ausentes ou não informados

Para os experimentos com modelos supervisionados, a variável target será classification, representando o resultado do teste de COVID-19 dos pacientes.

Descrição e Estatísticas Descritivas das colunas

Variável Descrição Estatísticas Descritivas (%)
USMER Indica se o paciente foi tratado em unidades médicas de primeiro, segundo ou terceiro nível. 1º nível: 36,8%
2º nível: 63,2%
MEDICAL_UNIT Tipo de instituição do Sistema Nacional de Saúde que prestou o atendimento. Unidade 12: 57,5%
Unidade 4: 30,0%
Demais unidades: 12,5%
SEX 1 para feminino e 2 para masculino. Feminino: 50,1%
Masculino: 49,9%
PATIENT_TYPE Tipo de atendimento recebido na unidade. 1 para alta domiciliar e 2 para hospitalização. Alta domiciliar: 80,9%
Hospitalização: 19,1%
DATE_DIED Se o paciente faleceu, indica a data da morte; caso contrário, 9999-99-99. Vivos: 92,7%
Óbitos registrados: 7,3%
INTUBED Se o paciente foi conectado ao respirador. Desconhecido (97): 80,9%
Não intubado (2): 15,2%
Intubado (1): 3,2%
Ignorado (99): 0,7%
PNEUMONIA Se o paciente já apresentava inflamação nos alvéolos pulmonares. Não: 85,1%
Sim: 13,4%
Ignorado: 1,5%
AGE Idade do paciente. Moda: 30 anos (2,6%)
Faixa 28–34: ~7,3%
PREGNANT Se o paciente está grávida. Não aplicável: 50,0%
Não grávida: 48,9%
Grávida: 0,8%
Ignorado: 0,3%
DIABETES Se o paciente tem diabetes. Não: 87,7%
Sim: 11,9%
Ignorado: 0,3%
COPD Se o paciente tem DPOC. Não: 98,3%
Sim: 1,4%
Ignorado: 0,3%
ASTHMA Se o paciente tem asma. Não: 96,7%
Sim: 3,0%
Ignorado: 0,3%
INMSUPR Se o paciente é imunossuprimido. Não: 98,3%
Sim: 1,4%
Ignorado: 0,3%
HIPERTENSION Se o paciente tem hipertensão. Não: 84,1%
Sim: 15,5%
Ignorado: 0,3%
OTHER_DISEASE Se o paciente tem outra doença. Não: 96,8%
Sim: 2,7%
Ignorado: 0,5%
CARDIOVASCULAR Se o paciente tem doença cardíaca ou vascular. Não: 97,7%
Sim: 2,0%
Ignorado: 0,3%
OBESITY Se o paciente é obeso. Não: 84,4%
Sim: 15,2%
Ignorado: 0,3%
RENAL_CHRONIC Se o paciente tem doença renal crônica. Não: 97,9%
Sim: 1,8%
Ignorado: 0,3%
TOBACCO Se o paciente faz uso de tabaco. Não: 91,7%
Sim: 8,0%
Ignorado: 0,3%
CLASIFFICATION_FINAL Resultados do teste de covid. Valores 1-3 = positivo em diferentes graus; ≥4 = negativo/inconclusivo. Positivo (1–3): 37,4%
Negativo/Inconclusivo (≥4): 62,6%
ICU Indica se o paciente foi internado em UTI. Desconhecido: 80,9%
Não: 16,8%
Sim: 1,6%
Ignorado: 0,7%

Pré Processamento

Primeiramente, verificamos a presença de valores nulos em todas as colunas. Na coluna AGE havia poucos valores ausentes em relação ao total, e por isso optamos por substituí-los pela mediana. Nas colunas categóricas, os valores ausentes foram substituídos pela moda. Em algumas variáveis, como DATE_DIED, identificamos a presença de “nulos lógicos” (valores ausentes que representam situações específicas, como “paciente não faleceu”). Para esses casos, foi feita uma análise contextual antes de qualquer imputação para evitar distorcer a informação. Por fim, aplicamos a normalização Min–Max à coluna AGE para colocá-la na mesma escala das variáveis categóricas codificadas, favorecendo o desempenho de algoritmos de machine learning baseados em distância ou gradientes.

USMER MEDICAL_UNIT SEX PATIENT_TYPE DATE_DIED INTUBED PNEUMONIA AGE PREGNANT DIABETES COPD ASTHMA INMSUPR HIPERTENSION OTHER_DISEASE CARDIOVASCULAR OBESITY RENAL_CHRONIC TOBACCO CLASIFFICATION_FINAL ICU DIED_FLAG
2 4 2 1 9999-99-99 97 2 0.272727 97 2 2 2 2 2 2 2 2 2 2 3 97 0
2 12 1 1 9999-99-99 97 2 0.214876 2 2 2 2 2 2 2 2 2 2 2 7 97 0
1 12 1 1 9999-99-99 97 2 0.396694 2 2 2 2 2 2 2 2 1 2 2 7 97 0
1 4 1 2 9999-99-99 99 99 0.157025 2 2 2 2 2 2 2 2 2 2 2 7 99 0
2 4 1 1 9999-99-99 97 2 0.297521 2 2 2 2 2 2 2 2 2 2 2 3 97 0
1 4 1 2 9999-99-99 2 2 0.520661 2 1 2 2 2 1 2 2 2 1 2 7 2 0
1 12 2 1 9999-99-99 97 2 0.280992 97 2 2 2 2 2 2 2 2 2 2 3 97 0
1 6 1 2 18/06/2020 2 1 0.528926 2 1 2 2 2 1 2 2 2 1 2 6 2 1
2 4 1 1 9999-99-99 97 2 0.272727 2 2 2 2 2 2 2 2 2 2 2 7 97 0
1 12 1 1 9999-99-99 97 2 0.429752 2 2 2 2 2 2 2 2 2 2 2 3 97 0
import pandas as pd


df = pd.read_csv('https://raw.githubusercontent.com/akemi-m/projetos-machine-learning/refs/heads/main/docs/projeto-1/covid_data.csv', low_memory=True)

d = df.copy()

fill_values = {
    'USMER': d['USMER'].mode()[0],
    'SEX': d['SEX'].mode()[0],
    'PATIENT_TYPE': d['PATIENT_TYPE'].mode()[0],
    'MEDICAL_UNIT': d['MEDICAL_UNIT'].mode()[0],
    'DATE_DIED': d['DATE_DIED'].mode()[0],
    'INTUBED': d['INTUBED'].mode()[0],
    'PNEUMONIA': d['PNEUMONIA'].mode()[0],
    'PREGNANT': d['PREGNANT'].mode()[0],
    'DIABETES': d['DIABETES'].mode()[0],
    'COPD': d['COPD'].mode()[0],
    'ASTHMA': d['ASTHMA'].mode()[0],
    'INMSUPR': d['INMSUPR'].mode()[0],
    'HIPERTENSION': d['HIPERTENSION'].mode()[0],
    'OTHER_DISEASE': d['OTHER_DISEASE'].mode()[0],
    'CARDIOVASCULAR': d['CARDIOVASCULAR'].mode()[0],
    'OBESITY': d['OBESITY'].mode()[0],
    'RENAL_CHRONIC': d['RENAL_CHRONIC'].mode()[0],
    'TOBACCO': d['TOBACCO'].mode()[0],
    'CLASIFFICATION_FINAL': d['CLASIFFICATION_FINAL'].mode()[0],
    'ICU': d['ICU'].mode()[0]
}

df.fillna(fill_values, inplace=True)

df['DIED_FLAG'] = (df['DATE_DIED'] != '9999-99-99').astype(int)
df['AGE'] = df['AGE'].fillna(df['AGE'].median())

# df.dropna()

age_min = df['AGE'].min()
age_max = df['AGE'].max()
df['AGE'] = (df['AGE'] - age_min) / (age_max - age_min)

print(df.sample(n=10).to_markdown(index=False))

Divisão dos Dados

O conjunto de dados foi dividido em 70% para treino e 30% para validação, garantindo que os modelos fossem treinados em partes significativa das observações, mas ainda avaliados em dados não vistos. O uso do conjunto de validação tem como objetivo detectar e reduzir o risco de overfitting.

Treinamento dos modelos

Árvore de Decisão

Accuracy: 0.65291681104227

Relatório de Classificação:

precision recall f1-score support
0 0.665532 0.897268 0.764219 197202.000000
1 0.584055 0.242368 0.342576 117371.000000
accuracy 0.652917 0.652917 0.652917 0.652917
macro avg 0.624794 0.569818 0.553397 314573.000000
weighted avg 0.635132 0.652917 0.606899 314573.000000

Matriz de Confusão:

0 1
0 176943 20259
1 88924 28447
2025-09-29T01:01:59.338279 image/svg+xml Matplotlib v3.10.6, https://matplotlib.org/

import matplotlib.pyplot as plt
import pandas as pd

from io import StringIO
from sklearn import tree
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix


df = pd.read_csv('https://raw.githubusercontent.com/akemi-m/projetos-machine-learning/refs/heads/main/docs/projeto-1/covid_data.csv', low_memory=True)

d = df.copy()

fill_values = {
    'USMER': d['USMER'].mode()[0],
    'SEX': d['SEX'].mode()[0],
    'PATIENT_TYPE': d['PATIENT_TYPE'].mode()[0],
    'MEDICAL_UNIT': d['MEDICAL_UNIT'].mode()[0],
    'DATE_DIED': d['DATE_DIED'].mode()[0],
    'INTUBED': d['INTUBED'].mode()[0],
    'PNEUMONIA': d['PNEUMONIA'].mode()[0],
    'PREGNANT': d['PREGNANT'].mode()[0],
    'DIABETES': d['DIABETES'].mode()[0],
    'COPD': d['COPD'].mode()[0],
    'ASTHMA': d['ASTHMA'].mode()[0],
    'INMSUPR': d['INMSUPR'].mode()[0],
    'HIPERTENSION': d['HIPERTENSION'].mode()[0],
    'OTHER_DISEASE': d['OTHER_DISEASE'].mode()[0],
    'CARDIOVASCULAR': d['CARDIOVASCULAR'].mode()[0],
    'OBESITY': d['OBESITY'].mode()[0],
    'RENAL_CHRONIC': d['RENAL_CHRONIC'].mode()[0],
    'TOBACCO': d['TOBACCO'].mode()[0],
    'CLASIFFICATION_FINAL': d['CLASIFFICATION_FINAL'].mode()[0],
    'ICU': d['ICU'].mode()[0]
}

d.fillna(fill_values, inplace=True)

d['DIED_FLAG'] = (d['DATE_DIED'] != '9999-99-99').astype(int)
d['AGE'] = d['AGE'].fillna(d['AGE'].median())

age_min = d['AGE'].min()
age_max = d['AGE'].max()
d['AGE'] = (d['AGE'] - age_min) / (age_max - age_min)

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

# Carregar o conjunto de dados
x = d[['USMER', 
    'SEX', 
    'PATIENT_TYPE', 
    'MEDICAL_UNIT', 
    'DIED_FLAG', 
    'INTUBED', 
    'PNEUMONIA', 
    'AGE', 
    'PREGNANT', 
    'DIABETES', 
    'COPD', 
    'ASTHMA', 
    'INMSUPR', 
    'HIPERTENSION', 
    'OTHER_DISEASE', 
    'CARDIOVASCULAR', 
    'OBESITY', 
    'RENAL_CHRONIC', 
    'TOBACCO', 
    'ICU']]
y = (df['CLASIFFICATION_FINAL'] >= 4).map({False: 1, True: 0})

# Dividir os dados em conjuntos de treinamento e teste
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3, random_state=42)

# Criar e treinar o modelo de árvore de decisão
classifier = 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 modelo
accuracy = 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))

tree.plot_tree(classifier, max_depth=3, fontsize=15)

# Para imprimir na página HTML
buffer = StringIO()
plt.savefig(buffer, format="svg")
print(buffer.getvalue())

KNN

Accuracy: 0.5805

Relatório de Classificação:

precision recall f1-score support
0 0.423947 0.371454 0.395968 2221.0000
1 0.655649 0.703361 0.678667 3779.0000
accuracy 0.580500 0.580500 0.580500 0.5805
macro avg 0.539798 0.537407 0.537318 6000.0000
weighted avg 0.569880 0.580500 0.574021 6000.0000

Matriz de Confusão:

0 1
0 825 1396
1 1121 2658
2025-09-29T01:02:01.378260 image/svg+xml Matplotlib v3.10.6, https://matplotlib.org/

import pandas as pd
from io import StringIO
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix, ConfusionMatrixDisplay
from sklearn.decomposition import PCA

# ---- Configuração
SAMPLE_SIZE = 20000  # número máximo de linhas para análise/plot

# Carregar dados
df = pd.read_csv(
    'https://raw.githubusercontent.com/akemi-m/projetos-machine-learning/refs/heads/main/docs/projeto-1/covid_data.csv',
    low_memory=True
)
d = df.copy()

# Preencher valores nulos
fill_values = {col: d[col].mode()[0] for col in [
    'USMER', 'SEX', 'PATIENT_TYPE', 'MEDICAL_UNIT', 'DATE_DIED', 'INTUBED', 'PNEUMONIA',
    'PREGNANT', 'DIABETES', 'COPD', 'ASTHMA', 'INMSUPR', 'HIPERTENSION', 'OTHER_DISEASE',
    'CARDIOVASCULAR', 'OBESITY', 'RENAL_CHRONIC', 'TOBACCO', 'CLASIFFICATION_FINAL', 'ICU'
]}
d.fillna(fill_values, inplace=True)
d['AGE'] = d['AGE'].fillna(d['AGE'].median())

# Agrupar categorias raras
for col in ['MEDICAL_UNIT', 'PATIENT_TYPE']:
    counts = d[col].value_counts()
    rare_labels = counts[counts < 100].index
    d[col] = d[col].replace(rare_labels, 'OUTRO')

# Variável alvo
d['TARGET'] = (d['CLASIFFICATION_FINAL'] >= 4).astype(int)

# Variáveis binárias já como int
bin_cols = ['DATE_DIED','INTUBED','PNEUMONIA','PREGNANT','DIABETES','COPD','ASTHMA',
            'INMSUPR','HIPERTENSION','OTHER_DISEASE','CARDIOVASCULAR','OBESITY',
            'RENAL_CHRONIC','TOBACCO','ICU']
for col in bin_cols:
    d[col] = (d[col] != 0) & (d[col] != '9999-99-99')
    d[col] = d[col].astype(int)

d['DIED_FLAG'] = (d['DATE_DIED'] != '9999-99-99').astype(int)

# Features
num_features = ['AGE']
cat_features = ['USMER','SEX','PATIENT_TYPE','MEDICAL_UNIT','DIED_FLAG']
X = d[num_features + cat_features + bin_cols]
y = d['TARGET']

# ---- Amostragem para acelerar
if len(X) > SAMPLE_SIZE:
    X_sample = X.sample(SAMPLE_SIZE, random_state=42)
    y_sample = y.loc[X_sample.index]
else:
    X_sample = X
    y_sample = y

# Split treino/teste (na amostra)
X_train, X_test, y_train, y_test = train_test_split(
    X_sample, y_sample, test_size=0.3, random_state=42, stratify=y_sample
)

# Pipeline
preprocessor = ColumnTransformer([
    ('num', StandardScaler(), num_features),
    ('cat', OneHotEncoder(handle_unknown='ignore', sparse_output=False), cat_features)
])

pipeline = Pipeline([
    ('preprocessor', preprocessor),
    ('classifier', KNeighborsClassifier(n_neighbors=3, n_jobs=-1))
])

pipeline.fit(X_train, y_train)

# Previsão
y_pred = pipeline.predict(X_test)

# ---- Avaliação
print("Accuracy:", accuracy_score(y_test, y_pred))
print("<h3>Relatório de Classificação:</h3>")
report_df = pd.DataFrame(classification_report(y_test, y_pred, output_dict=True)).transpose()
print(report_df.to_html(classes="table table-bordered table-striped", border=0))

print("<h3>Matriz de Confusão:</h3>")
cm = confusion_matrix(y_test, y_pred)
cm_df = pd.DataFrame(cm, index=['0', '1'], columns=['0', '1'])
print(cm_df.to_html(classes="table table-bordered table-striped", border=0))

# Plot matriz de confusão
disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=["Classe 0","Classe 1"])
disp.plot(cmap=plt.cm.Blues)
plt.title("<h3>Matriz de Confusão - KNN</h3>")

# ---- PCA para visualização
X_test_transformed = pipeline.named_steps['preprocessor'].transform(X_test)
pca = PCA(n_components=2)
X_test_pca = pca.fit_transform(X_test_transformed)

plt.figure(figsize=(10, 8))
plt.scatter(X_test_pca[:, 0], X_test_pca[:, 1], c=y_pred, cmap='viridis', s=8)
plt.title("KNN Classificação (PCA 2D) - Amostra Teste")
plt.xlabel("Principal Component 1")
plt.ylabel("Principal Component 2")


# Para imprimir na página HTML
buffer = StringIO()
plt.savefig(buffer, format="svg")
print(buffer.getvalue())

K-Means

Accuracy: 0.5949

Relatório de Classificação:

precision recall f1-score support
0 0.455414 0.482978 0.468791 7402.0000
1 0.685021 0.660660 0.672620 12598.0000
accuracy 0.594900 0.594900 0.594900 0.5949
macro avg 0.570217 0.571819 0.570705 20000.0000
weighted avg 0.600043 0.594900 0.597183 20000.0000

Matriz de Confusão:

0 1
0 3575 3827
1 4275 8323
2025-09-29T01:02:03.207387 image/svg+xml Matplotlib v3.10.6, https://matplotlib.org/

import matplotlib.pyplot as plt
import pandas as pd
from io import StringIO
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.cluster import KMeans
from sklearn.decomposition import PCA 
from sklearn.metrics import confusion_matrix, accuracy_score, classification_report

# ---- Configuração
SAMPLE_SIZE = 20000  # número máximo de linhas para análise/plot

# Carregar dados
df = pd.read_csv(
    'https://raw.githubusercontent.com/akemi-m/projetos-machine-learning/refs/heads/main/docs/projeto-1/covid_data.csv',
    low_memory=True
)
d = df.copy()

# Preencher valores nulos
fill_values = {col: d[col].mode()[0] for col in [
    'USMER', 'SEX', 'PATIENT_TYPE', 'MEDICAL_UNIT', 'DATE_DIED', 'INTUBED', 'PNEUMONIA',
    'PREGNANT', 'DIABETES', 'COPD', 'ASTHMA', 'INMSUPR', 'HIPERTENSION', 'OTHER_DISEASE',
    'CARDIOVASCULAR', 'OBESITY', 'RENAL_CHRONIC', 'TOBACCO', 'CLASIFFICATION_FINAL', 'ICU'
]}
d.fillna(fill_values, inplace=True)
d['AGE'] = d['AGE'].fillna(d['AGE'].median())

# Agrupar categorias raras
for col in ['MEDICAL_UNIT', 'PATIENT_TYPE']:
    counts = d[col].value_counts()
    rare_labels = counts[counts < 100].index
    d[col] = d[col].replace(rare_labels, 'OUTRO')

# Variável alvo
d['TARGET'] = (d['CLASIFFICATION_FINAL'] >= 4).astype(int)

# Variáveis binárias já como int
bin_cols = ['DATE_DIED','INTUBED','PNEUMONIA','PREGNANT','DIABETES','COPD','ASTHMA',
            'INMSUPR','HIPERTENSION','OTHER_DISEASE','CARDIOVASCULAR','OBESITY',
            'RENAL_CHRONIC','TOBACCO','ICU']
for col in bin_cols:
    d[col] = (d[col] != 0) & (d[col] != '9999-99-99')
    d[col] = d[col].astype(int)

d['DIED_FLAG'] = (d['DATE_DIED'] != '9999-99-99').astype(int)

# Features e Target
num_features = ['AGE']
cat_features = ['USMER','SEX','PATIENT_TYPE','MEDICAL_UNIT','DIED_FLAG']
X = d[num_features + cat_features + bin_cols]
y = d['TARGET']

# ---- Amostragem para acelerar
if len(X) > SAMPLE_SIZE:
    X_sample = X.sample(SAMPLE_SIZE, random_state=42)
    y_sample = y.loc[X_sample.index]
else:
    X_sample = X
    y_sample = y

# Pré-processamento
preprocessor = ColumnTransformer([
    ('num', StandardScaler(), num_features),
    ('cat', OneHotEncoder(handle_unknown='ignore', sparse_output=False), cat_features)
])

# Pipeline com KMeans
pipeline = Pipeline([
    ('preprocessor', preprocessor),
    ('classifier', KMeans(n_clusters=2, init='k-means++', max_iter=100, random_state=42))
])

# Ajustar pipeline na amostra
pipeline.fit(X_sample)

# Predição para a amostra
y_pred_sample = pipeline.predict(X_sample)


print("Accuracy:", accuracy_score(y_sample, y_pred_sample))
print("<h3>Relatório de Classificação:</h3>")
report_df = pd.DataFrame(classification_report(y_sample, y_pred_sample, output_dict=True)).transpose()
print(report_df.to_html(classes="table table-bordered table-striped", border=0))

# ---- Matriz de confusão
print("<h3> Matriz de Confusão:</h3>")
cm = confusion_matrix(y_sample, y_pred_sample)
cm_df = pd.DataFrame(cm, index=['0', '1'], columns=['0', '1'])
print(cm_df.to_html(classes="table table-bordered table-striped", border=0))

# ---- PCA para visualização
X_sample_transformed = pipeline.named_steps['preprocessor'].transform(X_sample)
pca = PCA(n_components=2)
X_sample_pca = pca.fit_transform(X_sample_transformed)

# Centróides no espaço PCA
kmeans_step = pipeline.named_steps['classifier']
centroids = kmeans_step.cluster_centers_
centroids_pca = pca.transform(centroids)

# Plot rápido
plt.figure(figsize=(10, 8))
plt.scatter(X_sample_pca[:, 0], X_sample_pca[:, 1], c=y_pred_sample, cmap='viridis', s=8)
plt.scatter(centroids_pca[:, 0], centroids_pca[:, 1], 
           c='red', marker='*', s=200, label='Centroids')
plt.title('K-Means Clustering (PCA 2D) - Amostragem')
plt.xlabel('Principal Component 1')
plt.ylabel('Principal Component 2')

# Para imprimir na página HTML
buffer = StringIO()
plt.savefig(buffer, format="svg")
print(buffer.getvalue())

Avaliação

2025-09-29T01:02:06.192194 image/svg+xml Matplotlib v3.10.6, https://matplotlib.org/
Modelo Acurácia Precisão Recall F1-score
0 KNN 0.58050 0.655649 0.703361 0.678667
1 Árvore de Decisão 0.63200 0.666033 0.833818 0.740541
2 KMeans 0.40135 0.541048 0.326957 0.407600
import pandas as pd
from io import StringIO
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import roc_curve, auc

# ---- Configuração
SAMPLE_SIZE = 20000  # número máximo de linhas para análise/plot

# Carregar dados
df = pd.read_csv(
    'https://raw.githubusercontent.com/akemi-m/projetos-machine-learning/refs/heads/main/docs/projeto-1/covid_data.csv',
    low_memory=True
)
d = df.copy()

# Preencher valores nulos
fill_values = {col: d[col].mode()[0] for col in [
    'USMER', 'SEX', 'PATIENT_TYPE', 'MEDICAL_UNIT', 'DATE_DIED', 'INTUBED', 'PNEUMONIA',
    'PREGNANT', 'DIABETES', 'COPD', 'ASTHMA', 'INMSUPR', 'HIPERTENSION', 'OTHER_DISEASE',
    'CARDIOVASCULAR', 'OBESITY', 'RENAL_CHRONIC', 'TOBACCO', 'CLASIFFICATION_FINAL', 'ICU'
]}
d.fillna(fill_values, inplace=True)
d['AGE'] = d['AGE'].fillna(d['AGE'].median())

# Agrupar categorias raras
for col in ['MEDICAL_UNIT', 'PATIENT_TYPE']:
    counts = d[col].value_counts()
    rare_labels = counts[counts < 100].index
    d[col] = d[col].replace(rare_labels, 'OUTRO')

# Variável alvo
d['TARGET'] = (d['CLASIFFICATION_FINAL'] >= 4).astype(int)

# Variáveis binárias já como int
bin_cols = ['DATE_DIED','INTUBED','PNEUMONIA','PREGNANT','DIABETES','COPD','ASTHMA',
            'INMSUPR','HIPERTENSION','OTHER_DISEASE','CARDIOVASCULAR','OBESITY',
            'RENAL_CHRONIC','TOBACCO','ICU']
for col in bin_cols:
    d[col] = (d[col] != 0) & (d[col] != '9999-99-99')
    d[col] = d[col].astype(int)

d['DIED_FLAG'] = (d['DATE_DIED'] != '9999-99-99').astype(int)

# Features
num_features = ['AGE']
cat_features = ['USMER','SEX','PATIENT_TYPE','MEDICAL_UNIT','DIED_FLAG']
X = d[num_features + cat_features + bin_cols]
y = d['TARGET']

# ---- Amostragem para acelerar
if len(X) > SAMPLE_SIZE:
    X_sample = X.sample(SAMPLE_SIZE, random_state=42)
    y_sample = y.loc[X_sample.index]
else:
    X_sample = X
    y_sample = y

# Split treino/teste
X_train, X_test, y_train, y_test = train_test_split(
    X_sample, y_sample, test_size=0.3, random_state=42, stratify=y_sample
)

# ---- Modelo KNN (com pipeline de preprocessamento)
preprocessor = ColumnTransformer([
    ('num', StandardScaler(), num_features),
    ('cat', OneHotEncoder(handle_unknown='ignore', sparse_output=False), cat_features)
])

knn_pipeline = Pipeline([
    ('preprocessor', preprocessor),
    ('classifier', KNeighborsClassifier(n_neighbors=3, n_jobs=-1))
])

knn_pipeline.fit(X_train, y_train)
y_proba_knn = knn_pipeline.predict_proba(X_test)[:, 1]

# ---- Modelo Árvore de Decisão
tree_clf = DecisionTreeClassifier(random_state=42)
tree_clf.fit(X_train, y_train)
y_proba_tree = tree_clf.predict_proba(X_test)[:, 1]

# ---- Curvas ROC
fpr_knn, tpr_knn, _ = roc_curve(y_test, y_proba_knn)
roc_auc_knn = auc(fpr_knn, tpr_knn)

fpr_tree, tpr_tree, _ = roc_curve(y_test, y_proba_tree)
roc_auc_tree = auc(fpr_tree, tpr_tree)

# ---- Plot Comparativo
plt.figure(figsize=(8, 6))
plt.plot(fpr_knn, tpr_knn, label=f'KNN (AUC = {roc_auc_knn:.2f})', lw=2)
plt.plot(fpr_tree, tpr_tree, label=f'Árvore de Decisão (AUC = {roc_auc_tree:.2f})', lw=2, color='green')
plt.plot([0, 1], [0, 1], color='gray', linestyle='--')

plt.xlabel('Taxa de Falsos Positivos (FPR)')
plt.ylabel('Taxa de Verdadeiros Positivos (TPR)')
plt.title('Curva ROC - Comparação KNN vs Árvore de Decisão')
plt.legend(loc="lower right")
plt.grid(True)

# Para imprimir na página HTML
buffer = StringIO()
plt.savefig(buffer, format="svg")
print(buffer.getvalue())
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.cluster import KMeans
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score

# ---- Configuração
SAMPLE_SIZE = 20000  # número máximo de linhas para análise/plot

# Carregar dados
df = pd.read_csv(
    'https://raw.githubusercontent.com/akemi-m/projetos-machine-learning/refs/heads/main/docs/projeto-1/covid_data.csv',
    low_memory=True
)
d = df.copy()

# Preencher valores nulos
fill_values = {col: d[col].mode()[0] for col in [
    'USMER', 'SEX', 'PATIENT_TYPE', 'MEDICAL_UNIT', 'DATE_DIED', 'INTUBED', 'PNEUMONIA',
    'PREGNANT', 'DIABETES', 'COPD', 'ASTHMA', 'INMSUPR', 'HIPERTENSION', 'OTHER_DISEASE',
    'CARDIOVASCULAR', 'OBESITY', 'RENAL_CHRONIC', 'TOBACCO', 'CLASIFFICATION_FINAL', 'ICU'
]}
d.fillna(fill_values, inplace=True)
d['AGE'] = d['AGE'].fillna(d['AGE'].median())

# Agrupar categorias raras
for col in ['MEDICAL_UNIT', 'PATIENT_TYPE']:
    counts = d[col].value_counts()
    rare_labels = counts[counts < 100].index
    d[col] = d[col].replace(rare_labels, 'OUTRO')

# Variável alvo
d['TARGET'] = (d['CLASIFFICATION_FINAL'] >= 4).astype(int)

# Variáveis binárias como int
bin_cols = ['DATE_DIED','INTUBED','PNEUMONIA','PREGNANT','DIABETES','COPD','ASTHMA',
            'INMSUPR','HIPERTENSION','OTHER_DISEASE','CARDIOVASCULAR','OBESITY',
            'RENAL_CHRONIC','TOBACCO','ICU']
for col in bin_cols:
    d[col] = (d[col] != 0) & (d[col] != '9999-99-99')
    d[col] = d[col].astype(int)

d['DIED_FLAG'] = (d['DATE_DIED'] != '9999-99-99').astype(int)

# Features
num_features = ['AGE']
cat_features = ['USMER','SEX','PATIENT_TYPE','MEDICAL_UNIT','DIED_FLAG']
X = d[num_features + cat_features + bin_cols]
y = d['TARGET']

# ---- Amostragem
if len(X) > SAMPLE_SIZE:
    X_sample = X.sample(SAMPLE_SIZE, random_state=42)
    y_sample = y.loc[X_sample.index]
else:
    X_sample = X
    y_sample = y

# Split treino/teste
X_train, X_test, y_train, y_test = train_test_split(
    X_sample, y_sample, test_size=0.3, random_state=42, stratify=y_sample
)

# Preprocessamento comum
preprocessor = ColumnTransformer([
    ('num', StandardScaler(), num_features),
    ('cat', OneHotEncoder(handle_unknown='ignore', sparse_output=False), cat_features)
])

# ---- Modelos
models = {
    "KNN": Pipeline([('preprocessor', preprocessor),
                     ('classifier', KNeighborsClassifier(n_neighbors=3, n_jobs=-1))]),

    "Árvore de Decisão": Pipeline([('preprocessor', preprocessor),
                                   ('classifier', DecisionTreeClassifier(random_state=42))]),

    "KMeans": Pipeline([('preprocessor', preprocessor),
                        ('classifier', KMeans(n_clusters=2, random_state=42, n_init=10))])
}

# ---- Resultados
results = []

for name, model in models.items():
    if name == "KMeans":
        model.fit(X_sample)  # não supervisionado
        y_pred = model.predict(X_sample)
        y_true = y_sample
    else:
        model.fit(X_train, y_train)
        y_pred = model.predict(X_test)
        y_true = y_test

    acc = accuracy_score(y_true, y_pred)
    prec = precision_score(y_true, y_pred, zero_division=0)
    rec = recall_score(y_true, y_pred, zero_division=0)
    f1 = f1_score(y_true, y_pred, zero_division=0)

    results.append({"Modelo": name, "Acurácia": acc, "Precisão": prec,
                    "Recall": rec, "F1-score": f1})

# ---- Tabela comparativa
results_df = pd.DataFrame(results)
print(results_df.to_html(classes="table table-bordered table-striped", border=0))

Os três modelos apresentam desempenhos distintos, refletindo suas naturezas e limitações. O KNN obteve acurácia de 58,1%, com precisão de 65,8% e recall de 69,8%, resultando em um F1-score de 67,7%. Isso indica que, apesar de uma acurácia relativamente baixa, o modelo conseguiu equilibrar bem precisão e recall, mostrando-se razoável na detecção dos positivos. Já a Árvore de Decisão apresentou desempenho superior, com acurácia de 63,2% e F1-score de 74,0%, destacando-se principalmente pelo recall de 83,4%, ou seja, foi o modelo que mais conseguiu identificar corretamente os casos positivos, ainda que com precisão um pouco menor que a do KNN. Em termos de discriminação global, os valores de AUC foram baixos (0,55 para KNN e 0,57 para Árvore), mostrando que ambos os classificadores têm poder limitado em separar classes.

Por outro lado, o KMeans, como esperado por ser um modelo não supervisionado, apresentou desempenho bem inferior (acurácia de 40,1% e F1-score de apenas 40,7%), evidenciando a dificuldade em alinhar automaticamente os clusters com as classes reais do problema. Sua precisão de 54,1% mostra que, quando acerta, tem alguma consistência, mas o recall baixo (32,7%) reforça que muitos casos positivos foram ignorados.

Em resumo, a Árvore de Decisão foi o modelo mais eficiente dentro do contexto supervisionado, com melhor equilíbrio entre métricas e maior recall, o que é desejável em cenários onde a detecção de casos positivos é crítica. O KNN, apesar de competitivo, foi menos robusto, e o KMeans demonstrou a limitação natural de modelos de clustering quando comparados diretamente a classificadores supervisionados.