Skip to content

Machine Learning com KNN (K-Nearest Neighbors)

Base de Dados Utilizada

Dataset - Kaggle

Tipo: numérica discreta O que é: identificador único do passageiro. Para que serve: não contém informação útil para predição. Ação necessária: remover do modelo.

2025-11-30T20:15:40.806954 image/svg+xml Matplotlib v3.10.7, https://matplotlib.org/

Tipo: categórica binária (target) O que é: 1 = sobreviveu; 0 = não sobreviveu. Para que serve: variável dependente a ser prevista. Ação necessária: checar balanceamento (a classe 0 é ligeiramente maior).

2025-11-30T20:15:40.885645 image/svg+xml Matplotlib v3.10.7, https://matplotlib.org/

Tipo: categórica ordinal O que é: classe socioeconômica do ticket (1ª, 2ª, 3ª). Para que serve: proxy de condição financeira/social que influencia sobrevivência. Ação necessária: manter como categórica ordinal; verificar distribuição nas classes.

2025-11-30T20:15:40.942196 image/svg+xml Matplotlib v3.10.7, https://matplotlib.org/

Tipo: categórica binária Oque é: sexo biológico do passageiro (male/female). Para que serve: uma das variáveis mais importantes na sobrevivência. Ação necessária: codificar para dummy (female → 1, male → 0).

2025-11-30T20:15:40.997410 image/svg+xml Matplotlib v3.10.7, https://matplotlib.org/

Tipo: numérica contínua O que é: idade em anos. Para que serve: importante para separar grupos vulneráveis (crianças, adultos). Ação necessária: 177 valores ausentes → imputar (média/mediana ou por título extraído do Name).

2025-11-30T20:15:41.061768 image/svg+xml Matplotlib v3.10.7, https://matplotlib.org/

Tipo: numérica discreta O que é: número de irmãos/cônjuges a bordo. Para que serve: indica tamanho do grupo familiar; pode influenciar sobrevivência. Ação necessária: manter; possível normalizar ou agrupar faixas.

2025-11-30T20:15:41.145963 image/svg+xml Matplotlib v3.10.7, https://matplotlib.org/

Tipo: numérica discreta O que é: número de pais/filhos a bordo. Para que serve: outro indicador do grupo familiar. Ação necessária: manter; possível criar “FamilySize = SibSp + Parch + 1”.

2025-11-30T20:15:41.227387 image/svg+xml Matplotlib v3.10.7, https://matplotlib.org/

Tipo: categórica (texto) O que é: número/código do ticket. Para que serve: pouco útil originalmente; pode ajudar se agrupado por prefixos. Ação necessária: normalmente remover.

2025-11-30T20:15:41.304670 image/svg+xml Matplotlib v3.10.7, https://matplotlib.org/

Tipo: numérica contínua O que é: tarifa paga pelo ticket. Para que serve: relação com classe social; boa variável preditiva. Ação necessária: checar outliers; possível normalização logarítmica.

2025-11-30T20:15:41.393198 image/svg+xml Matplotlib v3.10.7, https://matplotlib.org/

Tipo: categórica nominal O que é: porto de embarque (C, Q, S). Para que serve: pode refletir diferenças sociais/regionais. Ação necessária: imputar os 2 valores ausentes; criar dummies.

2025-11-30T20:15:41.468474 image/svg+xml Matplotlib v3.10.7, https://matplotlib.org/

Objetivo do Projeto

O objetivo deste trabalho é aplicar o algoritmo K-Nearest Neighbors (KNN) para resolver um problema de Classificação Binária e prever a Sobrevivência (Survived) de um passageiro no Titanic.

Para simplificar a visualização da fronteira de decisão, o modelo utiliza apenas as seguintes features contínuas:

  • Age (Idade do Passageiro)
  • Fare (Preço da Passagem)

Análise e Modelo KNN (K=5)

O gráfico gerado abaixo representa a fronteira de decisão do modelo. As regiões coloridas indicam a classe (sobrevivência) que o modelo KNN prediz para qualquer novo passageiro que caia naquela área do plano 2D.

Accuracy (KNN com k=5): 0.66 Classification Report: precision recall f1-score support Não Sobreviveu 0.68 0.77 0.72 126 Sobreviveu 0.60 0.49 0.54 89 accuracy 0.66 215 macro avg 0.64 0.63 0.63 215 weighted avg 0.65 0.66 0.65 215 Confusion Matrix: [[97 29] [45 44]] 2025-11-30T20:15:42.125091 image/svg+xml Matplotlib v3.10.7, https://matplotlib.org/

import numpy as np
import matplotlib.pyplot as plt
from io import StringIO
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report
from sklearn.preprocessing import StandardScaler
import seaborn as sns
import pandas as pd
import kagglehub
import os

# 1. CARREGAMENTO E PRÉ-PROCESSAMENTO DE DADOS

# Baixar o dataset Titanic via kagglehub
path = kagglehub.dataset_download("yasserh/titanic-dataset")
file_path = os.path.join(path, "Titanic-Dataset.csv")
df = pd.read_csv(file_path)

# Selecionar features numéricas: Age, Fare, Pclass
# Variável alvo binária: Survived (0 = não sobreviveu, 1 = sobreviveu)
X = df[['Pclass', 'Age', 'Fare']].copy()

# Variável alvo binária
y = df['Survived'].copy()

# Limpeza dos dados (remover linhas com valores faltantes)
data = X.copy()
data['Survived'] = y
data = data.dropna()

X_clean = data[['Pclass', 'Age', 'Fare']]
y_clean = data['Survived']

# 2. ESCALONAMENTO DOS DADOS (CRÍTICO PARA KNN)
scaler = StandardScaler()
# Ajustar e transformar as features
X_scaled = scaler.fit_transform(X_clean)
X_scaled_df = pd.DataFrame(X_scaled, columns=X_clean.columns, index=X_clean.index)


# 3. DIVISÃO TREINO/TESTE
# Usamos os dados escalonados (X_scaled_df) para o treino
X_train, X_test, y_train, y_test = train_test_split(
    X_scaled_df, y_clean,
    test_size=0.3,
    random_state=42
)

# 4. TREINAMENTO E AVALIAÇÃO DO MODELO KNN
knn = KNeighborsClassifier(n_neighbors=5)
knn.fit(X_train, y_train)
predictions = knn.predict(X_test)

# Métricas de desempenho
accuracy = accuracy_score(y_test, predictions)
print(f"Accuracy (KNN com k=5): {accuracy:.2f}")
print("\nClassification Report:")
print(classification_report(y_test, predictions, target_names=['Não Sobreviveu', 'Sobreviveu']))

# Matriz de confusão
cm = confusion_matrix(y_test, predictions)
print("\nConfusion Matrix:")
print(cm)


# 5. VISUALIZAÇÃO DA FRONTEIRA DE DECISÃO

# Para visualização em 2D, vamos usar apenas Age e Fare
X_2d = X_scaled_df[['Age', 'Fare']].copy()
X_train_2d, X_test_2d, y_train_2d, y_test_2d = train_test_split(
    X_2d, y_clean,
    test_size=0.3,
    random_state=42
)

# Treinar KNN com os dados 2D
knn_2d = KNeighborsClassifier(n_neighbors=5)
knn_2d.fit(X_train_2d, y_train_2d)

# Reduzir a amostra para visualização (para o scatter plot)
sample_size = min(1000, len(X_2d))
# Usamos o índice para garantir que X_vis e y_vis correspondam
X_vis = X_2d.sample(sample_size, random_state=42)
y_vis = y_clean.loc[X_vis.index]

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

# Definir os limites da grade a partir dos dados ESCALONADOS
h = 0.05 
x_min, x_max = X_2d['Age'].min() - 0.5, X_2d['Age'].max() + 0.5
y_min, y_max = X_2d['Fare'].min() - 0.5, X_2d['Fare'].max() + 0.5

xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))

# Criar a grade de pontos para predição
grid_points = np.c_[xx.ravel(), yy.ravel()]

# Previsão KNN para cada ponto da grade
Z = knn_2d.predict(grid_points)
Z = Z.reshape(xx.shape)

# Plot da fronteira e dos pontos
plt.contourf(xx, yy, Z, cmap=plt.cm.RdYlBu, alpha=0.3)
sns.scatterplot(x=X_vis['Age'], y=X_vis['Fare'], hue=y_vis, style=y_vis,
                palette="deep", s=100, legend='full')

# Configurar legenda e rótulos
handles, _ = plt.gca().get_legend_handles_labels()
plt.legend(handles=handles, labels=['Não Sobreviveu', 'Sobreviveu'], title="Titanic - Survivability")

plt.xlabel("Age (Scaled)")
plt.ylabel("Fare (Scaled)")
plt.title("KNN Decision Boundary (k=5) - Titanic Dataset")


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

Interpretação dos Resultados

O desempenho do modelo demonstra que a Idade e o Preço da Passagem (Fare) são preditores significativos para a sobrevivência no Titanic. Passageiros com passagens mais caras (classes mais altas) tinham maior probabilidade de sobreviver, assim como certos grupos etários.

Passo a Passo da Implementação

1. Importação de Bibliotecas e Carregamento de Dados

Importamos o StandardScaler, que é essencial para o algoritmo KNN.

import numpy as np
import matplotlib.pyplot as plt
from io import StringIO
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
from sklearn.preprocessing import StandardScaler # CRÍTICO para KNN
import seaborn as sns
import pandas as pd
import kagglehub

# Carregar o dataset via kagglehub
kaggle_dataset = kagglehub.dataset_download("brendan45774/test-file")
df = pd.read_csv(f"{kaggle_dataset}/titanic.csv")

# Selecionar Features (X) e Variável Alvo (y)
X = df[['Age', 'Fare']]
y = df['Survived']  # Variável binária: 0 (Não Sobreviveu) ou 1 (Sobreviveu)

# Limpeza e Preparação
data = X.copy()
data['Survived'] = y
data = data.dropna()  # Remove valores ausentes

X_clean = data[['Age', 'Fare']]
y_clean = data['Survived']

2. Escalonamento dos Atributos (StandardScaler)

O KNN é baseado na distância. Sem esta etapa, a variável Fare (valores maiores) dominaria o cálculo da distância em relação à Age (valores menores), invalidando o modelo.

# Aplica a Padronização (StandardScaler)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X_clean)
X_scaled_df = pd.DataFrame(X_scaled, columns=X_clean.columns, index=X_clean.index)

3. Treinamento e Avaliação do Modelo

Dividimos os dados escalonados (X_scaled_df) e treinamos o KNeighborsClassifier com K=5 vizinhos.

# Divisão Treino/Teste (70% Treino, 30% Teste)
X_train, X_test, y_train, y_test = train_test_split(
    X_scaled_df, y_clean,
    test_size=0.3,
    random_state=42
)

# Treinar o modelo KNN
knn = KNeighborsClassifier(n_neighbors=5)
knn.fit(X_train, y_train)

# Prever e Avaliar
predictions = knn.predict(X_test)
print(f"Accuracy: {accuracy_score(y_test, predictions):.2f}")
print("\nClassification Report:")
print(classification_report(y_test, predictions, target_names=['Não Sobreviveu', 'Sobreviveu']))

4. Geração da Fronteira de Decisão

Este passo gera a grade de visualização para plotar a fronteira de decisão do modelo KNN.

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

# Amostragem para plotagem
sample_size = 5000
X_vis = X_scaled_df.sample(sample_size, random_state=42)
y_vis = y_clean.loc[X_vis.index]

# Definição dos limites da grade
h = 0.05
x_min, x_max = X_scaled_df.iloc[:, 0].min() - 0.5, X_scaled_df.iloc[:, 0].max() + 0.5
y_min, y_max = X_scaled_df.iloc[:, 1].min() - 0.5, X_scaled_df.iloc[:, 1].max() + 0.5

# Criação da grade e predição
xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
grid_points = np.c_[xx.ravel(), yy.ravel()]
Z = knn.predict(grid_points).reshape(xx.shape)

# Plot da fronteira e dos dados
plt.contourf(xx, yy, Z, cmap=plt.cm.RdBu_r, alpha=0.3)
sns.scatterplot(x=X_vis.iloc[:, 0], y=X_vis.iloc[:, 1], hue=y_vis, 
                style=y_vis, palette={0: 'blue', 1: 'orange'}, 
                s=100, markers={0: 'o', 1: 'x'}, legend='full')

plt.xlabel("Age (Scaled)")
plt.ylabel("Fare (Scaled)")
plt.title("KNN Decision Boundary (k=5) - Titanic Dataset")
plt.legend(title="Titanic - Survivability", labels=["Não Sobreviveu", "Sobreviveu"])

plt.tight_layout()
plt.show()