K-Means - Doenças Cardiovasculares
O Pré-Processamento já foi feito e pode ser encontrado em: Árvore de Decisão
Objetivo
Aplicar o algoritmo de K-Means ao dataset de doenças cardíacas para verificar se é possível identificar grupos de pacientes com perfis semelhantes, sem utilizar a variável alvo (HeartDisease). O objetivo aqui não é prever, mas descobrir padrões ocultos (aprendizado não supervisionado).
Metodologia
K-Means configurado com: - n_clusters=3 (três grupo) - init="k-means++" (boa inicialização dos centróides) - max_iter=300 (número máximo de iterações)
Como os dados têm múltiplas dimensões, aplicou-se PCA (Análise de Componentes Principais) para reduzir para 2 dimensões (PC1 e PC2), permitindo representar os clusters graficamente.
Resultados Obtidos
Centroides finais (em espaço normalizado): - Foram obtidos 3 centróides representando os "perfis médios" de cada grupo de pacientes. (valores mostrados no código, em escala normalizada entre 0 e 1).
Inércia (WCSS - Within-Cluster Sum of Squares): - Valor: 712.73 - Esse número representa a soma das distâncias quadráticas dos pontos em relação ao seu centróide. Quanto menor, mais compactos e coesos são os clusters.
Visualização dos Clusters (via PCA): - O gráfico mostra os pacientes distribuídos em três grupos. - Cada cor representa um cluster. - Os pontos vermelhos em formato de estrela representam os centróides (pontos médios dos clusters). - Observa-se que os grupos são relativamente bem definidos, mas ainda com algumas sobreposições.
import numpy as np
import matplotlib.pyplot as plt
from io import StringIO
from sklearn.cluster import KMeans
from sklearn.decomposition import PCA
import pandas as pd
# pre processamento
def preprocess(df):
df.fillna(df.median(numeric_only=True), inplace=True)
df["Sex"] = df["Sex"].map({"M": 1, "F": 0})
df["ChestPainType"] = df["ChestPainType"].map({"TA": 0, "ATA": 1, "NAP": 2, "ASY": 3})
df["RestingECG"] = df["RestingECG"].map({"Normal": 0, "ST": 1, "LVH": 2})
df["ExerciseAngina"] = df["ExerciseAngina"].map({"Y": 1, "N": 0})
df["ST_Slope"] = df["ST_Slope"].map({"Up": 0, "Flat": 1, "Down": 2})
return df
# carregar dataset
df = pd.read_csv("https://raw.githubusercontent.com/bligui/Machine-Learning-Ana/refs/heads/main/dados/heart.csv")
df = preprocess(df)
numeric_cols = df.select_dtypes(include=[np.number]).columns
# Normalização Min-Max
df_minmax = df.copy()
df_minmax[numeric_cols] = (df_minmax[numeric_cols] - df_minmax[numeric_cols].min()) / (
df_minmax[numeric_cols].max() - df_minmax[numeric_cols].min()
)
X = df_minmax[numeric_cols].values
# k-means
kmeans = KMeans(n_clusters=3, init='k-means++', max_iter=300, random_state=42)
labels = kmeans.fit_predict(X)
# visualizacao
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X)
centers_pca = pca.transform(kmeans.cluster_centers_)
plt.figure(figsize=(12, 10))
plt.scatter(X_pca[:, 0], X_pca[:, 1], c=labels, cmap="viridis", s=60, alpha=0.7, edgecolor="k")
plt.scatter(centers_pca[:, 0], centers_pca[:, 1], c="red", marker="*", s=250, label="Centroids")
plt.title("K-Means Clustering (Heart Dataset) - PCA 2D Projection")
plt.xlabel("PC1")
plt.ylabel("PC2")
plt.legend()
# Print centroids and inertia
# print("Final centroids:", kmeans.cluster_centers_)
# print("Inertia (WCSS):", kmeans.inertia_)
buffer = StringIO()
plt.savefig(buffer, format="svg", transparent=True)
print(buffer.getvalue())
Interpretação
-
O algoritmo conseguiu separar a população em três grupos principais, possivelmente relacionados a perfis de risco cardiovascular diferentes.
-
Como a variável HeartDisease não foi usada, o modelo não sabe quem tem ou não a doença, mas pode ser interessante comparar depois os clusters com a variável alvo para verificar se existe relação entre eles e a presença da doença.
-
O PCA reduziu a dimensionalidade para 2 componentes, o que ajuda na visualização, mas simplifica a informação original. Mesmo assim, os clusters mostraram certa estrutura.
Conclusão
O algoritmo K-Means (k=3) conseguiu identificar três grupos distintos de pacientes, mesmo sem utilizar a variável alvo. A inércia encontrada (712,73) indica um nível razoável de coesão interna. Esse tipo de análise pode ser útil para segmentar pacientes em grupos de risco e auxiliar em análises médicas exploratórias.