Predição de Personalidade: Abordagem Avançada com Stacking de Modelos Supervisionados

Author

Gabriel Cardoso

Introdução

Este código tem como objetivo classificar indivíduos como introvertidos ou extrovertidos com base em carecterísticas de comportamento social do conjunto de dados de personalidade. Foi utilizado pré-processamento avançado, engenheria de recursos e com conjunto de empilhamento com Random Forest, Gradient Boosting, XGBoost e SVM, otimizados com amplo ajuste de hiperâmetros. O Objetivo é alcançar alto desempenho e métrica de competição (por exemplo, pontuação F1 ou AUC) e criar um script refinado.

Pacotes utilizados:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split, RandomizedSearchCV, StratifiedKFold, cross_val_score
from sklearn.preprocessing import LabelEncoder, StandardScaler, PolynomialFeatures
from sklearn.impute import SimpleImputer
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier, StackingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
from xgboost import XGBClassifier
from imblearn.over_sampling import SMOTE
from sklearn.metrics import classification_report, confusion_matrix, roc_curve, roc_auc_score, f1_score
import os
import shap
import joblib
import warnings

Análise Exploratória Exploramos o conjunto de dados para entender a distribuição das variáveis, o balanceamento das classes e as correlações, o que orienta as etapas de pré-processamento e modelagem.

df.head()
tempo_gasto_sozinho medo_de_apresentar_em_publico participacao_em_evento_social sair_de_casa cansaso_apos_socializar tamanho_circulo_amigos frequencia_postagem Personalidade
0 4.0 0 4.0 6.0 0 13.0 5.0 Extrovert
1 9.0 1 0.0 0.0 1 0.0 3.0 Introvert
2 9.0 1 1.0 2.0 1 5.0 2.0 Introvert
3 0.0 0 6.0 7.0 0 14.0 8.0 Extrovert
4 3.0 0 9.0 4.0 0 8.0 5.0 Extrovert
print("Dataset Shape:", df.shape)
print("\nDataset Info:")
print(df.info())
print("\nMissing Values:")
print(df.isnull().sum())
Dataset Shape: (2900, 8)

Dataset Info:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 2900 entries, 0 to 2899
Data columns (total 8 columns):
 #   Column                         Non-Null Count  Dtype  
---  ------                         --------------  -----  
 0   tempo_gasto_sozinho            2837 non-null   float64
 1   medo_de_apresentar_em_publico  2900 non-null   int64  
 2   participacao_em_evento_social  2838 non-null   float64
 3   sair_de_casa                   2834 non-null   float64
 4   cansaso_apos_socializar        2900 non-null   int64  
 5   tamanho_circulo_amigos         2823 non-null   float64
 6   frequencia_postagem            2835 non-null   float64
 7   Personalidade                  2900 non-null   object 
dtypes: float64(5), int64(2), object(1)
memory usage: 181.4+ KB
None

Missing Values:
tempo_gasto_sozinho              63
medo_de_apresentar_em_publico     0
participacao_em_evento_social    62
sair_de_casa                     66
cansaso_apos_socializar           0
tamanho_circulo_amigos           77
frequencia_postagem              65
Personalidade                     0
dtype: int64
# verificar valores ausentes nas colunas categoricas
categorical_columns = ['medo_de_apresentar_em_publico', 'cansaso_apos_socializar']
data_numerical = df.drop(columns=categorical_columns)
target_column = 'Personalidade'

for col in categorical_columns:
    print(f"\nUnique values in {col}:")
    print(df[col].value_counts(dropna=False))

Unique values in medo_de_apresentar_em_publico:
medo_de_apresentar_em_publico
1    1483
0    1417
Name: count, dtype: int64

Unique values in cansaso_apos_socializar:
cansaso_apos_socializar
1    1459
0    1441
Name: count, dtype: int64
# Primeiro, vamos garantir que data_numerical contém apenas as colunas numéricas corretas
data_numerical = df.select_dtypes(include=['float64', 'int64']).columns.tolist()
# Remover colunas categóricas que foram codificadas como numéricas
data_numerical = [col for col in data_numerical if col not in ['medo_de_apresentar_em_publico', 'cansaso_apos_socializar']]
plt.figure(figsize=(8, 6))
sns.countplot(x=target_column, data=df)
plt.title('Class Distribution of Personality Types')
plt.xlabel('Personalidade')
plt.ylabel('Count')
plt.show()

plt.figure(figsize=(8, 6))
for i, col in enumerate(data_numerical, 1):
    plt.subplot(3, 2, i)
    sns.boxplot(x=target_column, y=col, data=df)
    plt.title(f'{col} by Personality')
plt.tight_layout()
plt.show()

sns.pairplot(df[data_numerical + [target_column]], hue=target_column, diag_kind='hist')
plt.suptitle('Pair Plot of Numeric Features by Personality', y=1.02)
plt.show()

Limpeza dos dados

Lidamos com valores ausentes, codificamos variáveis categóricas,limitamos outliers e aplicamos SMOTE e normalização para preparar os dados para a modelagem.

# 1. Codificar a coluna alvo (Personalidade) para valores numéricos
le = LabelEncoder()
df[target_column] = le.fit_transform(df[target_column])
print(f"Classes codificadas: {dict(zip(le.classes_, le.transform(le.classes_)))}")

# 2. Definir colunas numéricas e categóricas
numeric_columns = ['tempo_gasto_sozinho', 'participacao_em_evento_social',
                  'sair_de_casa', 'tamanho_circulo_amigos', 'frequencia_postagem']
categorical_columns = ['medo_de_apresentar_em_publico', 'cansaso_apos_socializar']

# 3. Tratar valores faltantes
# Para colunas numéricas: preencher com a mediana
numeric_imputer = SimpleImputer(strategy='median')
df[numeric_columns] = numeric_imputer.fit_transform(df[numeric_columns])

# Para colunas categóricas: preencher com a moda (valor mais frequente)
categorical_imputer = SimpleImputer(strategy='most_frequent')
df[categorical_columns] = categorical_imputer.fit_transform(df[categorical_columns])

# 5. Lidar com outliers (método de caping - limitar os valores extremos)
for col in numeric_columns:
    Q1 = df[col].quantile(0.25)
    Q3 = df[col].quantile(0.75)
    IQR = Q3 - Q1
    lower_bound = Q1 - 1.5 * IQR
    upper_bound = Q3 + 1.5 * IQR
    df[col] = df[col].clip(lower=lower_bound, upper=upper_bound)
Classes codificadas: {'Extrovert': np.int64(0), 'Introvert': np.int64(1)}
# 1. Separar features (X) e target (y)
X = df.drop(columns=[target_column])  # Todas as colunas exceto 'Personalidade'
y = df[target_column]                 # Apenas a coluna 'Personalidade'

# 2. Dividir em conjuntos de treino e teste
X_train, X_test, y_train, y_test = train_test_split(
    X,
    y,
    test_size=0.3,     # 30% para teste, 70% para treino
    stratify=y,        # Mantém a proporção das classes
    random_state=42    # Semente para reprodutibilidade
)

# 3. Verificar a distribuição das classes (opcional)
print("\nDistribuição original:")
print(y.value_counts(normalize=True))

print("\nDistribuição no treino:")
print(y_train.value_counts(normalize=True))

print("\nDistribuição no teste:")
print(y_test.value_counts(normalize=True))


# 4. Normalizar os dados (StandardScaler)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)  # Ajuste e transformação no treino
X_test_scaled  = scaler.transform(X_test)       # Apenas transformação no teste

# 5. Converter de volta para DataFrame
X_train_scaled = pd.DataFrame(X_train_scaled, columns=X_train.columns, index=X_train.index)
X_test_scaled  = pd.DataFrame(X_test_scaled,  columns=X_test.columns,  index=X_test.index)

# 6. Verificar as formas dos conjuntos
print("\nDimensões após pré-processamento:")
print(f"Treino: {X_train_scaled.shape}, Teste: {X_test_scaled.shape}")

Distribuição original:
Personalidade
0    0.514138
1    0.485862
Name: proportion, dtype: float64

Distribuição no treino:
Personalidade
0    0.514286
1    0.485714
Name: proportion, dtype: float64

Distribuição no teste:
Personalidade
0    0.513793
1    0.486207
Name: proportion, dtype: float64

Dimensões após pré-processamento:
Treino: (2030, 7), Teste: (870, 7)

Modelo padrão

Implementar modelo padrão( sem nenhum paramentro especifico).

import pandas as pd
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.neighbors import KNeighborsClassifier
from xgboost import XGBClassifier
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
import seaborn as sns

# ------------------------------------------------------------
# 1) Concatenar treino e teste para projeto PCA
# ------------------------------------------------------------
features_scaled_df = pd.concat([X_train_scaled, X_test_scaled], axis=0).reset_index(drop=True)
y_full = pd.concat([y_train.reset_index(drop=True), y_test.reset_index(drop=True)], axis=0)

# ------------------------------------------------------------
# 2) Treinar cada modelo usando apenas parâmetros padrão
# ------------------------------------------------------------
rf_model_default = RandomForestClassifier()               # todos os hiperparâmetros no default
rf_model_default.fit(X_train_scaled, y_train)

gb_model_default = GradientBoostingClassifier()           # default
gb_model_default.fit(X_train_scaled, y_train)

knn_model_default = KNeighborsClassifier()                # default
knn_model_default.fit(X_train_scaled, y_train)

xgb_model_default = XGBClassifier(use_label_encoder=False, eval_metric='logloss')
# O XGBClassifier exige passar use_label_encoder=False em versões recentes
xgb_model_default.fit(X_train_scaled, y_train)

# ------------------------------------------------------------
# 3) Projeção PCA em 2D de todo o conjunto escalado
# ------------------------------------------------------------
pca = PCA(n_components=2)
X_pca_full = pca.fit_transform(features_scaled_df)

# ------------------------------------------------------------
# 4) Predições de cada modelo para todo o conjunto escalado
# ------------------------------------------------------------
rf_pred_full  = rf_model_default.predict(features_scaled_df)
gb_pred_full  = gb_model_default.predict(features_scaled_df)
knn_pred_full = knn_model_default.predict(features_scaled_df)
xgb_pred_full = xgb_model_default.predict(features_scaled_df)

# ------------------------------------------------------------
# 5) Plot 2×2 mostrando a separação PCA para cada modelo
# ------------------------------------------------------------
fig, axes = plt.subplots(2, 2, figsize=(14, 12))
model_info = [
    ('Random Forest (padrão)',     rf_pred_full,  axes[0, 0]),
    ('Gradient Boosting (padrão)', gb_pred_full,  axes[0, 1]),
    ('KNN (padrão)',               knn_pred_full, axes[1, 0]),
    ('XGBoost (padrão)',           xgb_pred_full, axes[1, 1])
]

for name, preds, ax in model_info:
    sns.scatterplot(
        x=X_pca_full[:, 0],
        y=X_pca_full[:, 1],
        hue=preds,
        palette='Set1',
        alpha=0.7,
        edgecolor='k',
        ax=ax
    )
    ax.set_title(f"{name} – PCA Projection")
    ax.set_xlabel("Componente Principal 1")
    ax.set_ylabel("Componente Principal 2")
    ax.legend(title='Classe Predita', loc='upper right')
    ax.grid(True)

plt.tight_layout()
plt.show()
C:\Users\gabriel.cardoso\AppData\Local\Programs\Python\Python313\Lib\site-packages\xgboost\training.py:183: UserWarning:

[15:21:02] WARNING: C:\actions-runner\_work\xgboost\xgboost\src\learner.cc:738: 
Parameters: { "use_label_encoder" } are not used.

import numpy as np
import pandas as pd
from sklearn.metrics import (
    roc_auc_score,
    f1_score,
    classification_report,
    confusion_matrix,
    accuracy_score
)

# ------------------------------------------------------------
# 1) Obter predições e probabilidades de cada modelo no conjunto de teste
# ------------------------------------------------------------
rf_pred  = rf_model_default.predict(X_test_scaled)
rf_proba = rf_model_default.predict_proba(X_test_scaled)[:, 1]

gb_pred  = gb_model_default.predict(X_test_scaled)
gb_proba = gb_model_default.predict_proba(X_test_scaled)[:, 1]

knn_pred  = knn_model_default.predict(X_test_scaled)
knn_proba = knn_model_default.predict_proba(X_test_scaled)[:, 1]

xgb_pred  = xgb_model_default.predict(X_test_scaled)
xgb_proba = xgb_model_default.predict_proba(X_test_scaled)[:, 1]

# ------------------------------------------------------------
# 2) Função auxiliar para exibir métricas e matriz de confusão
# ------------------------------------------------------------
def exibir_metricas_e_matriz(nome, y_true, y_pred, y_proba):
    print(f"\n=== {nome} (padrão) ===")
    # AUC e F1
    auc_val = roc_auc_score(y_true, y_proba)
    f1_val  = f1_score(y_true, y_pred)
    print(f"AUC: {auc_val:.4f} | F1-Score: {f1_val:.4f}")
    # Relatório de classificação
    print("\nRelatório de Classificação:")
    print(classification_report(y_true, y_pred))
    # Matriz de Confusão
    print("Matriz de Confusão:")
    print(confusion_matrix(y_true, y_pred))

# ------------------------------------------------------------
# 3) Exibir resultados para cada modelo
# ------------------------------------------------------------
exibir_metricas_e_matriz("Random Forest",     y_test, rf_pred,  rf_proba)
exibir_metricas_e_matriz("Gradient Boosting", y_test, gb_pred,  gb_proba)
exibir_metricas_e_matriz("KNN",               y_test, knn_pred, knn_proba)
exibir_metricas_e_matriz("XGBoost",           y_test, xgb_pred, xgb_proba)

# ------------------------------------------------------------
# 4) Tabela comparativa de AUC, F1-Score e Acurácia
# ------------------------------------------------------------
models_results = {
    'Random Forest':     (rf_pred,  rf_proba),
    'Gradient Boosting': (gb_pred,  gb_proba),
    'KNN':               (knn_pred, knn_proba),
    'XGBoost':           (xgb_pred, xgb_proba)
}

comparison_data = []
for nome, (pred, proba) in models_results.items():
    auc_val = roc_auc_score(y_test, proba)
    f1_val  = f1_score(y_test, pred)
    acc_val = accuracy_score(y_test, pred)
    comparison_data.append({
        'Modelo':    nome,
        'AUC':       round(auc_val, 6),
        'F1-Score':  round(f1_val, 6),
        'Acurácia':  round(acc_val, 6)
    })

comparison_df = pd.DataFrame(comparison_data)
print("\n=== Comparação Final ===")
print(comparison_df.to_string(index=False))

=== Random Forest (padrão) ===
AUC: 0.9417 | F1-Score: 0.9035

Relatório de Classificação:
              precision    recall  f1-score   support

           0       0.91      0.90      0.91       447
           1       0.90      0.91      0.90       423

    accuracy                           0.91       870
   macro avg       0.91      0.91      0.91       870
weighted avg       0.91      0.91      0.91       870

Matriz de Confusão:
[[404  43]
 [ 39 384]]

=== Gradient Boosting (padrão) ===
AUC: 0.9602 | F1-Score: 0.9188

Relatório de Classificação:
              precision    recall  f1-score   support

           0       0.94      0.90      0.92       447
           1       0.90      0.94      0.92       423

    accuracy                           0.92       870
   macro avg       0.92      0.92      0.92       870
weighted avg       0.92      0.92      0.92       870

Matriz de Confusão:
[[404  43]
 [ 27 396]]

=== KNN (padrão) ===
AUC: 0.9330 | F1-Score: 0.9200

Relatório de Classificação:
              precision    recall  f1-score   support

           0       0.94      0.90      0.92       447
           1       0.90      0.94      0.92       423

    accuracy                           0.92       870
   macro avg       0.92      0.92      0.92       870
weighted avg       0.92      0.92      0.92       870

Matriz de Confusão:
[[404  43]
 [ 26 397]]

=== XGBoost (padrão) ===
AUC: 0.9462 | F1-Score: 0.9019

Relatório de Classificação:
              precision    recall  f1-score   support

           0       0.92      0.89      0.90       447
           1       0.89      0.91      0.90       423

    accuracy                           0.90       870
   macro avg       0.90      0.90      0.90       870
weighted avg       0.90      0.90      0.90       870

Matriz de Confusão:
[[400  47]
 [ 37 386]]

=== Comparação Final ===
           Modelo      AUC  F1-Score  Acurácia
    Random Forest 0.941705  0.903529  0.905747
Gradient Boosting 0.960171  0.918794  0.919540
              KNN 0.933039  0.920046  0.920690
          XGBoost 0.946161  0.901869  0.903448

Tunning dos modelos

from sklearn.model_selection import RandomizedSearchCV, StratifiedKFold
from sklearn.metrics import classification_report, confusion_matrix, roc_auc_score, f1_score

# Definir os modelos e hiperparâmetros
models = {
    'Random Forest': {
        'model': RandomForestClassifier(random_state=42),
        'use_scaled': False,
        'params': {
            'n_estimators': [100, 150, 200],
            'max_depth': [None, 10, 20],
            'min_samples_split': [2, 5, 10],
            'class_weight': ['balanced', None]
        }
    },
    'Gradient Boosting': {
        'model': GradientBoostingClassifier(random_state=42),
        'use_scaled': False,
        'params': {
            'n_estimators': [100, 200],
            'learning_rate': [0.01, 0.1, 0.2],
            'max_depth': [3, 5],
            'subsample': [0.8, 1.0]
        }
    },
    'KNN': {
        'model': KNeighborsClassifier(),
        'use_scaled': True,
        'params': {
            'n_neighbors': [3, 5, 7, 9],
            'weights': ['uniform', 'distance'],
            'metric': ['euclidean', 'manhattan']
        }
    },
    'XGBoost': {
        'model': XGBClassifier(random_state=42, eval_metric='logloss'),
        'use_scaled': False,
        'params': {
            'n_estimators': [100, 150],
            'learning_rate': [0.05, 0.1],
            'max_depth': [3, 5],
            'subsample': [0.8, 1.0],
            'colsample_bytree': [0.8, 1.0]
        }
    }
}

# Configurações comuns
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
scoring = {'AUC': 'roc_auc', 'F1': 'f1_weighted', 'Accuracy': 'accuracy'}

# Dicionário para armazenar resultados
results = {}

# Loop através dos modelos
for model_name, config in models.items():
    print(f"\n=== Treinando {model_name} ===")

    # Selecionar dados (normalizados ou não)
    X_train_data = X_train_scaled if config['use_scaled'] else X_train
    X_test_data = X_test_scaled if config['use_scaled'] else X_test

    # Busca aleatória de hiperparâmetros
    search = RandomizedSearchCV(
        estimator=config['model'],
        param_distributions=config['params'],
        n_iter=20,
        cv=cv,
        scoring=scoring,
        refit='Accuracy',
        n_jobs=-1,
        random_state=42
    )

    search.fit(X_train_data, y_train)

    # Melhor modelo
    best_model = search.best_estimator_

    # Previsões
    y_pred = best_model.predict(X_test_data)
    y_proba = best_model.predict_proba(X_test_data)[:, 1]

    # Métricas
    auc = roc_auc_score(y_test, y_proba)
    f1 = f1_score(y_test, y_pred, average='weighted')
    report = classification_report(y_test, y_pred)
    cm = confusion_matrix(y_test, y_pred)

    # Armazenar resultados
    results[model_name] = {
        'model': best_model,
        'best_params': search.best_params_,
        'auc': auc,
        'f1': f1,
        'report': report,
        'confusion_matrix': cm
    }

    # Exibir resultados
    print(f"\nMelhores parâmetros: {search.best_params_}")
    print(f"AUC: {auc:.4f} | F1: {f1:.4f}")
    print("\nRelatório de Classificação:")
    print(report)
    print("\nMatriz de Confusão:")
    print(cm)

# Exibir resumo dos resultados
print("\n=== Resumo dos Modelos ===")
for model_name, result in results.items():
    print(f"\n{model_name}:")
    print(f"AUC: {result['auc']:.4f} | F1: {result['f1']:.4f}")

=== Treinando Random Forest ===

Melhores parâmetros: {'n_estimators': 100, 'min_samples_split': 5, 'max_depth': 10, 'class_weight': 'balanced'}
AUC: 0.9541 | F1: 0.9184

Relatório de Classificação:
              precision    recall  f1-score   support

           0       0.94      0.90      0.92       447
           1       0.90      0.94      0.92       423

    accuracy                           0.92       870
   macro avg       0.92      0.92      0.92       870
weighted avg       0.92      0.92      0.92       870


Matriz de Confusão:
[[403  44]
 [ 27 396]]

=== Treinando Gradient Boosting ===

Melhores parâmetros: {'subsample': 0.8, 'n_estimators': 100, 'max_depth': 3, 'learning_rate': 0.01}
AUC: 0.9563 | F1: 0.9196

Relatório de Classificação:
              precision    recall  f1-score   support

           0       0.94      0.90      0.92       447
           1       0.90      0.94      0.92       423

    accuracy                           0.92       870
   macro avg       0.92      0.92      0.92       870
weighted avg       0.92      0.92      0.92       870


Matriz de Confusão:
[[404  43]
 [ 27 396]]

=== Treinando KNN ===
C:\Users\gabriel.cardoso\AppData\Local\Programs\Python\Python313\Lib\site-packages\sklearn\model_selection\_search.py:317: UserWarning:

The total space of parameters 16 is smaller than n_iter=20. Running 16 iterations. For exhaustive searches, use GridSearchCV.

Melhores parâmetros: {'weights': 'uniform', 'n_neighbors': 9, 'metric': 'manhattan'}
AUC: 0.9428 | F1: 0.9184

Relatório de Classificação:
              precision    recall  f1-score   support

           0       0.94      0.90      0.92       447
           1       0.90      0.94      0.92       423

    accuracy                           0.92       870
   macro avg       0.92      0.92      0.92       870
weighted avg       0.92      0.92      0.92       870


Matriz de Confusão:
[[403  44]
 [ 27 396]]

=== Treinando XGBoost ===

Melhores parâmetros: {'subsample': 0.8, 'n_estimators': 100, 'max_depth': 3, 'learning_rate': 0.05, 'colsample_bytree': 0.8}
AUC: 0.9605 | F1: 0.9196

Relatório de Classificação:
              precision    recall  f1-score   support

           0       0.94      0.90      0.92       447
           1       0.90      0.94      0.92       423

    accuracy                           0.92       870
   macro avg       0.92      0.92      0.92       870
weighted avg       0.92      0.92      0.92       870


Matriz de Confusão:
[[404  43]
 [ 27 396]]

=== Resumo dos Modelos ===

Random Forest:
AUC: 0.9541 | F1: 0.9184

Gradient Boosting:
AUC: 0.9563 | F1: 0.9196

KNN:
AUC: 0.9428 | F1: 0.9184

XGBoost:
AUC: 0.9605 | F1: 0.9196

Resultados com os paramêtros “TUNADOS”

import pandas as pd
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.neighbors import KNeighborsClassifier
from xgboost import XGBClassifier
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
import seaborn as sns

# ------------------------------------------------------------
# 1) Concatenar treino e teste para projeção PCA
# ------------------------------------------------------------
features_scaled_df = pd.concat([X_train_scaled, X_test_scaled], axis=0).reset_index(drop=True)
y_full = pd.concat([y_train.reset_index(drop=True), y_test.reset_index(drop=True)], axis=0)

# ------------------------------------------------------------
# 2) Reajustar cada modelo com os melhores hiperparâmetros encontrados
# ------------------------------------------------------------
# Random Forest ajustado
rf_best = RandomForestClassifier(random_state=42, **results['Random Forest']['best_params'])
rf_best.fit(X_train_scaled, y_train)

# Gradient Boosting ajustado
gb_best = GradientBoostingClassifier(random_state=42, **results['Gradient Boosting']['best_params'])
gb_best.fit(X_train_scaled, y_train)

# KNN ajustado
knn_best = KNeighborsClassifier(**results['KNN']['best_params'])
knn_best.fit(X_train_scaled, y_train)

# XGBoost ajustado
xgb_best = XGBClassifier(use_label_encoder=False, eval_metric='logloss', random_state=42, **results['XGBoost']['best_params'])
xgb_best.fit(X_train_scaled, y_train)

# ------------------------------------------------------------
# 3) Projeção PCA em 2D de todo o conjunto escalado
# ------------------------------------------------------------
pca = PCA(n_components=2)
X_pca_full = pca.fit_transform(features_scaled_df)

# ------------------------------------------------------------
# 4) Predições de cada modelo ajustado para todo o conjunto escalado
# ------------------------------------------------------------
rf_pred_full  = rf_best.predict(features_scaled_df)
gb_pred_full  = gb_best.predict(features_scaled_df)
knn_pred_full = knn_best.predict(features_scaled_df)
xgb_pred_full = xgb_best.predict(features_scaled_df)

# ------------------------------------------------------------
# 5) Plot 2×2 mostrando a separação PCA para cada modelo ajustado
# ------------------------------------------------------------
fig, axes = plt.subplots(2, 2, figsize=(14, 12))
model_info = [
    ('Random Forest (tuned)',     rf_pred_full,  axes[0, 0]),
    ('Gradient Boosting (tuned)', gb_pred_full,  axes[0, 1]),
    ('KNN (tuned)',               knn_pred_full, axes[1, 0]),
    ('XGBoost (tuned)',           xgb_pred_full, axes[1, 1])
]

for name, preds, ax in model_info:
    sns.scatterplot(
        x=X_pca_full[:, 0],
        y=X_pca_full[:, 1],
        hue=preds,
        palette='Set1',
        alpha=0.7,
        edgecolor='k',
        ax=ax
    )
    ax.set_title(f"{name} – PCA Projection")
    ax.set_xlabel("Componente Principal 1")
    ax.set_ylabel("Componente Principal 2")
    ax.legend(title='Classe Predita', loc='upper right')
    ax.grid(True)

plt.tight_layout()
plt.show()
C:\Users\gabriel.cardoso\AppData\Local\Programs\Python\Python313\Lib\site-packages\xgboost\training.py:183: UserWarning:

[15:21:21] WARNING: C:\actions-runner\_work\xgboost\xgboost\src\learner.cc:738: 
Parameters: { "use_label_encoder" } are not used.

import numpy as np
import pandas as pd
from sklearn.metrics import (
    roc_auc_score,
    f1_score,
    classification_report,
    confusion_matrix,
    accuracy_score
)

# ------------------------------------------------------------
# 1) Obter predições e probabilidades de cada modelo tunado no conjunto de teste
# ------------------------------------------------------------
# Supondo que você tenha os modelos tunados rf_best, gb_best, knn_best e xgb_best
# treinados com X_train_scaled e y_train, e que X_test_scaled e y_test já existam.

rf_pred_tuned  = rf_best.predict(X_test_scaled)
rf_proba_tuned = rf_best.predict_proba(X_test_scaled)[:, 1]

gb_pred_tuned  = gb_best.predict(X_test_scaled)
gb_proba_tuned = gb_best.predict_proba(X_test_scaled)[:, 1]

knn_pred_tuned  = knn_best.predict(X_test_scaled)
knn_proba_tuned = knn_best.predict_proba(X_test_scaled)[:, 1]

xgb_pred_tuned  = xgb_best.predict(X_test_scaled)
xgb_proba_tuned = xgb_best.predict_proba(X_test_scaled)[:, 1]

# ------------------------------------------------------------
# 2) Função auxiliar para exibir métricas e matriz de confusão
# ------------------------------------------------------------
def exibir_metricas_e_matriz(nome, y_true, y_pred, y_proba):
    print(f"\n=== {nome} (tuned) ===")
    # AUC e F1
    auc_val = roc_auc_score(y_true, y_proba)
    f1_val  = f1_score(y_true, y_pred, average='weighted')
    print(f"AUC: {auc_val:.4f} | F1-Score: {f1_val:.4f}")
    # Relatório de classificação
    print("\nRelatório de Classificação:")
    print(classification_report(y_true, y_pred))
    # Matriz de Confusão
    print("Matriz de Confusão:")
    print(confusion_matrix(y_true, y_pred))

# ------------------------------------------------------------
# 3) Exibir resultados para cada modelo tunado
# ------------------------------------------------------------
exibir_metricas_e_matriz("Random Forest",     y_test, rf_pred_tuned,  rf_proba_tuned)
exibir_metricas_e_matriz("Gradient Boosting", y_test, gb_pred_tuned,  gb_proba_tuned)
exibir_metricas_e_matriz("KNN",               y_test, knn_pred_tuned, knn_proba_tuned)
exibir_metricas_e_matriz("XGBoost",           y_test, xgb_pred_tuned, xgb_proba_tuned)

# ------------------------------------------------------------
# 4) Tabela comparativa de AUC, F1-Score e Acurácia (modelos tunados)
# ------------------------------------------------------------
models_results_tuned = {
    'Random Forest':     (rf_pred_tuned,  rf_proba_tuned),
    'Gradient Boosting': (gb_pred_tuned,  gb_proba_tuned),
    'KNN':               (knn_pred_tuned, knn_proba_tuned),
    'XGBoost':           (xgb_pred_tuned, xgb_proba_tuned)
}

comparison_data_tuned = []
for nome, (pred, proba) in models_results_tuned.items():
    auc_val = roc_auc_score(y_test, proba)
    f1_val  = f1_score(y_test, pred, average='weighted')
    acc_val = accuracy_score(y_test, pred)
    comparison_data_tuned.append({
        'Modelo':    nome,
        'AUC':       round(auc_val, 6),
        'F1-Score':  round(f1_val, 6),
        'Acurácia':  round(acc_val, 6)
    })

comparison_df_tuned = pd.DataFrame(comparison_data_tuned)
print("\n=== Comparação Final (Modelos Tunados) ===")
print(comparison_df_tuned.to_string(index=False))

=== Random Forest (tuned) ===
AUC: 0.9541 | F1-Score: 0.9184

Relatório de Classificação:
              precision    recall  f1-score   support

           0       0.94      0.90      0.92       447
           1       0.90      0.94      0.92       423

    accuracy                           0.92       870
   macro avg       0.92      0.92      0.92       870
weighted avg       0.92      0.92      0.92       870

Matriz de Confusão:
[[403  44]
 [ 27 396]]

=== Gradient Boosting (tuned) ===
AUC: 0.9563 | F1-Score: 0.9196

Relatório de Classificação:
              precision    recall  f1-score   support

           0       0.94      0.90      0.92       447
           1       0.90      0.94      0.92       423

    accuracy                           0.92       870
   macro avg       0.92      0.92      0.92       870
weighted avg       0.92      0.92      0.92       870

Matriz de Confusão:
[[404  43]
 [ 27 396]]

=== KNN (tuned) ===
AUC: 0.9428 | F1-Score: 0.9184

Relatório de Classificação:
              precision    recall  f1-score   support

           0       0.94      0.90      0.92       447
           1       0.90      0.94      0.92       423

    accuracy                           0.92       870
   macro avg       0.92      0.92      0.92       870
weighted avg       0.92      0.92      0.92       870

Matriz de Confusão:
[[403  44]
 [ 27 396]]

=== XGBoost (tuned) ===
AUC: 0.9605 | F1-Score: 0.9196

Relatório de Classificação:
              precision    recall  f1-score   support

           0       0.94      0.90      0.92       447
           1       0.90      0.94      0.92       423

    accuracy                           0.92       870
   macro avg       0.92      0.92      0.92       870
weighted avg       0.92      0.92      0.92       870

Matriz de Confusão:
[[404  43]
 [ 27 396]]

=== Comparação Final (Modelos Tunados) ===
           Modelo      AUC  F1-Score  Acurácia
    Random Forest 0.954078  0.918404  0.918391
Gradient Boosting 0.956331  0.919554  0.919540
              KNN 0.942813  0.918404  0.918391
          XGBoost 0.960498  0.919554  0.919540
import matplotlib.pyplot as plt
from sklearn.metrics import roc_curve, auc

# Dicionário com os nomes dos modelos e suas probabilidades
modelos_tunados = {
    'Random Forest':     rf_proba_tuned,
    'Gradient Boosting': gb_proba_tuned,
    'KNN':               knn_proba_tuned,
    'XGBoost':           xgb_proba_tuned
}

plt.figure(figsize=(10, 7))
for nome, proba in modelos_tunados.items():
    fpr, tpr, _ = roc_curve(y_test, proba)
    roc_auc = auc(fpr, tpr)
    plt.plot(fpr, tpr, lw=2, label=f'{nome} (AUC = {roc_auc:.4f})')

# Linha de referência
plt.plot([0, 1], [0, 1], color='gray', linestyle='--')

plt.title('Curva ROC - Modelos Tunados')
plt.xlabel('Taxa de Falsos Positivos (FPR)')
plt.ylabel('Taxa de Verdadeiros Positivos (TPR)')
plt.legend(loc='lower right')
plt.grid(True)
plt.tight_layout()
plt.show()

Conclusão

O projeto demonstrou sucesso na classificação de traços de personalidade ,especificamente, distinguir entre indivíduos introvertidos e extrovertidos , a partir de padrões de comportamento social. A abordagem envolveu um pipeline robusto, com pré-processamento criterioso, engenharia de atributos e balanceamento das classes utilizando SMOTE.

Modelos supervisionados de alta performance foram ajustados com técnicas avançadas de otimização de hiperparâmetros, e os resultados obtidos reforçam a eficácia da estratégia adotada. Dentre os modelos testados, o XGBoost se destacou com a maior AUC (0.9605) e F1-Score (0.9196), seguido de perto por Gradient Boosting e Random Forest, que também apresentaram desempenhos consistentes e equilibrados.

Todos os modelos tunados atingiram acurácia acima de 91%, além de precisão e recall balanceados entre as classes, evidenciando a capacidade dos algoritmos em capturar padrões relevantes sem viés acentuado. A matriz de confusão de cada modelo também confirma essa consistência, com baixos índices de falsos positivos e negativos.

Portanto, conclui-se que a aplicação de técnicas de ensemble learning, combinadas com estratégias adequadas de preparação dos dados, foi altamente eficaz na tarefa proposta. O pipeline criado é robusto e replicável, com potencial para ser aplicado em outros cenários relacionados à análise de perfil comportamental. Como continuidade, futuras melhorias poderiam envolver o uso de embeddings de linguagem natural para análise de texto (caso existam descrições) ou a implementação de modelos de deep learning.