Skip to content

KNN

Objetivo

O objetivo geral deste roteiro é utilizar as bibliotecas pandas, numpy, matplotlib e scikit-learn, além de uma base escolhida no Kagle, para treinar e avaliar um algoritmo de K-Nearest Neighbors (KNN).

Base de Dados

A base de dados escolhida para a realização deste roteiro foi a MBA Admission Dataset. Esta base possui 6194 linhas e 10 colunas, incluido uma coluna de ID da aplicação e uma coluna de status da admissão, esta é a váriavel dependente que será objeto da classificação.

Análise da Base

A seguir foi feita uma análise do significado e composição de cada coluna presente na base com a finalidade de indentificar possíveis problemas á serem tradados posteriormente.

Esta coluna é composta pelos ID's das aplicações realizadas, ou seja trata-se de um valor numérico lógico, único a cada aplicação, desta forma pode-se afirmar que esta coluna não terá relevância para o algoritmo e deverá ser retirada da base para treinamento.

2025-12-04T02:28:46.610266 image/svg+xml Matplotlib v3.10.7, https://matplotlib.org/

Esta coluna é preenchida com o genêro do aplicante, contendo apenas valores textuais entre "male" e "female", não incluindo opções como "non-binary", "other" ou "prefer not to inform". Logo, estes dados, por serem textuais e apresentarem binariedade, deverão ser transformados em uma variável binária numérica para que se atinja um melhor desempenho do algoritmo.

2025-12-04T02:28:46.689759 image/svg+xml Matplotlib v3.10.7, https://matplotlib.org/

Esta coluna é preenchida com valores booleanos que classificam o aplicantente como "estrangeiro" ou "não-estrangeiro". Logo, estes dados, por serem textuais e apresentarem binariedade, deveriam ser transformados em uma variável binária numérica para que se atinja um melhor desempenho do algoritmo.

Entretanto, a classificação desta coluna tambem poder ser notada na coluna "race", pois todos os valores nulos presentes na posterior são unicamente referentes a alunos estrangeiros.

2025-12-04T02:28:46.721605 image/svg+xml Matplotlib v3.10.7, https://matplotlib.org/

Esta coluna representa a performance acadêmica prévia do aplicante, que é calculada a partir do histórico escolar. Neste as notas particulares de cada matéria podem variar de 0 á 4, 0 sendo a pior nota possível e 4 a maior. Neste caso os GPA's dos aplicantes variam entre 2.65 e 3.77, apresentando uma curva normal. Devido ao fato destes valores serem numéricos e a maioria das variáveis do modelo serem binárias ou dummies, esta deve ser padronizada para valores entre 0 e 1.

2025-12-04T02:28:46.759206 image/svg+xml Matplotlib v3.10.7, https://matplotlib.org/

Esta coluna representa em que curso o aplicante deseja entrar, podendo assumir um de três valores textuais: "Humanities", "STEM" e "Business". Neste caso, como a variavel é textual, não apresenta binariedade e não possui noção de escala (como em "ruim", "regular" e "bom"), a técnica correta para o tratamento desta coluna será o "One Hot", transformando-a em 2 variáveis dummies.

2025-12-04T02:28:46.835490 image/svg+xml Matplotlib v3.10.7, https://matplotlib.org/

Esta coluna representa a indentificação racial do aplicante, porém tambem há diversas linhas com valor nulo nesta coluna. Ao comparar o preenchimento desta coluna com as demais, percebe-se que o valor desta coluna so se apresenta nulo para estudantes estrangeiros, tornando a coluna "international" redundante.

Desta forma, para otimizar o modelo, devemos remover a coluna "international", prezando pela menor quantidade de colunas possível, e gerar dummies para cada valor registrado na coluna, pois esta não possui noção de escala (como em "ruim", "regular" e "bom").

2025-12-04T02:28:46.875864 image/svg+xml Matplotlib v3.10.7, https://matplotlib.org/

Esta coluna representa o desempenho do aplicante na prova de adimissão, variando de 570 á 780, porém estas notas não apresentam uma curva normal, pois há muitos registros de notas menores que a média a mais do que há registos de notas maiores que a média. Devido ao fato destes valores serem numéricos e a maioria das variáveis do modelo serem binárias ou dummies, esta deve ser padronizada para valores entre 0 e 1.

2025-12-04T02:28:46.923833 image/svg+xml Matplotlib v3.10.7, https://matplotlib.org/

Esta coluna representa o tempo de experiência prévia do aplicante no mercado, exibida em anos. Os valores podem variar de 1 á 9, apresentando uma curva normal. Devido ao fato destes valores serem numéricos e a maioria das variáveis do modelo serem binárias ou dummies, esta deve ser padronizada para valores entre 0 e 1.

2025-12-04T02:28:47.103490 image/svg+xml Matplotlib v3.10.7, https://matplotlib.org/

Esta coluna representa a área de experiência prévia do aplicante no mercado, podendo assumir, nesta base um de quatorze valores textuais. E como esta coluna não apresenta binariedade e não possui noção de escala (como em "ruim", "regular" e "bom"), a técnica correta para o tratamento desta coluna será o "One Hot", transformando-a em 13 variáveis dummies.

2025-12-04T02:28:47.194125 image/svg+xml Matplotlib v3.10.7, https://matplotlib.org/

Esta coluna apresenta valores em texto para os aplicantes admitos e na lista de espera, além de valores nulos para aqueles que não foram aceitos. Esta coluna é o objeto da classificação e portanto será separada das outras colunas da base, e os valores nulos deveram ser preenchidos.

2025-12-04T02:28:47.305339 image/svg+xml Matplotlib v3.10.7, https://matplotlib.org/

Pré-processamento

Esta secção visa preparar os dados para o treinamento da árvore de decisão, atendendo as observações e análises feitas no tópico anterior.

gender gpa gmat work_exp admission race_Black race_Hispanic race_Other race_White race_international major_Humanities major_STEM work_industry_Consulting work_industry_Energy work_industry_Financial Services work_industry_Health Care work_industry_Investment Banking work_industry_Investment Management work_industry_Media/Entertainment work_industry_Nonprofit/Gov work_industry_Other work_industry_PE/VC work_industry_Real Estate work_industry_Retail work_industry_Technology
1 1.44716 0.180703 1.92091 Refused False False False True False False True False False False False False False False False False True False False False
1 -0.928626 -1.03656 0.952244 Refused False False False False True False True False False False False False False False False False False True False False
1 -0.268685 0.992212 0.952244 Waitlist False False False False False True False True False False False False False False False False False False False False
0 1.97511 -0.427929 -0.0164207 Refused True False False False False True False False False False False False False False False False True False False False
1 0.787219 0.789334 -0.0164207 Admit False False False True False False True True False False False False False False False False False False False False
1 -2.18251 -0.630806 -1.95375 Refused False True False False False True False False False False False False True False False False False False False False
1 -0.400673 -0.0221743 0.952244 Refused True False False False False False True False False False False False False False False False True False False False
1 -1.52257 -0.833683 -0.0164207 Refused True False False False False True False False False False False False False False False False True False False False
1 -1.52257 -1.64519 1.92091 Refused False False False False True True False False False False False False False False False False False False False True
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from io import StringIO
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import LabelEncoder
from sklearn.inspection import permutation_importance
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score
import seaborn as sns

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

label_encoder = LabelEncoder()
scaler = StandardScaler()

df = pd.read_csv("./docs/base/MBA.csv")

#Excluir as conlunas não desejadas
df = df.drop(columns= ["application_id", "international"])

#Preencher os valores nulos da coluna "race"
df["race"] = df["race"].fillna("international")

#Preencher os valores nulos da coluna "admission"
df["admission"] = df["admission"].fillna("Refused")

#Label encoding da coluna em texto binária
df["gender"] = label_encoder.fit_transform(df["gender"])

#Escolonando as váriaveis continuas
df["gpa"] = scaler.fit_transform(df[["gpa"]])
df["gmat"] = scaler.fit_transform(df[["gmat"]])
df["work_exp"] = scaler.fit_transform(df[["work_exp"]])

#Gerando dummies das colunas em texto não binárias
df = pd.get_dummies(df,columns= ["race", "major", "work_industry"], drop_first=True)

print(df.sample(frac=.0015).to_markdown(index=False))
application_id gender international gpa major race gmat work_exp work_industry admission
1037 Male True 2.97 STEM nan 570 4 Nonprofit/Gov nan
4845 Female True 3.24 Business nan 600 5 Consulting nan
6025 Male True 3.15 Business nan 570 5 Investment Banking nan
2108 Male False 3.31 STEM White 680 7 Technology Admit
1553 Male True 3.25 STEM nan 670 8 Financial Services nan
3105 Male False 3.53 Business Black 620 4 CPG nan
344 Female False 3.18 STEM White 590 6 Nonprofit/Gov Admit
3974 Male True 3.4 Humanities nan 770 5 Technology Admit
3758 Male True 3.43 Humanities nan 690 6 Consulting nan

Divisão dos dados

Devido a composição da coluna de admission, a seperação dos dados deve ser feita com maior atenção. Caso esta separação fosse feita com aleatoriedade, haveria a possibilidade de que a base de treinamento tornar-se enviesada. Portanto, esta deve ser executada com proporcionalidade a composição da coluna alvo. Tendo em vista situações como esta o sickit-learn já implementou o sorteamento extratificado como a opção stratify no comando train_test_split().

Além disto para o treinamento foi utilizado uma separação arbitrária da base em 70% treinamento e 30% validação.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from io import StringIO
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import LabelEncoder
from sklearn.inspection import permutation_importance
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score
import seaborn as sns

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

label_encoder = LabelEncoder()
scaler = StandardScaler()

df = pd.read_csv("./docs/base/MBA.csv")

#Excluir as conlunas não desejadas
df = df.drop(columns= ["application_id", "international"])

#Preencher os valores nulos da coluna "race"
df["race"] = df["race"].fillna("international")

#Preencher os valores nulos da coluna "admission"
df["admission"] = df["admission"].fillna("Refused")

#Label encoding da coluna em texto binária
df["gender"] = label_encoder.fit_transform(df["gender"])

#Escolonando as váriaveis continuas
df["gpa"] = scaler.fit_transform(df[["gpa"]])
df["gmat"] = scaler.fit_transform(df[["gmat"]])
df["work_exp"] = scaler.fit_transform(df[["work_exp"]])

#Gerando dummies das colunas em texto não binárias
df = pd.get_dummies(df,columns= ["race", "major", "work_industry"], drop_first=True)

#Separar em vairaveis indenpendetes e dependente
X = df[["gender", "gpa", "major", "race", "gmat", "work_exp", "work_industry"]]
y = label_encoder.fit_transform(df["admission"])

#Separar em teste e validação
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

Treinamento do Modelo

A seguir, foi feito o treinamento do modelo utilizando os três vizinhos mais próximos para a classificação, ou seja k = 4, e foi utilizada a função permutation_importance para avaliar a importância das features no modelo.

Esta função realiza diversas iterações sequênciais com a base de teste. Em cada uma das iterações, uma coluna da base de teste tem seus valores embaralhados, e o modelo tenta classifica-lá. Com as classificações obtidas, esta função calcula a diferença na precisão. Desta forma medindo a importância de cada feature.

Accuracy: 0.79
Importância das Features:

Feature Importância
0 gmat 0.013785
1 work_industry_Health Care 0.003349
2 race_international 0.003253
3 work_industry_Technology 0.000993
4 work_industry_Real Estate 0.000694
5 work_industry_Nonprofit/Gov 0.000557
6 work_industry_Media/Entertainment -0.000065
7 work_industry_Retail -0.000137
8 work_industry_Investment Banking -0.000202
9 work_industry_Investment Management -0.000557
10 gender -0.000597
11 work_industry_PE/VC -0.000718
12 work_industry_Energy -0.000799
13 major_Humanities -0.000856
14 race_Other -0.000944
15 race_Black -0.001001
16 race_Hispanic -0.001316
17 work_industry_Financial Services -0.001937
18 work_industry_Other -0.002171
19 major_STEM -0.002373
20 race_White -0.003890
21 gpa -0.005093
22 work_industry_Consulting -0.009023
23 work_exp -0.011913

import numpy as np
import pandas as pd
from io import StringIO
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import LabelEncoder
from sklearn.inspection import permutation_importance
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score
import seaborn as sns

label_encoder = LabelEncoder()
scaler = StandardScaler()

df = pd.read_csv("./docs/base/MBA.csv")

#Excluir as conlunas não desejadas
df = df.drop(columns= ["application_id", "international"])

#Preencher os valores nulos da coluna "race"
df["race"] = df["race"].fillna("international")

#Preencher os valores nulos da coluna "admission"
df["admission"] = df["admission"].fillna("Refused")

#Label encoding da coluna em texto binária
df["gender"] = label_encoder.fit_transform(df["gender"])

#Escolonando as váriaveis continuas
df["gpa"] = scaler.fit_transform(df[["gpa"]])
df["gmat"] = scaler.fit_transform(df[["gmat"]])
df["work_exp"] = scaler.fit_transform(df[["work_exp"]])

#Gerando dummies das colunas em texto não binárias
df = pd.get_dummies(df,columns= ["race", "major", "work_industry"], drop_first=True)

#Separar em vairaveis indenpendetes e dependente
X = df.drop("admission", axis=1)
y = label_encoder.fit_transform(df["admission"])

#Separar em teste e validação
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# Treinar o modelo
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}")

# Calcular permutation importance
result = permutation_importance(
    knn, X_test, y_test,
    n_repeats=100,        # número de permutações (quanto mais, mais estável)
    random_state=42
)

# Ordenar importâncias
importances = result.importances_mean
indices = np.argsort(importances)[::-1]  # do mais importante ao menos

df_result = pd.DataFrame({
    "Feature": X.columns[indices],
    "Importância": importances[indices]
})

# Gerar o markdown da tabela
print("<br>Importância das Features:")
print(df_result.sort_values(by='Importância', ascending=False).to_html())

Avaliação do Modelo

Ao fim do treinamento o modelo apresentou 79% de precisão, valor satisfatório. Entretanto algumas features apresentaram valores negativos no teste de permutation_importance, o que indica que o modelo ficaria mais preciso caso esta variável não estivesse presente, porém por se tratar de um valor muito próximo a zero, este indicio pode ser apenas ruído estatístico.

Retreinamento

A seguir foi feito o retreinamento do modelo sem a coluna "work_exp" para testar a hipótese de que o modelo teria melhora em sua remoção.

Accuracy: 0.81
Importância das Features:

Feature Importância
0 gmat 0.030517
1 gpa 0.012873
2 gender 0.009201
3 race_international 0.008805
4 race_Black 0.008394
5 major_STEM 0.007199
6 race_White 0.005634
7 work_industry_Consulting 0.004487
8 work_industry_PE/VC 0.004431
9 work_industry_Financial Services 0.003979
10 work_industry_Nonprofit/Gov 0.003277
11 race_Hispanic 0.003107
12 major_Humanities 0.002994
13 work_industry_Technology 0.002405
14 work_industry_Other 0.001953
15 work_industry_Energy 0.001574
16 work_industry_Real Estate 0.001533
17 work_industry_Investment Banking 0.001251
18 race_Other 0.001017
19 work_industry_Media/Entertainment 0.000831
20 work_industry_Retail -0.000032
21 work_industry_Health Care -0.001768
22 work_industry_Investment Management -0.002147

import numpy as np
import pandas as pd
from io import StringIO
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import LabelEncoder
from sklearn.inspection import permutation_importance
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score
import seaborn as sns

label_encoder = LabelEncoder()
scaler = StandardScaler()

df = pd.read_csv("./docs/base/MBA.csv")

#Excluir as conlunas não desejadas
df = df.drop(columns= ["application_id", "international" , "work_exp"])

#Preencher os valores nulos da coluna "race"
df["race"] = df["race"].fillna("international")

#Preencher os valores nulos da coluna "admission"
df["admission"] = df["admission"].fillna("Refused")

#Label encoding da coluna em texto binária
df["gender"] = label_encoder.fit_transform(df["gender"])

#Escolonando as váriaveis continuas
df["gpa"] = scaler.fit_transform(df[["gpa"]])
df["gmat"] = scaler.fit_transform(df[["gmat"]])

#Gerando dummies das colunas em texto não binárias
df = pd.get_dummies(df,columns= ["race", "major", "work_industry"], drop_first=True)

#Separar em vairaveis indenpendetes e dependente
X = df.drop("admission", axis=1)
y = label_encoder.fit_transform(df["admission"])

#Separar em teste e validação
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# Treinar o modelo
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}")

# Calcular permutation importance
result = permutation_importance(
    knn, X_test, y_test,
    n_repeats=100,        # número de permutações (quanto mais, mais estável)
    random_state=42
)

# Ordenar importâncias
importances = result.importances_mean
indices = np.argsort(importances)[::-1]  # do mais importante ao menos

df_result = pd.DataFrame({
    "Feature": X.columns[indices],
    "Importância": importances[indices]
})

# Gerar o markdown da tabela
print("<br>Importância das Features:")
print(df_result.sort_values(by='Importância', ascending=False).to_html())
  • Análise

Após o retreinamento, o modelo apresentou aumento de precisão em dois pontos percentuais, portanto será realizado o 2° retreino, seguindo a mesma lógica, ou seja, removendo a variável com menor valor no teste de permutation_importance, desde que esta seja negativa.

A seguir foi feito o retreinamento do modelo sem as colunas "work_exp" e "work_industry_Investment Management" para testar a hipótese de que o modelo teria melhora em sua remoção.

Accuracy: 0.82
Importância das Features:

Feature Importância
0 gmat 0.036449
1 gpa 0.019580
2 race_Black 0.010807
3 work_industry_Consulting 0.010500
4 race_international 0.010291
5 gender 0.009968
6 race_White 0.009282
7 major_STEM 0.007789
8 work_industry_PE/VC 0.007667
9 major_Humanities 0.006021
10 work_industry_Nonprofit/Gov 0.005884
11 work_industry_Financial Services 0.004310
12 race_Hispanic 0.002785
13 work_industry_Investment Banking 0.002768
14 work_industry_Technology 0.001727
15 race_Other 0.001186
16 work_industry_Energy 0.000807
17 work_industry_Real Estate 0.000767
18 work_industry_Retail 0.000759
19 work_industry_Media/Entertainment 0.000040
20 work_industry_Health Care -0.000105
21 work_industry_Other -0.000904

import numpy as np
import pandas as pd
from io import StringIO
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import LabelEncoder
from sklearn.inspection import permutation_importance
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score
import seaborn as sns

label_encoder = LabelEncoder()
scaler = StandardScaler()

df = pd.read_csv("./docs/base/MBA.csv")

#Excluir as conlunas não desejadas
df = df.drop(columns= ["application_id", "international" , "work_exp"])

#Preencher os valores nulos da coluna "race"
df["race"] = df["race"].fillna("international")

#Preencher os valores nulos da coluna "admission"
df["admission"] = df["admission"].fillna("Refused")

#Label encoding da coluna em texto binária
df["gender"] = label_encoder.fit_transform(df["gender"])

#Escolonando as váriaveis continuas
df["gpa"] = scaler.fit_transform(df[["gpa"]])
df["gmat"] = scaler.fit_transform(df[["gmat"]])

#Gerando dummies das colunas em texto não binárias
df = pd.get_dummies(df,columns= ["race", "major", "work_industry"], drop_first=True)

#Removendo colunas prejudiciais
df = df.drop(columns= ["work_industry_Investment Management"])

#Separar em vairaveis indenpendetes e dependente
X = df.drop("admission", axis=1)
y = label_encoder.fit_transform(df["admission"])

#Separar em teste e validação
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# Treinar o modelo
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}")

# Calcular permutation importance
result = permutation_importance(
    knn, X_test, y_test,
    n_repeats=100,        # número de permutações (quanto mais, mais estável)
    random_state=42
)

# Ordenar importâncias
importances = result.importances_mean
indices = np.argsort(importances)[::-1]  # do mais importante ao menos

df_result = pd.DataFrame({
    "Feature": X.columns[indices],
    "Importância": importances[indices]
})

# Gerar o markdown da tabela
print("<br>Importância das Features:")
print(df_result.sort_values(by='Importância', ascending=False).to_html())
  • Análise

Após o retreinamento, o modelo apresentou aumento de precisão em um ponto percentual, portanto será realizado o 3° retreino, seguindo a mesma lógica, ou seja, removendo a variável com menor valor no teste de permutation_importance, desde que esta seja negativa.

A seguir foi feito o retreinamento do modelo sem as colunas "work_exp", "work_industry_Investment Management" e "work_industry_Other" para testar a hipótese de que o modelo teria melhora em sua remoção.

Accuracy: 0.82
Importância das Features:

Feature Importância
0 gmat 0.036893
1 gpa 0.020613
2 gender 0.012203
3 major_STEM 0.011768
4 race_Black 0.010856
5 race_White 0.008975
6 race_international 0.008967
7 major_Humanities 0.007684
8 work_industry_Nonprofit/Gov 0.005609
9 work_industry_Consulting 0.005165
10 work_industry_PE/VC 0.005157
11 work_industry_Technology 0.004673
12 work_industry_Financial Services 0.003422
13 race_Hispanic 0.002526
14 work_industry_Investment Banking 0.001727
15 work_industry_Real Estate 0.000960
16 work_industry_Energy 0.000823
17 race_Other 0.000299
18 work_industry_Retail -0.000032
19 work_industry_Media/Entertainment -0.000734
20 work_industry_Health Care -0.001598

import numpy as np
import pandas as pd
from io import StringIO
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import LabelEncoder
from sklearn.inspection import permutation_importance
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score
import seaborn as sns

label_encoder = LabelEncoder()
scaler = StandardScaler()

df = pd.read_csv("./docs/base/MBA.csv")

#Excluir as conlunas não desejadas
df = df.drop(columns= ["application_id", "international" , "work_exp"])

#Preencher os valores nulos da coluna "race"
df["race"] = df["race"].fillna("international")

#Preencher os valores nulos da coluna "admission"
df["admission"] = df["admission"].fillna("Refused")

#Label encoding da coluna em texto binária
df["gender"] = label_encoder.fit_transform(df["gender"])

#Escolonando as váriaveis continuas
df["gpa"] = scaler.fit_transform(df[["gpa"]])
df["gmat"] = scaler.fit_transform(df[["gmat"]])

#Gerando dummies das colunas em texto não binárias
df = pd.get_dummies(df,columns= ["race", "major", "work_industry"], drop_first=True)

#Removendo colunas prejudiciais
df = df.drop(columns= ["work_industry_Investment Management", "work_industry_Other"])

#Separar em vairaveis indenpendetes e dependente
X = df.drop("admission", axis=1)
y = label_encoder.fit_transform(df["admission"])

#Separar em teste e validação
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# Treinar o modelo
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}")

# Calcular permutation importance
result = permutation_importance(
    knn, X_test, y_test,
    n_repeats=100,        # número de permutações (quanto mais, mais estável)
    random_state=42
)

# Ordenar importâncias
importances = result.importances_mean
indices = np.argsort(importances)[::-1]  # do mais importante ao menos

df_result = pd.DataFrame({
    "Feature": X.columns[indices],
    "Importância": importances[indices]
})

# Gerar o markdown da tabela
print("<br>Importância das Features:")
print(df_result.sort_values(by='Importância', ascending=False).to_html())
  • Análise

Após o retreinamento, o modelo não apresentou variação de precisão, entretanto percebe-se um padrão. Toda vez que o modelo é retreinado alguma das variáveis dummmies relacianadas a coluna "work_industry" são estimadas como prejudicias ao modelo, logo o 4° retreino será realizado sem a variável "work_industry" por inteira.

A seguir foi feito o retreinamento do modelo sem as colunas "work_exp" e "work_industry" para testar a hipótese de que o modelo teria melhora em sua remoção.

Accuracy: 0.79
Importância das Features:

Feature Importância
0 gmat 0.011090
1 race_Hispanic 0.002510
2 race_Black 0.001606
3 gpa -0.000597
4 major_STEM -0.001872
5 race_Other -0.003228
6 race_White -0.003810
7 gender -0.004810
8 race_international -0.006053
9 major_Humanities -0.007764

import numpy as np
import pandas as pd
from io import StringIO
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import LabelEncoder
from sklearn.inspection import permutation_importance
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score
import seaborn as sns

label_encoder = LabelEncoder()
scaler = StandardScaler()

df = pd.read_csv("./docs/base/MBA.csv")

#Excluir as conlunas não desejadas
df = df.drop(columns= ["application_id", "international" , "work_exp", "work_industry"])

#Preencher os valores nulos da coluna "race"
df["race"] = df["race"].fillna("international")

#Preencher os valores nulos da coluna "admission"
df["admission"] = df["admission"].fillna("Refused")

#Label encoding da coluna em texto binária
df["gender"] = label_encoder.fit_transform(df["gender"])

#Escolonando as váriaveis continuas
df["gpa"] = scaler.fit_transform(df[["gpa"]])
df["gmat"] = scaler.fit_transform(df[["gmat"]])

#Gerando dummies das colunas em texto não binárias
df = pd.get_dummies(df,columns= ["race", "major"], drop_first=True)

#Separar em vairaveis indenpendetes e dependente
X = df.drop("admission", axis=1)
y = label_encoder.fit_transform(df["admission"])

#Separar em teste e validação
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# Treinar o modelo
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}")

# Calcular permutation importance
result = permutation_importance(
    knn, X_test, y_test,
    n_repeats=100,        # número de permutações (quanto mais, mais estável)
    random_state=42
)

# Ordenar importâncias
importances = result.importances_mean
indices = np.argsort(importances)[::-1]  # do mais importante ao menos

df_result = pd.DataFrame({
    "Feature": X.columns[indices],
    "Importância": importances[indices]
})

# Gerar o markdown da tabela
print("<br>Importância das Features:")
print(df_result.sort_values(by='Importância', ascending=False).to_html())
  • Análise

Após o retreinamento, o modelo apresentou queda de precisão em três pontos percentuais, logo conclui-se que parte dos valores da coluna "work_industry" são relevantes, portanto o 5° treino será realizado apenas removendo variáveis indicadas como prejudiciais.

A seguir foi feito o retreinamento do modelo sem as colunas "work_exp", "work_industry_Investment Management", "work_industry_Other", "work_industry_Health Care" e "work_industry_Media/Entertainment" para testar a hipótese de que o modelo teria melhora em sua remoção.

Accuracy: 0.82
Importância das Features:

Feature Importância
0 gmat 0.039290
1 gpa 0.021606
2 gender 0.014738
3 race_international 0.014237
4 race_Black 0.013043
5 major_Humanities 0.011872
6 major_STEM 0.011558
7 race_White 0.010856
8 work_industry_Consulting 0.006610
9 work_industry_Financial Services 0.005601
10 work_industry_PE/VC 0.004455
11 work_industry_Nonprofit/Gov 0.003697
12 race_Hispanic 0.002881
13 work_industry_Investment Banking 0.001251
14 race_Other 0.001227
15 work_industry_Real Estate 0.000920
16 work_industry_Technology 0.000081
17 work_industry_Retail 0.000000
18 work_industry_Energy -0.000040

import numpy as np
import pandas as pd
from io import StringIO
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import LabelEncoder
from sklearn.inspection import permutation_importance
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score
import seaborn as sns

label_encoder = LabelEncoder()
scaler = StandardScaler()

df = pd.read_csv("./docs/base/MBA.csv")

#Excluir as conlunas não desejadas
df = df.drop(columns= ["application_id", "international" , "work_exp"])

#Preencher os valores nulos da coluna "race"
df["race"] = df["race"].fillna("international")

#Preencher os valores nulos da coluna "admission"
df["admission"] = df["admission"].fillna("Refused")

#Label encoding da coluna em texto binária
df["gender"] = label_encoder.fit_transform(df["gender"])

#Escolonando as váriaveis continuas
df["gpa"] = scaler.fit_transform(df[["gpa"]])
df["gmat"] = scaler.fit_transform(df[["gmat"]])

#Gerando dummies das colunas em texto não binárias
df = pd.get_dummies(df,columns= ["race", "major", "work_industry"], drop_first=True)

#Removendo colunas prejudiciais
df = df.drop(columns= ["work_industry_Investment Management", "work_industry_Other", "work_industry_Health Care", "work_industry_Media/Entertainment"])

#Separar em vairaveis indenpendetes e dependente
X = df.drop("admission", axis=1)
y = label_encoder.fit_transform(df["admission"])

#Separar em teste e validação
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# Treinar o modelo
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}")

# Calcular permutation importance
result = permutation_importance(
    knn, X_test, y_test,
    n_repeats=100,        # número de permutações (quanto mais, mais estável)
    random_state=42
)

# Ordenar importâncias
importances = result.importances_mean
indices = np.argsort(importances)[::-1]  # do mais importante ao menos

df_result = pd.DataFrame({
    "Feature": X.columns[indices],
    "Importância": importances[indices]
})

# Gerar o markdown da tabela
print("<br>Importância das Features:")
print(df_result.sort_values(by='Importância', ascending=False).to_html())
  • Análise

Após o retreinamento, o modelo não apresentou variação de precisão, e o único valor negativo presente nesta avaliação é estatisticamente irrelevante, portanto a melhor precisão que este modelo pode obter com estas condições é de 82%.

Análise

Em relação ao modelo de árvore de decisão treinado anteriormente, este modelo requer maior cuidado com a limpeza e tratamento da base e é mais sensível á outliers, entretanto obteve melhor resultado em suas predições.