Exploração de Dados
Descrição da base de dados e código de exploração
O câncer de mama é o tipo de câncer mais comum entre mulheres em todo o mundo, responsável por aproximadamente 25% de todos os casos e afetando milhões de pessoas todos os anos. Ele se desenvolve quando células da mama começam a crescer de forma descontrolada, formando tumores que podem ser identificados por exames de imagem (raios-X) ou detectados como nódulos.
O principal desafio no diagnóstico é diferenciar corretamente os tumores malignos (cancerosos) dos benignos (não cancerosos). O objetivo deste projeto é desenvolver um modelo de classificação supervisionada capaz de prever, com base em atributos numéricos das células, se um tumor é maligno ou benigno, e estabelecer um diagnóstico confiável.
Sobre o Dataset
Total de registros: 569 amostras
Variável alvo: diagnosis (M = maligno, B = benigno)
Número de variáveis preditoras: 30 atributos numéricos relacionados ao tamanho, textura, formato e concavidade das células.
| id | diagnosis | radius_mean | texture_mean | perimeter_mean | area_mean | smoothness_mean | compactness_mean | concavity_mean | concave points_mean | symmetry_mean | fractal_dimension_mean | radius_se | texture_se | perimeter_se | area_se | smoothness_se | compactness_se | concavity_se | concave points_se | symmetry_se | fractal_dimension_se | radius_worst | texture_worst | perimeter_worst | area_worst | smoothness_worst | compactness_worst | concavity_worst | concave points_worst | symmetry_worst | fractal_dimension_worst |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 87930 | B | 12.47 | 18.6 | 81.09 | 481.9 | 0.09965 | 0.1058 | 0.08005 | 0.03821 | 0.1925 | 0.06373 | 0.3961 | 1.044 | 2.497 | 30.29 | 0.006953 | 0.01911 | 0.02701 | 0.01037 | 0.01782 | 0.003586 | 14.97 | 24.64 | 96.05 | 677.9 | 0.1426 | 0.2378 | 0.2671 | 0.1015 | 0.3014 | 0.0875 |
| 859575 | M | 18.94 | 21.31 | 123.6 | 1130 | 0.09009 | 0.1029 | 0.108 | 0.07951 | 0.1582 | 0.05461 | 0.7888 | 0.7975 | 5.486 | 96.05 | 0.004444 | 0.01652 | 0.02269 | 0.0137 | 0.01386 | 0.001698 | 24.86 | 26.58 | 165.9 | 1866 | 0.1193 | 0.2336 | 0.2687 | 0.1789 | 0.2551 | 0.06589 |
| 8670 | M | 15.46 | 19.48 | 101.7 | 748.9 | 0.1092 | 0.1223 | 0.1466 | 0.08087 | 0.1931 | 0.05796 | 0.4743 | 0.7859 | 3.094 | 48.31 | 0.00624 | 0.01484 | 0.02813 | 0.01093 | 0.01397 | 0.002461 | 19.26 | 26 | 124.9 | 1156 | 0.1546 | 0.2394 | 0.3791 | 0.1514 | 0.2837 | 0.08019 |
| 907915 | B | 12.4 | 17.68 | 81.47 | 467.8 | 0.1054 | 0.1316 | 0.07741 | 0.02799 | 0.1811 | 0.07102 | 0.1767 | 1.46 | 2.204 | 15.43 | 0.01 | 0.03295 | 0.04861 | 0.01167 | 0.02187 | 0.006005 | 12.88 | 22.91 | 89.61 | 515.8 | 0.145 | 0.2629 | 0.2403 | 0.0737 | 0.2556 | 0.09359 |
| 921385 | B | 11.54 | 14.44 | 74.65 | 402.9 | 0.09984 | 0.112 | 0.06737 | 0.02594 | 0.1818 | 0.06782 | 0.2784 | 1.768 | 1.628 | 20.86 | 0.01215 | 0.04112 | 0.05553 | 0.01494 | 0.0184 | 0.005512 | 12.26 | 19.68 | 78.78 | 457.8 | 0.1345 | 0.2118 | 0.1797 | 0.06918 | 0.2329 | 0.08134 |
| 927241 | M | 20.6 | 29.33 | 140.1 | 1265 | 0.1178 | 0.277 | 0.3514 | 0.152 | 0.2397 | 0.07016 | 0.726 | 1.595 | 5.772 | 86.22 | 0.006522 | 0.06158 | 0.07117 | 0.01664 | 0.02324 | 0.006185 | 25.74 | 39.42 | 184.6 | 1821 | 0.165 | 0.8681 | 0.9387 | 0.265 | 0.4087 | 0.124 |
| 9012000 | M | 22.01 | 21.9 | 147.2 | 1482 | 0.1063 | 0.1954 | 0.2448 | 0.1501 | 0.1824 | 0.0614 | 1.008 | 0.6999 | 7.561 | 130.2 | 0.003978 | 0.02821 | 0.03576 | 0.01471 | 0.01518 | 0.003796 | 27.66 | 25.8 | 195 | 2227 | 0.1294 | 0.3885 | 0.4756 | 0.2432 | 0.2741 | 0.08574 |
| 853201 | M | 17.57 | 15.05 | 115 | 955.1 | 0.09847 | 0.1157 | 0.09875 | 0.07953 | 0.1739 | 0.06149 | 0.6003 | 0.8225 | 4.655 | 61.1 | 0.005627 | 0.03033 | 0.03407 | 0.01354 | 0.01925 | 0.003742 | 20.01 | 19.52 | 134.9 | 1227 | 0.1255 | 0.2812 | 0.2489 | 0.1456 | 0.2756 | 0.07919 |
| 8611161 | B | 13.34 | 15.86 | 86.49 | 520 | 0.1078 | 0.1535 | 0.1169 | 0.06987 | 0.1942 | 0.06902 | 0.286 | 1.016 | 1.535 | 12.96 | 0.006794 | 0.03575 | 0.0398 | 0.01383 | 0.02134 | 0.004603 | 15.53 | 23.19 | 96.66 | 614.9 | 0.1536 | 0.4791 | 0.4858 | 0.1708 | 0.3527 | 0.1016 |
| 911673 | B | 13.9 | 16.62 | 88.97 | 599.4 | 0.06828 | 0.05319 | 0.02224 | 0.01339 | 0.1813 | 0.05536 | 0.1555 | 0.5762 | 1.392 | 14.03 | 0.003308 | 0.01315 | 0.009904 | 0.004832 | 0.01316 | 0.002095 | 15.14 | 21.8 | 101.2 | 718.9 | 0.09384 | 0.2006 | 0.1384 | 0.06222 | 0.2679 | 0.07698 |
- Preparação dos Dados
Os dados foram pré-processados para garantir melhor qualidade nas previsões. As principais etapas incluíram:
Normalização das variáveis numéricas.
Aplicação do SMOTE (Synthetic Minority Oversampling Technique) para balanceamento das classes.
Utilização de PCA (Principal Component Analysis) para redução de dimensionalidade, garantindo menor complexidade e melhor desempenho computacional.
- Treinamento dos Modelos
KNN (K-Nearest Neighbors): Algoritmo supervisionado baseado na proximidade dos vizinhos mais próximos.
KMeans (K-Means Clustering): Algoritmo não supervisionado de agrupamento, adaptado para a tarefa de classificação.
Aplicação da Técnicas
Este projeto tem como objetivo avaliar e comparar o desempenho de dois algoritmos de Machine Learning – KNN (K-Nearest Neighbors) e KMeans (K-Means Clustering) – aplicados a um problema de classificação binária. A análise foi conduzida com foco em métricas de desempenho e matrizes de confusão, de forma a compreender vantagens e limitações de cada abordagem.
Implementação do KNN
Accuracy: 0.91
import numpy as np
import matplotlib.pyplot as plt
from io import StringIO
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score
import seaborn as sns
import pandas as pd
from sklearn.preprocessing import LabelEncoder
from imblearn.over_sampling import SMOTE
plt.figure(figsize=(12,10))
#carregamento da base
df = pd.read_csv('https://raw.githubusercontent.com/MariaLuizazz/MACHINE-LEARNING-PESSOAL/refs/heads/main/dados/breast-cancer.csv')
#Préprocess
#remoção da coluna id pois é irrelevante para o modelo
df = df.drop(columns=['id'])
#conversão de letra para número
label_encoder = LabelEncoder()
df['diagnosis'] = label_encoder.fit_transform(df['diagnosis'])
#imputação com mediana de valores ausentes nas features concavity_worts e concavity points_worst
df['concavity_mean'].fillna(df['concavity_mean'].median(), inplace=True)
df['concave points_mean'].fillna(df['concave points_mean'].median(), inplace=True)
#escolha de features
# Em vez de usar apenas 2 features
X = df[['radius_mean', 'texture_mean']]
y = df['diagnosis']
#Separação de treino e teste
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42, stratify=y)
smote = SMOTE(random_state=42)
X_train_balanced, y_train_balanced = smote.fit_resample(X_train, y_train)
#Treianamento do KNN
knn = KNeighborsClassifier(n_neighbors=11)
knn.fit(X_train_balanced, y_train_balanced)
#Teste e validação
predictions = knn.predict(X_test)
print(f"Accuracy: {accuracy_score(y_test, predictions):.2f}")
# Mapeia os rótulos: 0 -> Benigno, 1 -> Maligno
labels_map = {0: "Benigno", 1: "Maligno"}
y_labels = y.map(labels_map)
#Preparação para o gráfico da fronteira de decisão(malha de visualização)
h = 0.02
x_min, x_max = X.iloc[:, 0].min() - 1, X.iloc[:, 0].max() + 1
y_min, y_max = X.iloc[:, 1].min() - 1, X.iloc[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, h),np.arange(y_min, y_max, h))
#Prevendo classe em cada ponto
Z = knn.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
#gráfico final
plt.contourf(xx, yy, Z, cmap=plt.cm.RdYlGn_r, alpha=0.3)
sns.scatterplot(x=X.iloc[:, 0], y=X.iloc[:, 1], hue=y_labels, style=y_labels, palette={'Benigno': 'green', 'Maligno': 'red'}, s=100) #motivooooooo do errroo
plt.xlabel("radius_mean")
plt.ylabel("texture_mean")
plt.title("KNN Decision Boundary (k=11) - Diagnóstico de Câncer (Com Balanceamento SMOTE)")
plt.legend(title="Diagnóstico")
#Exibição do gráfico
buffer = StringIO()
plt.savefig(buffer, format="svg", transparent=True)
print(buffer.getvalue())
Implementação do KMEANS
import numpy as np
import matplotlib.pyplot as plt
from io import StringIO
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
import pandas as pd
df = pd.read_csv('https://raw.githubusercontent.com/MariaLuizazz/MACHINE-LEARNING-PESSOAL/refs/heads/main/dados/breast-cancer.csv')
# Features
X = df.drop(columns=['diagnosis', 'id'])
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
kmeans = KMeans(n_clusters=3, init='k-means++', max_iter=100, random_state=42)
labels = kmeans.fit_predict(X_pca)
# Adicionar clusters ao dataframe
df['Cluster'] = labels
plt.figure(figsize=(10, 8))
plt.scatter(X_pca[:, 0], X_pca[:, 1], c=labels, cmap='viridis', s=50)
plt.scatter(kmeans.cluster_centers_[:, 0], kmeans.cluster_centers_[:, 1],
c='red', marker='*', s=200, label='Centróides')
plt.title('Clusters após redução de dimensionalidade (PCA)')
plt.xlabel('Componente Principal 1')
plt.ylabel('Componente Principal 2')
plt.legend()
plt.show()
buffer = StringIO()
plt.savefig(buffer, format="svg", transparent=True)
print(buffer.getvalue())
Matrizes de Confusão
Matriz de Confusão - KNN
| Previsto Benigno | Previsto Maligno | |
|---|---|---|
| Real Benigno | 99 | 8 |
| Real Maligno | 7 | 57 |
Matriz de Confusão - KMeans
| Previsto Benigno | Previsto Maligno | |
|---|---|---|
| Real Benigno | 354 | 3 |
| Real Maligno | 53 | 159 |
Avaliação dos Modelos
Matriz de Confusão - KNN
| Previsto Benigno | Previsto Maligno | |
|---|---|---|
| Real Benigno | 99 | 8 |
| Real Maligno | 7 | 57 |
- Acurácia: 0.912
- Precisão: 0.877
- Recall: 0.891
- F1-score: 0.884
Matriz de Confusão - KMeans
| Previsto Benigno | Previsto Maligno | |
|---|---|---|
| Real Benigno | 354 | 3 |
| Real Maligno | 53 | 159 |
- Acurácia: 0.902
- Precisão: 0.981
- Recall: 0.750
- F1-score: 0.850
Comparação de Métricas
| Modelo | Acurácia | Precisão | Recall | F1-score |
|---|---|---|---|---|
| KNN | 0.912 | 0.877 | 0.891 | 0.884 |
| KMeans | 0.902 | 0.981 | 0.750 | 0.850 |
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import LabelEncoder, StandardScaler
from sklearn.decomposition import PCA
from sklearn.metrics import confusion_matrix, classification_report, accuracy_score, precision_score, recall_score, f1_score
from imblearn.over_sampling import SMOTE
from sklearn.cluster import KMeans
from scipy.stats import mode
df = pd.read_csv('https://raw.githubusercontent.com/MariaLuizazz/MACHINE-LEARNING-PESSOAL/refs/heads/main/dados/breast-cancer.csv')
# Pré-processamento
df = df.drop(columns=['id'])
label_encoder = LabelEncoder()
df['diagnosis'] = label_encoder.fit_transform(df['diagnosis'])
# Corrigir valores ausentes
df['concavity_mean'].fillna(df['concavity_mean'].median(), inplace=True)
df['concave points_mean'].fillna(df['concave points_mean'].median(), inplace=True)
# Features selecionadas
X = df[['radius_mean', 'texture_mean', 'perimeter_mean', 'area_mean',
'smoothness_mean', 'compactness_mean', 'concavity_mean']]
y = df['diagnosis']
#knn
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# PCA
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
X_train, X_test, y_train, y_test = train_test_split(
X_pca, y, test_size=0.3, random_state=42, stratify=y
)
# Balanceamento com SMOTE
smote = SMOTE(random_state=42)
X_train_bal, y_train_bal = smote.fit_resample(X_train, y_train)
# Treinamento do KNN
knn = KNeighborsClassifier(n_neighbors=11)
knn.fit(X_train_bal, y_train_bal)
# Predição
y_pred_knn = knn.predict(X_test)
cm_knn = confusion_matrix(y_test, y_pred_knn)
# Métricas KNN
acc_knn = accuracy_score(y_test, y_pred_knn)
prec_knn = precision_score(y_test, y_pred_knn)
rec_knn = recall_score(y_test, y_pred_knn)
f1_knn = f1_score(y_test, y_pred_knn)
# Modelo 2: KMeans
scaler_full = StandardScaler()
X_scaled_full = scaler_full.fit_transform(X)
pca_full = PCA(n_components=2)
X_pca_full = pca_full.fit_transform(X_scaled_full)
kmeans = KMeans(n_clusters=2, init='k-means++', max_iter=100, random_state=42, n_init=10)
clusters = kmeans.fit_predict(X_pca_full)
# Mapear clusters para classes reais
mapping = {}
for cluster in np.unique(clusters):
mask = clusters == cluster
mapping[cluster] = mode(y[mask], keepdims=True).mode[0]
y_pred_kmeans = [mapping[c] for c in clusters]
cm_kmeans = confusion_matrix(y, y_pred_kmeans)
# Métricas KMeans
acc_kmeans = accuracy_score(y, y_pred_kmeans)
prec_kmeans = precision_score(y, y_pred_kmeans)
rec_kmeans = recall_score(y, y_pred_kmeans)
f1_kmeans = f1_score(y, y_pred_kmeans)
# Impressão em Markdown
def matriz_markdown(cm, labels, titulo):
md = f"### {titulo}\n\n"
md += f"| | Previsto {labels[0]} | Previsto {labels[1]} |\n"
md += f"|-----------------|------------------|------------------|\n"
md += f"| **Real {labels[0]}** | {cm[0,0]} | {cm[0,1]} |\n"
md += f"| **Real {labels[1]}** | {cm[1,0]} | {cm[1,1]} |\n"
return md
print(matriz_markdown(cm_knn, ["Benigno", "Maligno"], "Matriz de Confusão - KNN"))
print(f"- Acurácia: {acc_knn:.3f}\n- Precisão: {prec_knn:.3f}\n- Recall: {rec_knn:.3f}\n- F1-score: {f1_knn:.3f}\n")
print(matriz_markdown(cm_kmeans, ["Benigno", "Maligno"], "Matriz de Confusão - KMeans"))
print(f"- Acurácia: {acc_kmeans:.3f}\n- Precisão: {prec_kmeans:.3f}\n- Recall: {rec_kmeans:.3f}\n- F1-score: {f1_kmeans:.3f}\n")
# Comparação lado a lado em Markdown
comparacao = f"""
### Comparação de Métricas
| Modelo | Acurácia | Precisão | Recall | F1-score |
|----------|----------|----------|--------|----------|
| **KNN** | {acc_knn:.3f} | {prec_knn:.3f} | {rec_knn:.3f} | {f1_knn:.3f} |
| **KMeans**| {acc_kmeans:.3f} | {prec_kmeans:.3f} | {rec_kmeans:.3f} | {f1_kmeans:.3f} |
"""
print(comparacao)
Comparação dos Resultados
A avaliação dos dois modelos (KNN e KMeans) permitiu observar diferenças importantes em termos de desempenho, destacando pontos fortes e limitações de cada abordagem.
O modelo KNN apresentou uma acurácia de 91,2%, com métricas balanceadas entre precisão (0,877), recall (0,891) e F1-score (0,884). Isso indica que o algoritmo teve um bom equilíbrio entre identificar corretamente os casos benignos e malignos, sendo mais consistente no tratamento das duas classes. No entanto, a precisão foi ligeiramente inferior, o que significa que, entre os casos previstos como malignos, houve uma proporção maior de falsos positivos em comparação ao KMeans.
Já o modelo KMeans, por se tratar de um algoritmo de aprendizado não supervisionado, surpreendeu ao alcançar uma acurácia de 90,2%, próxima à do KNN. Seu destaque foi a alta precisão (0,981), ou seja, quase todas as amostras classificadas como malignas realmente pertenciam a essa classe. Porém, essa alta precisão veio acompanhada de uma queda no recall (0,750), mostrando que o KMeans deixou de identificar corretamente uma parcela considerável dos casos malignos, classificando-os como benignos. Isso é crítico em contextos sensíveis, como o diagnóstico médico, em que falsos negativos podem trazer riscos significativos.
- De forma geral, pode-se afirmar que:
O KNN é mais equilibrado e confiável para cenários em que tanto falsos positivos quanto falsos negativos precisam ser controlados.
O KMeans é vantajoso em termos de precisão, mas pode não ser o mais indicado em situações em que a detecção completa dos casos positivos (alto recall) é essencial.
Portanto, a escolha entre os dois modelos dependerá diretamente do contexto de aplicação: se o objetivo for minimizar falsos negativos, o KNN se mostra mais adequado; se o objetivo for garantir maior segurança nas classificações positivas, o KMeans pode ser preferido.