Skip to content

Random Forest

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 Random Tree.

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:33:57.911220 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:33:57.990798 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:33:58.022763 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:33:58.058950 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:33:58.133917 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:33:58.172942 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:33:58.217872 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:33:58.303596 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:33:58.391013 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:33:58.497703 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 2.99 0 5 640 5 1 Refused
1 3.38 0 4 680 4 13 Refused
1 3.32 1 1 660 5 1 Refused
1 3 1 0 610 4 5 Refused
0 3.32 2 1 670 4 8 Refused
1 3.04 2 4 600 3 1 Refused
0 3.15 2 5 580 6 1 Refused
0 3.07 2 5 650 5 1 Refused
1 3.31 2 3 630 6 9 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
5193 Male True 3.24 Business nan 630 5 Nonprofit/Gov nan
3152 Male True 3.41 Humanities nan 770 5 Investment Management nan
288 Male False 3.14 Business Hispanic 580 2 PE/VC nan
3619 Male False 3.34 Business Other 650 5 Other nan
3925 Female False 3.16 Business White 670 5 PE/VC nan
2946 Male True 3.01 Humanities nan 570 5 Consulting nan
4248 Male False 3.42 STEM Hispanic 690 4 Health Care nan
5474 Female False 3.53 Business Black 650 5 Other nan
191 Female False 3.32 Humanities White 730 5 Nonprofit/Gov Admit

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

Accuracy: 0.8386
Importância das Features:

Feature Importância
4 gmat 0.519098
1 gpa 0.322064
0 gender 0.064424
3 race 0.035490
6 work_industry 0.028160
5 work_exp 0.021203
2 major 0.009562

import matplotlib.pyplot as plt
import pandas as pd
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.ensemble import RandomForestClassifier

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)

# Initialize and train the model
rf = RandomForestClassifier(n_estimators=100,  # Number of trees
                            max_depth=5,       # Max depth of trees
                            max_features='sqrt',  # Features per split
                            random_state=42)
rf.fit(X_train, y_train)

# Predict and evaluate
predictions = rf.predict(X_test)
print(f"Accuracy: {accuracy_score(y_test, predictions):.4f}")

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

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 gmat e gpa com 51.9% e 32.2% de importância, respectivamente, e a coluna com menor relevancia para o modelo é a major, com 0.9% de importância.

Conclusão

O processo de treinamento da Random Forest consistiu em preparar os dados (remoção de application_id, codificações e alinhamento dos tipos), separar a base em 70% treino e 30% validação com estratificação pela coluna admission, e então ajustar um comitê de árvores de decisão: cada árvore foi treinada em bootstraps do conjunto de treino e, a cada divisão, considerou apenas um subconjunto aleatório de variáveis, o que reduz correlação entre árvores e estabiliza o erro. A previsão final resulta do voto majoritário entre as árvores. A avaliação do modelo obteve 78,48% de acurácia, e as importâncias de atributos, calculadas pelo ganho médio de impureza ao longo das divisões, indicaram GMAT (51,9%) e GPA (32,2%) como os preditores mais influentes, enquanto major apresentou baixa relevância (0,9%).