Skip to content

Árvore de Decisão

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 árvore de decisão.

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:41.827685 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 dummy para que se atinja um melhor desempenho do algoritmo.

2025-12-04T02:28:41.914637 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 dummy 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:41.948305 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 já serem numéricos estes já estão adequados para o modelo.

2025-12-04T02:28:41.987476 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 e não apresenta binariedade, a técnica correta para o tratamento desta coluna será o Label Enconding, transformando estes valores textuais em valores númericos.

2025-12-04T02:28:42.066038 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 como esta coluna não apresentar binariedade, deverá ser utilizada a técnica de Label Enconding, transformando estes valores textuais e nulos em valores númericos.

2025-12-04T02:28:42.107753 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 já serem numéricos estes já estão adequados para o modelo.

2025-12-04T02:28:42.155776 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 já serem numéricos estes já estão adequados para o modelo.

2025-12-04T02:28:42.250393 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, deverá ser utilizada a técnica de Label Enconding, transformando estes valores textuais em valores númericos.

2025-12-04T02:28:42.341047 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:42.451627 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 major race gmat work_exp work_industry admission
1 3.33 1 0 720 5 9 Admit
1 3.12 2 2 580 5 9 Refused
0 3.32 2 5 640 4 1 Refused
1 3 2 4 590 6 8 Refused
0 3.35 0 0 690 6 13 Admit
1 3.26 1 5 690 5 1 Admit
1 3.1 2 0 630 4 10 Refused
1 3.49 2 4 670 4 1 Waitlist
1 3.31 1 0 610 5 0 Refused
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.preprocessing import LabelEncoder

label_encoder = LabelEncoder()


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 das colunas em texto
df["race"] = label_encoder.fit_transform(df["race"])
df["gender"] = label_encoder.fit_transform(df["gender"])
df["major"] = label_encoder.fit_transform(df["major"])
df["work_industry"] = label_encoder.fit_transform(df["work_industry"])

print(df.sample(frac=.0015).to_markdown(index=False))
application_id gender international gpa major race gmat work_exp work_industry admission
5365 Female True 3.41 Business nan 660 6 Consulting Admit
4112 Male False 2.87 Humanities White 580 6 Technology nan
1050 Female False 3.2 Business White 580 6 Investment Banking nan
2665 Male False 3.07 STEM White 600 5 Consulting nan
3949 Female False 3.5 Humanities Asian 720 5 Financial Services Admit
1944 Female False 3.32 STEM White 720 2 Media/Entertainment Admit
4875 Male True 3.03 STEM nan 620 7 Consulting nan
553 Female False 3.35 Humanities White 640 5 Investment Banking Waitlist
1080 Female False 3.06 Business Hispanic 730 5 Nonprofit/Gov 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 matplotlib.pyplot as plt
import pandas as pd
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split

label_encoder = LabelEncoder()

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 das colunas em texto
df["race"] = label_encoder.fit_transform(df["race"])
df["gender"] = label_encoder.fit_transform(df["gender"])
df["major"] = label_encoder.fit_transform(df["major"])
df["work_industry"] = label_encoder.fit_transform(df["work_industry"])

#Separar em vairaveis indenpendetes e dependente
x = df[["gender", "gpa", "major", "race", "gmat", "work_exp", "work_industry"]]
y = df["admission"]

#Separar em teste e validação
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3, random_state=27, stratify=y)

Treinamento da Árvore

Precisão da Validação: 0.7784
Importância das Features:

Feature Importância
1 gpa 0.284064
4 gmat 0.281837
6 work_industry 0.158193
5 work_exp 0.119838
2 major 0.069255
3 race 0.069199
0 gender 0.017615
2025-12-04T02:28:43.721498 image/svg+xml Matplotlib v3.10.7, https://matplotlib.org/

import matplotlib.pyplot as plt
import pandas as pd
from io import StringIO
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn import tree

label_encoder = LabelEncoder()


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 das colunas em texto
df["race"] = label_encoder.fit_transform(df["race"])
df["gender"] = label_encoder.fit_transform(df["gender"])
df["major"] = label_encoder.fit_transform(df["major"])
df["work_industry"] = label_encoder.fit_transform(df["work_industry"])

#Separar em vairaveis indenpendetes e dependente
x = df[["gender", "gpa", "major", "race", "gmat", "work_exp", "work_industry"]]
y = df["admission"]

#Separar em teste e validação
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3, random_state=42, stratify=y)

# Criar e treinar o modelo de árvore de decisão
classifier = tree.DecisionTreeClassifier()
classifier.fit(x_train, y_train)

# Avaliar o modelo
y_pred = classifier.predict(x_test)
accuracy = accuracy_score(y_test, y_pred)
print(f"Precisão da Validação: {accuracy:.4f}")

feature_importance = pd.DataFrame({
    'Feature': classifier.feature_names_in_,
    'Importância': classifier.feature_importances_
})
print("<br>Importância das Features:")
print(feature_importance.sort_values(by='Importância', ascending=False).to_html())

plt.figure(figsize=(20, 10))
tree.plot_tree(classifier, max_depth=5, fontsize=10)

# Para imprimir na página HTML
buffer = StringIO()
plt.savefig(buffer, format="svg")
print(buffer.getvalue())

Avaliação do Modelo

Com este treinamento o modelo apresenta 78.48% de precisão, número satisfatório para um modelo de classificação real, e as colunas mais importantes em sua tomada de deicisão são as ponutações gpa e gmat com 29.4% e 27.7% de importância, respectivamente, e a coluna com menor relevancia para o modelo é a gender, com 1.7% de importância.

Entretando utilizar mais dados no treinamento do modelo poderia melhorara sua precisão. Logo, para compravar esta hipótese o modelo será treinado novamente com 80% da base de dados original para treinamento.

Retreinamento

Precisão da Validação: 0.7764
Importância das Features:

Feature Importância
1 gpa 0.335151
4 gmat 0.294273
6 work_industry 0.128850
3 race 0.089507
5 work_exp 0.083052
2 major 0.052624
0 gender 0.016543
2025-12-04T02:28:44.556834 image/svg+xml Matplotlib v3.10.7, https://matplotlib.org/

import matplotlib.pyplot as plt
import pandas as pd
from io import StringIO
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn import tree

label_encoder = LabelEncoder()


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 das colunas em texto
df["race"] = label_encoder.fit_transform(df["race"])
df["gender"] = label_encoder.fit_transform(df["gender"])
df["major"] = label_encoder.fit_transform(df["major"])
df["work_industry"] = label_encoder.fit_transform(df["work_industry"])

#Separar em vairaveis indenpendetes e dependente
x = df[["gender", "gpa", "major", "race", "gmat", "work_exp", "work_industry"]]
y = 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, stratify=y)

# Criar e treinar o modelo de árvore de decisão
classifier = tree.DecisionTreeClassifier()
classifier.fit(x_train, y_train)

# Avaliar o modelo
y_pred = classifier.predict(x_test)
accuracy = accuracy_score(y_test, y_pred)
print(f"Precisão da Validação: {accuracy:.4f}")

feature_importance = pd.DataFrame({
    'Feature': classifier.feature_names_in_,
    'Importância': classifier.feature_importances_
})
print("<br>Importância das Features:")
print(feature_importance.sort_values(by='Importância', ascending=False).to_html())

plt.figure(figsize=(20, 10))
tree.plot_tree(classifier, max_depth=5, fontsize=10)

# Para imprimir na página HTML
buffer = StringIO()
plt.savefig(buffer, format="svg")
print(buffer.getvalue())

Avaliação do novo modelo

Com este retreinamento a hipótese anterior é rejeitada, pois ao utilizar 80% da base para treinamento a precisão geral do modelo caiu para 77.89%. Entretanto, as métricas de gpa e gmat continuaram sendo as mais relevantes, comprovando sua importância para o modelo.

Conclusão

Ao fim deste roteiro nota-se que as colunas não precisam estar normalizadas para que se treine uma árvore de decisão, aumentar os dados de treinamento do modelo, em detrimento dos dados de teste, pode prejudicar a precisão geral do mesmo e que grande parte do tempo de trabalho do cientista de dados é a análise e limpeza da base de dados original.