Projeto Random Forest
Neste projeto, vamos construir um modelo de Random Forest para classificar as casas da base Ames Housing em diferentes categorias de preço.
Nesta etapa, foi realizada a análise exploratória do dataset AmesHousing.csv, verificando as primeiras linhas, informações gerais, estatísticas descritivas, valores ausentes e visualização de algumas variáveis categóricas.
Primeiras 5 linhas do dataset:
Order PID MS SubClass MS Zoning Lot Frontage Lot Area ... Misc Val Mo Sold Yr Sold Sale Type Sale Condition SalePrice
0 1 526301100 20 RL 141.0 31770 ... 0 5 2010 WD Normal 215000
1 2 526350040 20 RH 80.0 11622 ... 0 6 2010 WD Normal 105000
2 3 526351010 20 RL 81.0 14267 ... 12500 6 2010 WD Normal 172000
3 4 526353030 20 RL 93.0 11160 ... 0 4 2010 WD Normal 244000
4 5 527105010 60 RL 74.0 13830 ... 0 3 2010 WD Normal 189900
[5 rows x 82 columns]
Informações do dataset:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 2930 entries, 0 to 2929
Data columns (total 82 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Order 2930 non-null int64
1 PID 2930 non-null int64
2 MS SubClass 2930 non-null int64
3 MS Zoning 2930 non-null object
4 Lot Frontage 2440 non-null float64
5 Lot Area 2930 non-null int64
6 Street 2930 non-null object
7 Alley 198 non-null object
8 Lot Shape 2930 non-null object
9 Land Contour 2930 non-null object
10 Utilities 2930 non-null object
11 Lot Config 2930 non-null object
12 Land Slope 2930 non-null object
13 Neighborhood 2930 non-null object
14 Condition 1 2930 non-null object
15 Condition 2 2930 non-null object
16 Bldg Type 2930 non-null object
17 House Style 2930 non-null object
18 Overall Qual 2930 non-null int64
19 Overall Cond 2930 non-null int64
20 Year Built 2930 non-null int64
21 Year Remod/Add 2930 non-null int64
22 Roof Style 2930 non-null object
23 Roof Matl 2930 non-null object
24 Exterior 1st 2930 non-null object
25 Exterior 2nd 2930 non-null object
26 Mas Vnr Type 1155 non-null object
27 Mas Vnr Area 2907 non-null float64
28 Exter Qual 2930 non-null object
29 Exter Cond 2930 non-null object
30 Foundation 2930 non-null object
31 Bsmt Qual 2850 non-null object
32 Bsmt Cond 2850 non-null object
33 Bsmt Exposure 2847 non-null object
34 BsmtFin Type 1 2850 non-null object
35 BsmtFin SF 1 2929 non-null float64
36 BsmtFin Type 2 2849 non-null object
37 BsmtFin SF 2 2929 non-null float64
38 Bsmt Unf SF 2929 non-null float64
39 Total Bsmt SF 2929 non-null float64
40 Heating 2930 non-null object
41 Heating QC 2930 non-null object
42 Central Air 2930 non-null object
43 Electrical 2929 non-null object
44 1st Flr SF 2930 non-null int64
45 2nd Flr SF 2930 non-null int64
46 Low Qual Fin SF 2930 non-null int64
47 Gr Liv Area 2930 non-null int64
48 Bsmt Full Bath 2928 non-null float64
49 Bsmt Half Bath 2928 non-null float64
50 Full Bath 2930 non-null int64
51 Half Bath 2930 non-null int64
52 Bedroom AbvGr 2930 non-null int64
53 Kitchen AbvGr 2930 non-null int64
54 Kitchen Qual 2930 non-null object
55 TotRms AbvGrd 2930 non-null int64
56 Functional 2930 non-null object
57 Fireplaces 2930 non-null int64
58 Fireplace Qu 1508 non-null object
59 Garage Type 2773 non-null object
60 Garage Yr Blt 2771 non-null float64
61 Garage Finish 2771 non-null object
62 Garage Cars 2929 non-null float64
63 Garage Area 2929 non-null float64
64 Garage Qual 2771 non-null object
65 Garage Cond 2771 non-null object
66 Paved Drive 2930 non-null object
67 Wood Deck SF 2930 non-null int64
68 Open Porch SF 2930 non-null int64
69 Enclosed Porch 2930 non-null int64
70 3Ssn Porch 2930 non-null int64
71 Screen Porch 2930 non-null int64
72 Pool Area 2930 non-null int64
73 Pool QC 13 non-null object
74 Fence 572 non-null object
75 Misc Feature 106 non-null object
76 Misc Val 2930 non-null int64
77 Mo Sold 2930 non-null int64
78 Yr Sold 2930 non-null int64
79 Sale Type 2930 non-null object
80 Sale Condition 2930 non-null object
81 SalePrice 2930 non-null int64
dtypes: float64(11), int64(28), object(43)
memory usage: 1.8+ MB
None
Estatísticas descritivas:
Order PID MS SubClass MS Zoning Lot Frontage ... Mo Sold Yr Sold Sale Type Sale Condition SalePrice
count 2930.00000 2.930000e+03 2930.000000 2930 2440.000000 ... 2930.000000 2930.000000 2930 2930 2930.000000
unique NaN NaN NaN 7 NaN ... NaN NaN 10 6 NaN
top NaN NaN NaN RL NaN ... NaN NaN WD Normal NaN
freq NaN NaN NaN 2273 NaN ... NaN NaN 2536 2413 NaN
mean 1465.50000 7.144645e+08 57.387372 NaN 69.224590 ... 6.216041 2007.790444 NaN NaN 180796.060068
std 845.96247 1.887308e+08 42.638025 NaN 23.365335 ... 2.714492 1.316613 NaN NaN 79886.692357
min 1.00000 5.263011e+08 20.000000 NaN 21.000000 ... 1.000000 2006.000000 NaN NaN 12789.000000
25% 733.25000 5.284770e+08 20.000000 NaN 58.000000 ... 4.000000 2007.000000 NaN NaN 129500.000000
50% 1465.50000 5.354536e+08 50.000000 NaN 68.000000 ... 6.000000 2008.000000 NaN NaN 160000.000000
75% 2197.75000 9.071811e+08 70.000000 NaN 80.000000 ... 8.000000 2009.000000 NaN NaN 213500.000000
max 2930.00000 1.007100e+09 190.000000 NaN 313.000000 ... 12.000000 2010.000000 NaN NaN 755000.000000
[11 rows x 82 columns]
Valores ausentes por coluna:
Order 0
PID 0
MS SubClass 0
MS Zoning 0
Lot Frontage 490
...
Mo Sold 0
Yr Sold 0
Sale Type 0
Sale Condition 0
SalePrice 0
Length: 82, dtype: int64
from matplotlib import patches
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from IPython.display import display
df = pd.read_csv('data/AmesHousing.csv')
print("Primeiras 5 linhas do dataset:")
print(df.head())
print("\nInformações do dataset:")
print(df.info())
print("\nEstatísticas descritivas:")
print(df.describe(include='all'))
colunas = df.columns
print("\nColunas do dataset:")
print(colunas)
nulos = df.isnull().sum()
print("\nValores ausentes por coluna:")
print(nulos.head(100).to_string())
plt.style.use('ggplot')
plt.figure(figsize=(15,10))
df.isnull().mean().sort_values(ascending=False).head(20).plot(kind='bar', color='salmon')
plt.title("Percentual de Valores Ausentes nas 20 Variáveis com Mais NAs")
plt.ylabel("% de valores faltantes")
plt.savefig('./docs/classes/arvore-de-decisao/img/valores_ausentes.png')
#variáveis categóricas
plt.figure(figsize=(25, 20))
plt.subplot(2, 2, 1)
sns.countplot(data=df, x='MS Zoning', palette='viridis', order=df['MS Zoning'].value_counts().index)
plt.title('Classificação de Zonas Residenciais')
plt.xticks(rotation=45)
plt.xlabel('Zona de Zoneamento')
plt.ylabel('Contagem')
plt.tight_layout()
plt.subplot(2, 2, 2)
df['Street'].value_counts().plot.pie(autopct='%1.1f%%', figsize=(6,6), colors=sns.color_palette('viridis', 5), labels=df['Street'].value_counts().index)
plt.title('Tipo de Rua')
plt.ylabel('') #remove o label do eixo Y
plt.tight_layout()
plt.subplot(2, 2, 3)
df['Neighborhood'].value_counts().head(5).plot(kind='bar', color=sns.color_palette('viridis', 5))
plt.title('Top 5 Vizinhanças')
plt.xticks(rotation=45)
plt.xlabel('Vizinhança')
plt.ylabel('Contagem')
plt.tight_layout()
plt.subplot(2, 2, 4)
sns.countplot(data=df, x='House Style', palette='viridis', order=df['House Style'].value_counts().index)
plt.xticks(rotation=45)
plt.title('Estilo da Casa')
plt.ylabel('Contagem')
plt.tight_layout()
plt.savefig('./docs/classes/arvore-de-decisao/img/categoricas.png')
#variáveis numéricas
plt.figure(figsize=(15, 10))
plt.subplot(2, 2, 1)
sns.histplot(df['SalePrice'], bins=30, kde=True, color='salmon')
plt.title("Distribuição do Preço de Venda")
plt.xlabel("Preço de Venda")
plt.ylabel("Frequência")
plt.tight_layout()
plt.subplot(2, 2, 2)
sns.histplot(df['Gr Liv Area'], bins=30, kde=True, color='salmon')
plt.title("Distribuição da Área Acima do Solo")
plt.xlabel("Área Acima do Solo (pés²)")
plt.ylabel("Frequência")
plt.tight_layout()
plt.subplot(2, 2, 3)
sns.histplot(df['Year Built'], bins=30, kde=True, color='salmon')
plt.title("Distribuição do Ano de Construção")
plt.xlabel("Ano de Construção")
plt.ylabel("Frequência")
plt.tight_layout()
plt.subplot(2, 2, 4)
sns.histplot(df['Total Bsmt SF'], bins=30, kde=True, color='salmon')
plt.title("Distribuição da Área do Porão")
plt.xlabel("Área do Porão (pés²)")
plt.ylabel("Frequência")
plt.tight_layout()
plt.savefig('./docs/classes/arvore-de-decisao/img/dist_geral_numericas.png')
plt.show()
plt.figure(figsize=(15, 10))
plt.subplot(2, 1, 1)
sns.boxplot(x='Neighborhood', y='SalePrice', palette='viridis', data=df)
plt.title("Preço por Bairro")
plt.xticks(rotation=90)
plt.subplot(2, 1, 2)
sns.boxplot(x='Overall Qual', y='SalePrice', data=df, palette='viridis')
plt.title("Qualidade Geral vs Preço de Venda")
plt.tight_layout()
plt.savefig('./docs/classes/arvore-de-decisao/img/boxplots.png')
corr_cols = ['Lot Area', 'Gr Liv Area', 'Total Bsmt SF', 'Garage Area', '1st Flr SF',
'2nd Flr SF', 'Full Bath', 'Half Bath', 'Bedroom AbvGr', 'TotRms AbvGrd',
'Overall Qual', 'Overall Cond', 'Year Built', 'Year Remod/Add']
plt.figure(figsize=(10,8))
sns.heatmap(df[corr_cols].corr(), annot=True, cmap='coolwarm', fmt=".2f")
plt.title("Correlação entre variáveis numéricas")
plt.savefig('./docs/classes/arvore-de-decisao/img/matriz_correlacao.png') Utilizamos a biblioteca Pandas para a análise de dados e a biblioteca Matplotlib.pyplot para fazer os gráficos para uma melhor visualização dos dados.
Logo após carregarmos a base de dados:
- base = pd.read_csv('caminho/para/a/base')
Utilizamos comandos para a análise exploratória da base:
-
base.head() -> sem a especificação de quantas linhas puxar, o head imprime as primeiras 5 linhas da sua base de dados.
-
base.info() -> mostra as colunas da base de dados e seus respectivos tipos.
-
base.describe(include='all') -> imprime as estatísticas descritivas das colunas da base de dados, como a frequência, a média, o desvio padrão e etc.
-
base.isnull().sum() -> soma todos os valores nulos por coluna.
Após finalizar esse processo com a análise exploratória, precisamos fazer a visualização da análise realizada. Por isso, utilizamos os seguintes comandos:
-
plt.style.use('ggplot') -> sendo plt a biblioteca do matplotlib, esse comando serve para escolhermos o estilo que
-
plt.figure(figsize=(15, 10)) -> cria uma nova figura para colocar os gráficos.
-
plt.subplot(2, 2, 1) -> este comando divide a figura em uma grade de 2 linhas por 2 colunas. O último parâmetro é a posição do gráfico.
-
base['nome_coluna'].value_counts().plot.pie(autopct='%1.1f%%', figsize=(6,6)) -> constrói um gráfico de pizza com a coluna escolhida da base. O comando autopct exibe as porcentagens em cada fatia.
-
plt.title('Título') -> intitula o gráfico.
-
plt.ylabel('') -> remove o label do eixo Y, comando utilizado apenas em gráficos de pizza
-
base['nome_coluna'].value_counts().head(5).plot(kind='bar') -> constrói um gráfico de barra, e com o comando head(5) irá aparecer apenas os 5 mais frequentes daquela coluna.
-
plt.xticks(rotation=45) -> rotaciona o gráfico de barra a 45° para uma melhor visualização.
-
plt.savefig('caminho/que/deseja/salvar') -> salva a figura completa que foi iniciada lá no início do código no caminho que foi apontado.
-
plt.show() -> exibe a figura na tela.
Gráficos gerados na análise exploratória
A partir deste gráfico, podemos observar que algumas variáveis possuem uma quantidade significativa de valores ausentes. Isso pode impactar a análise e o desempenho do modelo, sendo necessário considerar estratégias para lidar com esses dados faltantes, como imputação ou remoção dessas variáveis.
No gráfico acima, podemos observar a distribuição das variáveis categóricas selecionadas. Isso nos ajuda a entender a frequência de diferentes categorias dentro dessas variáveis, o que pode ser útil para identificar padrões ou tendências no conjunto de dados. Um exemplo disso é o gráfico de pizza que mostra a predominância de um tipo específico de rua, indicando que a maioria das propriedades está localizada em ruas pavimentadas. E o gráfico de classe de zona residencial, onde a maioria das propriedades está situada em zonas residenciais (RL: Residential Low Density).
No gráfico acima, podemos observar a distribuição das variáveis numéricas selecionadas. Isso nos ajuda a entender a frequência de diferentes valores dentro dessas variáveis, o que pode ser útil para identificar padrões ou tendências no conjunto de dados. Um exemplo disso é a distribuição do preço de venda, que apresenta uma assimetria à direita, indicando que a maioria das propriedades tem preços mais baixos, com algumas propriedades de alto valor.
Os boxplots acima ilustram a relação entre a qualidade geral das casas e o preço de venda. Podemos observar que, em geral, casas com melhor qualidade tendem a ter preços de venda mais altos. No entanto, também há uma variação significativa nos preços dentro de cada categoria de qualidade, indicando que outros fatores além da qualidade geral também influenciam o preço de venda. Além disso, temos um boxplot sobre o preço de venda e o bairro onde a casa está localizada. Podemos observar que certos bairros, como NoRidge e NridgHt, apresentam preços de venda significativamente mais altos em comparação com outros bairros. Isso sugere que a localização é um fator importante na determinação do valor das propriedades.
A matriz de correlação acima mostra a relação entre várias variáveis numéricas no conjunto de dados. Podemos observar que algumas variáveis, como "Gr Liv Area" (área de vida acima do solo) e "Overall Qual" (qualidade geral), têm uma correlação positiva forte com o preço de venda ("SalePrice"). Isso indica que casas com maior área de vida e melhor qualidade tendem a ter preços de venda mais altos. Outras variáveis, como "Lot Area" (área do lote) e "Garage Area" (área da garagem), também mostram correlações positivas, mas em menor grau. Essas informações podem ser úteis para identificar quais características das casas são mais influentes na determinação do preço de venda.
2. Pré-processamento
Como primeira fase do pré-processamento, precisamos tratar os valores ausentes. Como visto anteriormente, principalmente no gráfico gerado, temos muitos valores nulos a serem tratados. Abaixo estão listados a quantidade dos valores nulos em todas as variáveis:
- Lot Frontage (490 valores nulos),
- Mas Vnr Type (1775 valores nulos),
- Mas Vnr Area (23 valores nulos),
- Bsmt Qual (80 valores nulos),
- Bsmt Cond (80 valores nulos),
- Bsmt Exposure (83 valores nulos),
- BsmtFin Type 1 (80 valores nulos),
- BsmtFin SF 1 (1 valor nulo),
- BsmtFin Type 2 (81 valores nulos),
- BsmtFin SF 2 (1 valor nulo),
- Bsmt Unf SF (1 valor nulo),
- Total Bsmt SF (1 valor nulo),
- Electrical (1 valor nulo),
- Bsmt Full Bath (2 valores nulos),
- Bsmt Half Bath (2 valores nulos),
- Fireplace Qu (1422 valores nulos),
- Garage Type (157 valores nulos),
- Garage Yr Blt (159 valores nulos),
- Garage Finish (159 valores nulos),
- Garage Cars (1 valor nulo),
- Garage Area (1 valor nulo),
- Garage Qual (159 valores nulos),
- Garage Cond (159 valores nulos),
- Pool QC (2917 valores nulos),
- Fence (2358 valores nulos),
- Misc Feature (2824 valores nulos).
Para tratar esses valores nulos, utilizei as seguintes estratégias:
- Remoção das variáveis com muitos valores nulos (mais de 50% dos dados ausentes): Mas Vnr Type, Fireplace Qu, Pool QC, Fence e Misc Feature.
- Preenchimento dos valores nulos em variáveis numéricas com a mediana da coluna.
- Preenchimento dos valores nulos em variáveis categóricas com a moda da coluna.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier, plot_tree
df = pd.read_csv('data/AmesHousing.csv')
#tratamento de valores nulos
maiores_valores_nulos = ['Pool QC', 'Misc Feature', 'Alley', 'Fence', 'Fireplace Qu']
df = df.drop(columns=maiores_valores_nulos)
# Preenchimento de valores nulos em variáveis numéricas com a mediana
num_cols = df.select_dtypes(include=['float64', 'int64']).columns
for col in num_cols:
df[col].fillna(df[col].median(), inplace=True)
# Preenchimento de valores nulos em variáveis categóricas com a moda
cat_cols = df.select_dtypes(include=['object']).columns
for col in cat_cols:
df[col].fillna(df[col].mode()[0], inplace=True)
nulos = df.isnull().sum()
print("\nValores ausentes por coluna:")
print(nulos.head(100).to_string()) # Verificando se ainda há valores nulos Valores ausentes por coluna:
Order 0
PID 0
MS SubClass 0
MS Zoning 0
Lot Frontage 0
Lot Area 0
Street 0
Lot Shape 0
Land Contour 0
Utilities 0
Lot Config 0
Land Slope 0
Neighborhood 0
Condition 1 0
Condition 2 0
Bldg Type 0
House Style 0
Overall Qual 0
Overall Cond 0
Year Built 0
Year Remod/Add 0
Roof Style 0
Roof Matl 0
Exterior 1st 0
Exterior 2nd 0
Mas Vnr Type 0
Mas Vnr Area 0
Exter Qual 0
Exter Cond 0
Foundation 0
Bsmt Qual 0
Bsmt Cond 0
Bsmt Exposure 0
BsmtFin Type 1 0
BsmtFin SF 1 0
BsmtFin Type 2 0
BsmtFin SF 2 0
Bsmt Unf SF 0
Total Bsmt SF 0
Heating 0
Heating QC 0
Central Air 0
Electrical 0
1st Flr SF 0
2nd Flr SF 0
Low Qual Fin SF 0
Gr Liv Area 0
Bsmt Full Bath 0
Bsmt Half Bath 0
Full Bath 0
Half Bath 0
Bedroom AbvGr 0
Kitchen AbvGr 0
Kitchen Qual 0
TotRms AbvGrd 0
Functional 0
Fireplaces 0
Garage Type 0
Garage Yr Blt 0
Garage Finish 0
Garage Cars 0
Garage Area 0
Garage Qual 0
Garage Cond 0
Paved Drive 0
Wood Deck SF 0
Open Porch SF 0
Enclosed Porch 0
3Ssn Porch 0
Screen Porch 0
Pool Area 0
Misc Val 0
Mo Sold 0
Yr Sold 0
Sale Type 0
Sale Condition 0
SalePrice 0
Após tratar os valores nulos, precisamos transformar as variáveis categóricas em numéricas para que o modelo de árvore de decisão possa utilizá-las. Utilizei duas técnicas principais para isso:
Label Encoding -> serve para variáveis categóricas que possuem uma ordem ou hierarquia e que têm um número limitado de categorias. Exemplo: Poor, Fair, Average, Good, Excellent.
One-Hot Encoding -> serve para variáveis categóricas que não possuem uma ordem ou hierarquia e têm um número elevado de categorias. Exemplo: Cor do carro (vermelho, azul, verde).
Mapeamento Manual -> para variáveis ordinais específicas, onde atribuímos valores numéricos com base na ordem percebida.
Na tabela abaixo estão listadas as variáveis que foram transformadas utilizando cada técnica:
| Técnica | Variáveis |
|---|---|
| Mapeamento Manual | Exter Qual, Exter Cond, Bsmt Qual, Bsmt Cond, Heating QC, Kitchen Qual, Garage Qual, Garage Cond, Bsmt Exposure, BsmtFin Type 1, BsmtFin Type 2, Functional |
| One-Hot Encoding | MS Zoning, Street, Lot Shape, Land Contour, Utilities, Lot Config, Land Slope, Neighborhood, Condition 1, Condition 2, Bldg Type, House Style, Roof Style, Roof Matl, Exterior 1st, Exterior 2nd, Mas Vnr Type, Foundation, Heating, Central Air, Electrical, Garage Type, Garage Finish, Paved Drive, Sale Type, Sale Condition |
- Variáveis categóricas foram transformadas em numéricas para uso no modelo.
Passo 2: Carregar o Conjunto de Dados
Carregamos o conjunto de dados Iris diretamente do repositório do UCI Machine Learning Repository.
```python
url = "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"
columns = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species']
data = pd.read_csv(url, names=columns)
```
Passo 3: Pré-processamento dos Dados
Verificamos se há valores ausentes e codificamos a variável alvo.
```python
print(data.isnull().sum())
data['species'] = data['species'].astype('category').cat.codes
```
Passo 4: Dividir os Dados em Conjuntos de Treinamento e Teste
Dividimos os dados em conjuntos de treinamento e teste para avaliar o desempenho do modelo.
```python
X = data.drop('species', axis=1)
y = data['species']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
```
Passo 5: Construir o Modelo Random Forest
Criamos e treinamos o modelo Random Forest.
```python
rf_model = RandomForestClassifier(n_estimators=100, random_state=42)
rf_model.fit(X_train, y_train)
```
Passo 6: Avaliar o Modelo
Avaliamos o desempenho do modelo usando métricas como matriz de confusão e relatório de classificação.
```python
y_pred = rf_model.predict(X_test)
print(confusion_matrix(y_test, y_pred))
print(classification_report(y_test, y_pred))
```
Conclusão
O modelo de Random Forest foi capaz de classificar as espécies de flores com uma boa precisão. A matriz de confusão e o relatório de classificação fornecem insights sobre o desempenho do modelo e suas áreas de melhoria. Com ajustes adicionais e mais dados, o modelo pode ser aprimorado ainda mais.




