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.
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.
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.
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.
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.
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").
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.
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.
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.
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.
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.