precision recall f1-score support 0 0.783 0.837 0.809 43 1 0.562 0.474 0.514 19 accuracy 0.726 62 macro avg 0.673 0.655 0.662 62 weighted avg 0.715 0.726 0.719 62
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from io import StringIO
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
from sklearn.decomposition import PCA
# ===== 1) Carrega o CSV =====
df = pd.read_csv('https://raw.githubusercontent.com/marcelademartini/Machine-Learning-1/refs/heads/main/Testing.csv')
# Define a coluna alvo
target = 'Outcome' if 'Outcome' in df.columns else df.columns[-1]
# X e y (dummies para categóricas)
X_raw = df.drop(columns=[target])
X = pd.get_dummies(X_raw, drop_first=True)
y = df[target]
# Codifica alvo não numérico
if not np.issubdtype(y.dtype, np.number):
y = pd.factorize(y)[0]
# Trata NaN
X = X.fillna(X.median(numeric_only=True))
# ===== 2) Split + escala =====
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y if len(np.unique(y)) > 1 else None
)
scaler = StandardScaler()
X_train_s = scaler.fit_transform(X_train)
X_test_s = scaler.transform(X_test)
# ===== 3) Treina KNN =====
k = 3
knn = KNeighborsClassifier(n_neighbors=k)
knn.fit(X_train_s, y_train)
y_pred = knn.predict(X_test_s)
# ===== 4) Métricas =====
acc = accuracy_score(y_test, y_pred)
print(classification_report(y_test, y_pred, digits=3))
# ===== Helper: imprimir figura como SVG =====
def print_svg_current_fig():
buf = StringIO()
plt.savefig(buf, format="svg", transparent=True, bbox_inches="tight")
print(buf.getvalue())
plt.close()
# ===== 5) Matriz de confusão =====
cm = confusion_matrix(y_test, y_pred)
plt.figure(figsize=(5,4), dpi=120)
plt.imshow(cm, interpolation='nearest')
plt.title("Matriz de Confusão (teste)")
plt.xlabel("Predito")
plt.ylabel("Real")
for i in range(cm.shape[0]):
for j in range(cm.shape[1]):
plt.text(j, i, str(cm[i, j]), ha="center", va="center")
plt.colorbar()
print_svg_current_fig()
# ===== 6) Visualização 2D (PCA) da fronteira de decisão =====
if X_train.shape[1] >= 2:
pca = PCA(n_components=2, random_state=42)
X_train_2d = pca.fit_transform(X_train_s)
X_test_2d = pca.transform(X_test_s)
knn_viz = KNeighborsClassifier(n_neighbors=k).fit(X_train_2d, y_train)
h = 0.05
x_min, x_max = X_train_2d[:, 0].min() - 0.5, X_train_2d[:, 0].max() + 0.5
y_min, y_max = X_train_2d[:, 1].min() - 0.5, X_train_2d[:, 1].max() + 0.5
xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
Z = knn_viz.predict(np.c_[xx.ravel(), yy.ravel()]).reshape(xx.shape)
plt.figure(figsize=(6,5), dpi=120)
plt.contourf(xx, yy, Z, alpha=0.30)
plt.scatter(X_train_2d[:,0], X_train_2d[:,1], c=y_train, s=20, marker='o', label='treino')
plt.scatter(X_test_2d[:,0], X_test_2d[:,1], c=y_test, s=40, marker='x', label='teste')
plt.title(f"Fronteira de Decisão (PCA 2D) — KNN k={k}")
plt.xlabel("PC1")
plt.ylabel("PC2")
plt.legend(loc="best")
print_svg_current_fig()
Matriz de Confusão (bruta) — y_true x cluster: [[111 17 87] [ 11 39 43] [ 0 0 0]] Matriz de Confusão (clusters remapeados → classes): [[198 17] [ 54 39]] Melhor permutação cluster->classe encontrada: {0: np.int64(0), 1: np.int64(1)}
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from io import StringIO
from itertools import permutations
from sklearn.cluster import KMeans
from sklearn.metrics import confusion_matrix
# ===== Helper: imprimir figura como SVG =====
def print_svg_current_fig():
buf = StringIO()
plt.savefig(buf, format="svg", transparent=True, bbox_inches="tight")
print(buf.getvalue())
plt.close()
# ===== 1) Carrega o CSV =====
df = pd.read_csv('https://raw.githubusercontent.com/marcelademartini/Machine-Learning-1/refs/heads/main/Testing.csv')
# Garante alvo verdadeiro (para comparar com clusters)
if 'Outcome' not in df.columns:
raise ValueError("Não há coluna 'Outcome' no CSV — sem rótulos reais não dá para fazer matriz de confusão.")
y_true = df['Outcome'].to_numpy()
# ===== 2) Seleciona features numéricas =====
X_num = df.select_dtypes(include=[np.number]).dropna()
if X_num.shape[1] >= 2:
X = X_num.iloc[:, :2].to_numpy()
else:
col = X_num.iloc[:, 0].to_numpy().reshape(-1, 1)
X = np.hstack([col, col])
# ===== 3) Roda K-Means =====
k = 3
kmeans = KMeans(n_clusters=k, init='k-means++', max_iter=100, random_state=42)
labels = kmeans.fit_predict(X)
# ===== 4) MATRIZ DE CONFUSÃO (BRUTA): classes reais x clusters =====
cm_raw = confusion_matrix(y_true, labels)
print("Matriz de Confusão (bruta) — y_true x cluster:\n", cm_raw)
plt.figure(figsize=(5,4), dpi=120)
plt.imshow(cm_raw, interpolation='nearest')
plt.title("Matriz de Confusão (bruta)\n(Classes reais × Clusters)")
plt.xlabel("Cluster (predito pelo K-Means)")
plt.ylabel("Classe real (Outcome)")
for i in range(cm_raw.shape[0]):
for j in range(cm_raw.shape[1]):
plt.text(j, i, str(cm_raw[i, j]), ha="center", va="center")
plt.colorbar()
print_svg_current_fig()
# ===== 5)REMAPEAMENTO DOS CLUSTERS PARA CLASSES =====
# Tenta encontrar a melhor permutação de mapeamento cluster->classe que maximize acertos
classes = np.unique(y_true)
n_classes = len(classes)
n_clusters = k
# Se número de clusters >= número de classes
best_perm = None
best_hits = -1
for perm in permutations(range(n_clusters), n_classes):
# constrói predição remapeando apenas os clusters usados (demais ficam como a primeira classe)
map_dict = {cluster_idx: classes[i] for i, cluster_idx in enumerate(perm)}
y_mapped = np.array([map_dict.get(c, classes[0]) for c in labels])
hits = (y_mapped == y_true).sum()
if hits > best_hits:
best_hits = hits
best_perm = perm
# Aplica melhor mapeamento encontrado
map_dict = {cluster_idx: classes[i] for i, cluster_idx in enumerate(best_perm)}
y_pred_mapped = np.array([map_dict.get(c, classes[0]) for c in labels])
cm_mapped = confusion_matrix(y_true, y_pred_mapped)
print("\nMatriz de Confusão (clusters remapeados → classes):\n", cm_mapped)
print("Melhor permutação cluster->classe encontrada:", map_dict)
plt.figure(figsize=(5,4), dpi=120)
plt.imshow(cm_mapped, interpolation='nearest')
plt.title("Matriz de Confusão (clusters remapeados)")
plt.xlabel("Predito (após remapeamento)")
plt.ylabel("Classe real (Outcome)")
for i in range(cm_mapped.shape[0]):
for j in range(cm_mapped.shape[1]):
plt.text(j, i, str(cm_mapped[i, j]), ha="center", va="center")
plt.colorbar()
print_svg_current_fig()
# ===== 6) Plot do clustering em 2D =====
plt.figure(figsize=(6,5), dpi=120)
plt.scatter(X[:, 0], X[:, 1], c=labels, s=40, alpha=0.8, label='pontos', cmap='viridis')
plt.scatter(kmeans.cluster_centers_[:, 0], kmeans.cluster_centers_[:, 1],
c='red', marker='*', s=200, label='centroides')
plt.title('K-Means Clustering Results')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.legend(loc='best')
print_svg_current_fig()
1) KNN: passo a passo do meu código
1.1. Importação e leitura do CSV
-
Eu importo as bibliotecas do Python que preciso e leio o arquivo Testing.csv. Se existir a coluna Outcome, uso como alvo. Caso contrário, pego a última coluna como alvo.
-
Por que isso? Eu preciso de uma variável de saída para transformar o problema em classificação supervisionada.
1.2. Seleção de variáveis e tratamento de tipos
-
Eu separo X (todas as colunas menos o alvo) e y (o alvo). Se houver colunas categóricas em X, transformo em dummies com get_dummies. Se y não for numérica, fatorizo para virar números inteiros.
-
Por que isso? Modelos como KNN trabalham com números. Então eu deixo tudo numérico e consistente.
1.3. Tratamento de ausentes
-
Eu preencho valores ausentes em X com a mediana das colunas numéricas.
-
Por que isso? Evito perder linhas e mantenho a escala robusta a outliers.
1.4. Split treino e teste com estratificação
-
Eu divido os dados em treino e teste usando train_test_split com test_size=0.2 e random_state=42. Se houver mais de uma classe, uso stratify=y para manter as proporções das classes nos dois conjuntos.
-
Por que isso? Preciso medir desempenho em dados que o modelo não viu. A estratificação evita desbalancear as classes no split.
1.5. Padronização
-
Eu padronizo X_train e X_test com StandardScaler (média 0 e desvio 1).
-
Por que isso? KNN usa distâncias. Se as escalas forem diferentes, uma variável pode dominar a distância. Padronizar deixa tudo comparável.
1.6. Treino do KNN
-
Eu escolho k = 3, crio KNeighborsClassifier(n_neighbors=3), ajusto com fit e gero previsões no teste com predict.
-
Por que isso? KNN classifica um ponto olhando os vizinhos mais próximos. k=3 é um começo simples para experimentar.
1.7. Métricas e relatório
-
Eu calculo a acurácia e imprimo classification_report, que traz precisão, recall e F1 por classe, além das médias.
-
Como ler?
-
Precisão: dos positivos que eu previ, quantos eram realmente positivos
-
Recall: dos positivos reais, quantos eu acertei
-
F1: balanço entre precisão e recall
1.8. Matriz de confusão com figura em SVG
-
Eu calculo cm = confusion_matrix(y_test, y_pred), ploto com imshow, escrevo os números em cada célula e exporto a figura como SVG usando a função print_svg_current_fig().
-
Por que isso? A matriz de confusão mostra onde o modelo acerta e onde se confunde entre as classes. Exportar em SVG mantém a imagem nítida no navegador e no GitPages.
1.9. Visualização 2D com PCA e fronteira de decisão
-
Se houver pelo menos duas features, eu rodo um PCA para reduzir para 2 componentes e treino um KNN nesse espaço 2D apenas para visualização. Depois eu desenho a fronteira de decisão com contourf, marco os pontos de treino e teste, e exporto em SVG.
-
Por que isso? Ver a fronteira de decisão ajuda a entender como o KNN está separando as classes depois da redução de dimensionalidade. É apenas ilustrativo.
2) K-Means: matriz de confusão usando a mesma lógica visual
- O K-Means é não supervisionado, então ele cria clusters sem saber a classe real. Mesmo assim, se o meu dataset tem uma coluna alvo (por exemplo, Outcome), eu posso comparar os clusters com essa coluna para inspecionar o alinhamento entre grupos encontrados e as classes reais.
2.1. O que eu faço antes de treinar
-
Carrego o Testing.csv
-
Confirmo que Outcome existe para poder comparar
-
Seleciono apenas colunas numéricas
-
Para visualização, uso as duas primeiras colunas numéricas. Se só tiver uma, eu duplico para formar um plano 2D
2.2. Treino do K-Means
-
Eu rodo KMeans(n_clusters=3, init='k-means++', max_iter=100, random_state=42) e pego os rótulos de cluster com fit_predict.
-
Por que 3 clusters? É um valor inicial para observar o comportamento. Posso variar para testar.
2.3. Matriz de confusão bruta: classes reais × clusters
-
Eu calculo cm_raw = confusion_matrix(y_true, labels) e ploto do mesmo jeito que no KNN: imshow, números nas células e exportação SVG pela mesma função.
-
Importante: esta matriz é bruta. Cluster 0 não quer dizer classe 0, porque os rótulos de cluster são arbitrários.
2.4. Remapeamento de clusters para classes
-
Para tornar a leitura mais parecida com classificação, eu tento mapear os clusters para as classes reais. Eu testo todas as permutações possíveis de cluster para classe e escolho a que dá mais acertos. Com esse mapeamento, eu gero uma segunda matriz de confusão, agora “alinhada”.
-
Por que isso ajuda? Ajuda a ler a matriz como se fosse uma predição de classe. Ainda é um modelo não supervisionado, mas o remapeamento torna a comparação mais clara.
2.5. Visualização 2D dos clusters
-
Eu desenho um scatter plot dos pontos coloridos pelos rótulos de cluster e marco os centróides com estrelas. Exporto em SVG.
-
Por que isso? Ver a distribuição no plano 2D ajuda a entender como os clusters ficaram.
3) Observações e limitações
-
No KNN, a matriz de confusão é direta porque existem classes reais e previsões.
-
No K-Means, a matriz de confusão precisa ser interpretada com cuidado, porque os rótulos de cluster são arbitrários. O remapeamento é uma etapa extra que melhora a comparação, mas não transforma o K-Means em um classificador supervisionado.
-
Padronizar dados é essencial para KNN e geralmente ajuda bastante em métodos baseados em distância.
-
No K-Means, escolher o número de clusters é uma decisão importante. Vale testar valores diferentes e usar métricas de clusterização como silhouette e Davies Bouldin.
4) Como reproduzir
-
Coloque Testing.csv na mesma pasta dos notebooks ou scripts.
-
Para o KNN, rode o script principal. Ele já:
-
prepara dados
-
treina o modelo
-
imprime relatório de classificação
-
gera e imprime a matriz de confusão
-
exporta as figuras como SVG
-
Para o K-Means, rode o script de clustering. Ele:
-
gera a matriz de confusão bruta
-
remapeia clusters para classes e gera a matriz alinhada
-
cria o scatter com centróides
-
exporta tudo como SVG
-
Se eu quiser publicar no GitPages, os SVGs ficam nítidos e leves, então as figuras carregam rápido e com boa qualidade.
5) Ideias de extensão
-
Ajustar k no KNN e comparar as métricas
-
Usar validação cruzada para avaliar estabilidade
-
Testar diferentes números de clusters e calcular silhouette
-
Usar mais componentes no PCA e só reduzir para 2D na hora da visualização
-
Comparar K-Means com outros métodos de clusterização como DBSCAN e GMM
Comparação entre KNN e K-Means
1) Resultados do KNN
- Matriz de confusão
- 36 verdadeiros negativos
- 7 falsos positivos
- 10 falsos negativos
-
9 verdadeiros positivos
-
Fronteira de decisão (PCA 2D)
- As regiões de decisão mostram bastante sobreposição.
- Isso explica a acurácia de aproximadamente 72%.
- O modelo acerta mais a classe 0, mas erra bastante na classe 1.
2) Resultados do K-Means
- Matriz de confusão bruta
- Clusters não correspondem diretamente às classes.
-
Há mistura significativa entre classe 0 e 1.
-
Matriz de confusão remapeada
-
O alinhamento melhora os resultados, mas ainda há muitos falsos negativos (classe 1 confundida).
-
Visualização dos clusters
- Centrôides bem definidos.
- Porém, classes diferentes caem nos mesmos clusters.
3) Comparação Final
- KNN
- Supervisionado (usa rótulos).
- Acurácia ≈ 72%.
-
Generaliza melhor e distingue padrões conhecidos.
-
K-Means
- Não supervisionado (não usa rótulos).
- Clusters não refletem perfeitamente as classes reais.
- Mesmo remapeado, tem desempenho inferior.
4) Conclusão
- O KNN é o melhor modelo para este dataset.
- O K-Means é útil para explorar agrupamentos, mas não substitui um classificador supervisionado quando os rótulos estão disponíveis.