Skip to content

Classificação de Estresse Acadêmico com KNN

Exploração dos Dados

A base possui 280 registros e 9 colunas.

Estágio acadêmico: Refere-se ao nível acadêmico do respondente (graduação, ensino médio, pós-graduação).

Pressão dos colegas: O quanto o aluno se sente pressionado pelos colegas em uma escala de 1 a 5.

Pressão acadêmica da família: O quanto o aluno se sente pressionado pela família em uma escala de 1 a 5.

Ambiente de estudo: Como o aluno classifica o ambiente onde estuda (categorias como Peaceful, Noisy, Disrupted).

Estratégia de enfrentamento: Como o aluno enfrenta o estresse (ex.: Analisar a situação com inteligência, Colapso emocional, Apoio de amigos/família).

Maus hábitos: Indica se o respondente possui hábitos nocivos, como fumar ou beber (Sim/Não/Prefiro não responder).

Competição acadêmica: Grau de competição acadêmica percebida (escala de 1 a 5).

Índice de estresse: Nível de estresse acadêmico (escala de 1 a 5).

AcademicStage PeerPressure HomePressure StudyEnv Strategy BadHabits AcademicComp Stress
undergraduate 4 5 Noisy Analyze the situation and handle it with intellect No 3 5
undergraduate 3 4 Peaceful Analyze the situation and handle it with intellect No 3 3
undergraduate 1 1 Peaceful Social support (friends, family) No 2 4
undergraduate 3 2 Peaceful Analyze the situation and handle it with intellect No 4 3
undergraduate 3 3 Peaceful Analyze the situation and handle it with intellect No 4 5

Pré-processamento

Remoção de colunas irrelevantes

A coluna Timestamp foi removida por não agregar informações para a previsão.

Variável-alvo

A variável alvo é Stress. Por problemas na acuracia do modelo e após alguns testes, decidi que ao invés de utilizar 5 grupos, um para cada tipo de stress, dividi em apenas 2, onde 1 o nivel é abaixo de 3 e 0, onde o nível é 3 ou maior

Tratamento de valores ausentes

Foi identificado apenas um valor nulo em StudyEnv (ambiente de estudo), que foi preenchido com o valor mais frequente (Peaceful).

Codificação de variáveis categóricas

As colunas AcademicStage, StudyEnv, Strategy e BadHabits são categóricas e foram convertidas para valores numéricos utilizando Label Encoding.

Features e target

features (X): estágio acadêmico, pressão dos colegas, pressão da família, ambiente de estudo, estratégia de enfrentamento, hábitos nocivos, nível de competição acadêmica.

target (y): índice de estresse acadêmico.


Divisão dos Dados

Os dados foram divididos de forma estratificada em 80% treino e 20% teste, garantindo que as proporções entre as classes de estresse fossem preservadas.


Treinamento do Modelo

Accuracy: 0.82
Feature Importances (Permutation):

Feature Importance Std
AcademicComp 0.097024 0.040111
HomePressure 0.040476 0.032581
AcademicStage 0.036905 0.020585
BadHabits 0.016667 0.034173
PeerPressure 0.011905 0.025394
StudyEnv 0.002381 0.019956
Strategy -0.001786 0.016846
2025-11-18T23:24:50.899126 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, classification_report, confusion_matrix
import seaborn as sns
import pandas as pd
from sklearn.preprocessing import LabelEncoder
from sklearn.inspection import permutation_importance
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
from itertools import cycle

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

# Preprocess the data
def preprocess(df: pd.DataFrame) -> pd.DataFrame:
    # remocao da coluna Timestamp
    df = df.drop(columns=['Timestamp'])

    # Tratamento de missing values
    ## 'Study Environment' tem 1 valor ausente -> preenchid com a moda (valor mais frequente)
    df['StudyEnv'] = df['StudyEnv'].fillna(df['StudyEnv'].mode().iloc[0])

    #conversao de variaveis categoricas
    label_encoder = LabelEncoder()
    df['AcademicStage'] = label_encoder.fit_transform(df['AcademicStage'])
    df['StudyEnv'] = label_encoder.fit_transform(df['StudyEnv'])
    df['Strategy'] = label_encoder.fit_transform(df['Strategy'])
    df['BadHabits'] = label_encoder.fit_transform(df['BadHabits'])

    # Selecao de features
    features = [
        'AcademicStage',                       
        'PeerPressure',                       
        'HomePressure',    
        'StudyEnv',                            
        'Strategy',                      
        'BadHabits',
        'AcademicComp' 
    ]

    return df[features]


# Carregar base
df = pd.read_csv('https://raw.githubusercontent.com/tigasparzin/Machine-Learning/refs/heads/main/data/StressExp.csv')

d = preprocess(df.copy())

X = d[['AcademicStage','PeerPressure',                       
        'HomePressure',    
        'StudyEnv',                            
        'Strategy',                      
        'BadHabits',
        'AcademicComp']]
# alvo binário: baixo (<3) e alto (>=3)
y = (df['Stress'] >= 3).map({False: 1, True: 0})

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# Train KNN model
knn = KNeighborsClassifier(n_neighbors=3)
knn.fit(X_train, y_train)
predictions = knn.predict(X_test)
print(f"Accuracy: {accuracy_score(y_test, predictions):.2f}")


r = permutation_importance(
    knn,                  
    X_test,               
    y_test,               
    n_repeats=30,         
    random_state=42,
    scoring='accuracy'    
)


feature_importance = pd.DataFrame({
    'Feature': X.columns,
    'Importance': r.importances_mean,
    'Std': r.importances_std
})

report_dict = classification_report(y_test, predictions, output_dict=True)
report_df = pd.DataFrame(report_dict).transpose()

cm = confusion_matrix(y_test, predictions)
labels = knn.classes_
cm_df = pd.DataFrame(cm, index=labels, columns=labels)

# ordenar e mostrar (HTML igual ao seu exemplo)
feature_importance = feature_importance.sort_values(by='Importance', ascending=False)
print("<br>Feature Importances (Permutation):")
print(feature_importance.to_html(index=False))

# print("<h3>Relatório de Classificação:</h3>")
# print(report_df.to_html(classes="table table-bordered table-striped", border=0))

# Escalar features
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# Reduzir para 2 dimensões (apenas para visualização)
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)

# Split train/test
X_train, X_test, y_train, y_test = train_test_split(X_pca, y, test_size=0.3, random_state=42)

# Treinar KNN
knn = KNeighborsClassifier(n_neighbors=3)
knn.fit(X_train, y_train)
predictions = knn.predict(X_test)


# Visualize decision boundary
h = 0.02  # Step size in mesh
x_min, x_max = X_pca[:, 0].min() - 1, X_pca[:, 0].max() + 1
y_min, y_max = X_pca[:, 1].min() - 1, X_pca[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))

Z = knn.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)

plt.contourf(xx, yy, Z, cmap=plt.cm.RdYlBu, alpha=0.3)
sns.scatterplot(x=X_pca[:, 0], y=X_pca[:, 1], hue=y, style=y, palette="deep", s=100)
plt.xlabel("Feature 1")
plt.ylabel("Feature 2")
plt.title("KNN Decision Boundary (k=3)")

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

Avaliação do Modelo

Accuracy: 0.82 2025-11-18T23:24:51.076311 image/svg+xml Matplotlib v3.10.7, https://matplotlib.org/

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from io import StringIO
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import LabelEncoder
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay

# carregar os dados
df = pd.read_csv('https://raw.githubusercontent.com/tigasparzin/Machine-Learning/refs/heads/main/data/StressExp.csv')

# Preprocess the data
def preprocess(df: pd.DataFrame) -> pd.DataFrame:
    # remocao da coluna Timestamp
    df = df.drop(columns=['Timestamp'])

    # Tratamento de missing values
    ## 'Study Environment' tem 1 valor ausente -> preenchid com a moda (valor mais frequente)
    df['StudyEnv'] = df['StudyEnv'].fillna(df['StudyEnv'].mode().iloc[0])

    #conversao de variaveis categoricas
    label_encoder = LabelEncoder()
    df['AcademicStage'] = label_encoder.fit_transform(df['AcademicStage'])
    df['StudyEnv'] = label_encoder.fit_transform(df['StudyEnv'])
    df['Strategy'] = label_encoder.fit_transform(df['Strategy'])
    df['BadHabits'] = label_encoder.fit_transform(df['BadHabits'])

    # Selecao de features
    features = [
        'AcademicStage',                       
        'PeerPressure',                       
        'HomePressure',    
        'StudyEnv',                            
        'Strategy',                      
        'BadHabits',
        'AcademicComp' 
    ]

    return df[features]


d = preprocess(df.copy())

X = d[['AcademicStage','PeerPressure',                       
        'HomePressure',    
        'StudyEnv',                            
        'Strategy',                      
        'BadHabits',
        'AcademicComp']]
# alvo binário: baixo (<3) e alto (>=3)
y = (df['Stress'] >= 3).map({False: 1, True: 0})

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# Train KNN model
knn = KNeighborsClassifier(n_neighbors=3)
knn.fit(X_train, y_train)
predictions = knn.predict(X_test)
print(f"Accuracy: {accuracy_score(y_test, predictions):.2f}")

# gerar matriz de confusão
labels = np.sort(np.unique(np.concatenate([y_test, predictions])))
cm = confusion_matrix(y_test, predictions, labels=labels)

# plotar matriz de confusão
fig, ax = plt.subplots(figsize=(8, 6))
disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=labels)
disp.plot(ax=ax, cmap=plt.cm.Blues, values_format="d", colorbar=False)

ax.set_title("Matriz de Confusão - KNN")
ax.set_xlabel("Previsto")
ax.set_ylabel("Real")

# exportar para SVG
buffer = StringIO()
plt.savefig(buffer, format="svg", transparent=True, bbox_inches="tight")
svg_text = buffer.getvalue()
print(svg_text)

plt.close(fig)

Sobre binarização no alvo

Durante os primeiros modelos de teste, com os 5 niveis de estresse, estava tendo resultados com acuracias como 52% e após mudanças no modelo para tentar aumentar esse valor, chegando até no maximo 65%, então percebi que o erro poderia estar, na quantidade, assim tive a ideia de classificar os niveis de estresse como altos (=< 3) e baixos(< 3), assim chegando em uma acuracia de 82%.

Conclusão

O uso do KNN nesta base evidenciou pontos importantes para o aprendizado:

  • O desbalanceamento das classes afeta a performance: níveis baixos de estresse (1 e 2) são pouco representados, o que dificulta acertos nessas classes.

  • O modelo se mostrou sensível à escolha das variáveis de entrada e ao valor de k. Testes com diferentes combinações podem alterar significativamente a acurácia.

  • A normalização/standardização das variáveis numéricas é fundamental para que todas as features tenham peso similar na distância euclidiana usada pelo KNN.

  • Técnicas como binarização do alvo (baixo vs. alto estresse) podem ser boas alternativas dependendo do objetivo da análise.

Assim, o projeto reforça a importância do pré-processamento cuidadoso, da atenção ao balanceamento de classes e da análise crítica dos resultados para que o modelo seja útil de acordo com o problema real.