import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, r2_score
from sklearn.preprocessing import LabelEncoderTutorial 7 - Métodos de Regressão
Carregando os pacotes
Lendo os dados
Usaremos dados do Ikea, uma loja de móveis com filiais em várias partes do mundo.
O próposito é prever o preço dos móveis vendidos na IKEA a partir de várias características destes produtos como a categoria e o tamanho do móvel, conforme aqui.
Vamos carregar os dados e ver as primeiras linhas.
df = pd.read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2020/2020-11-03/ikea.csv')
df.info()<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3694 entries, 0 to 3693
Data columns (total 14 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Unnamed: 0 3694 non-null int64
1 item_id 3694 non-null int64
2 name 3694 non-null object
3 category 3694 non-null object
4 price 3694 non-null float64
5 old_price 3694 non-null object
6 sellable_online 3694 non-null bool
7 link 3694 non-null object
8 other_colors 3694 non-null object
9 short_description 3694 non-null object
10 designer 3694 non-null object
11 depth 2231 non-null float64
12 height 2706 non-null float64
13 width 3105 non-null float64
dtypes: bool(1), float64(4), int64(2), object(7)
memory usage: 378.9+ KB
df.head()| Unnamed: 0 | item_id | name | category | price | old_price | sellable_online | link | other_colors | short_description | designer | depth | height | width | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 90420332 | FREKVENS | Bar furniture | 265.0 | No old price | True | https://www.ikea.com/sa/en/p/frekvens-bar-tabl... | No | Bar table, in/outdoor, 51x51 cm | Nicholai Wiig Hansen | NaN | 99.0 | 51.0 |
| 1 | 1 | 368814 | NORDVIKEN | Bar furniture | 995.0 | No old price | False | https://www.ikea.com/sa/en/p/nordviken-bar-tab... | No | Bar table, 140x80 cm | Francis Cayouette | NaN | 105.0 | 80.0 |
| 2 | 2 | 9333523 | NORDVIKEN / NORDVIKEN | Bar furniture | 2095.0 | No old price | False | https://www.ikea.com/sa/en/p/nordviken-nordvik... | No | Bar table and 4 bar stools | Francis Cayouette | NaN | NaN | NaN |
| 3 | 3 | 80155205 | STIG | Bar furniture | 69.0 | No old price | True | https://www.ikea.com/sa/en/p/stig-bar-stool-wi... | Yes | Bar stool with backrest, 74 cm | Henrik Preutz | 50.0 | 100.0 | 60.0 |
| 4 | 4 | 30180504 | NORBERG | Bar furniture | 225.0 | No old price | True | https://www.ikea.com/sa/en/p/norberg-wall-moun... | No | Wall-mounted drop-leaf table, ... | Marcus Arvonen | 60.0 | 43.0 | 74.0 |
Limpeza dos dados
Também vamos fazer uma revisão geral dos dados.
Conforme visto no output do info() há várias colunas em formato de string e algumas colunas que apenas são um id de cada linha, além de termos valores NULL. Precisamos limpar o dataset para deixá-lo mais adequado ao modelo de machine learning.
ikea = df[['price','category','sellable_online','depth','height','width']].dropna()
ikea['category'] = ikea['category'].astype('category')
ikea.info()<class 'pandas.core.frame.DataFrame'>
Index: 1899 entries, 3 to 3688
Data columns (total 6 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 price 1899 non-null float64
1 category 1899 non-null category
2 sellable_online 1899 non-null bool
3 depth 1899 non-null float64
4 height 1899 non-null float64
5 width 1899 non-null float64
dtypes: bool(1), category(1), float64(4)
memory usage: 78.6 KB
Análise Exploratória de Dados
Análise de Correlação.
num_cols = ikea.select_dtypes(include=['int64', 'float64'])
corr_matrix = num_cols.corr()
sns.heatmap(corr_matrix, annot=True, cmap='coolwarm')
plt.title('Heatmap de Correlações')
plt.show()
Gráficos de Dispersão com o preço
sns.scatterplot(data=ikea, x='depth', y='price')
plt.show()
sns.scatterplot(data=ikea, x='width', y='price')
plt.show()
sns.scatterplot(data=ikea, x='height', y='price')
plt.show()
sns.scatterplot(data=ikea, x='sellable_online', y='price')
plt.show()
como praticamente todos os produtos se vendem online, vamos eliminar essa coluna.
counts = ikea['sellable_online'].value_counts()
sns.countplot(x='sellable_online', data=ikea)
plt.show()
ikea = ikea.drop(['sellable_online'], axis=1)
ikea.head()| price | category | depth | height | width | |
|---|---|---|---|---|---|
| 3 | 69.0 | Bar furniture | 50.0 | 100.0 | 60.0 |
| 4 | 225.0 | Bar furniture | 60.0 | 43.0 | 74.0 |
| 5 | 345.0 | Bar furniture | 45.0 | 91.0 | 40.0 |
| 6 | 129.0 | Bar furniture | 44.0 | 95.0 | 50.0 |
| 8 | 129.0 | Bar furniture | 44.0 | 95.0 | 50.0 |
Vamos ver o preço:
sns.histplot(data=ikea, x='price',
bins=30, color='darkblue')
plt.xlabel('Preço')
plt.show()
Como o preço está muito agregaado aos valores mais baixos, é necessário transformar a coluna, p.ex. utilizando log10.
ikea['log_price'] = np.log10(ikea['price'])
ikea = ikea.drop(['price'], axis=1)
ikea.head()| category | depth | height | width | log_price | |
|---|---|---|---|---|---|
| 3 | Bar furniture | 50.0 | 100.0 | 60.0 | 1.838849 |
| 4 | Bar furniture | 60.0 | 43.0 | 74.0 | 2.352183 |
| 5 | Bar furniture | 45.0 | 91.0 | 40.0 | 2.537819 |
| 6 | Bar furniture | 44.0 | 95.0 | 50.0 | 2.110590 |
| 8 | Bar furniture | 44.0 | 95.0 | 50.0 | 2.110590 |
sns.histplot(data=ikea, x='log_price',
bins=30, color='darkblue')
plt.xlabel('Log10 Preço')
plt.show()
Antes de iniciar a modelagem, precisamos codificar a(s) coluna(s) categóricas, neste caso, temos uma única coluna, chamada category.
label_encoder = LabelEncoder()
ikea['category_encoded'] = label_encoder.fit_transform(ikea['category'])ikea = ikea.drop('category', axis=1)Modelagem Supervisionada
Vamos dividir o dataset em treino e teste:
X = ikea.drop('log_price', axis=1) # Features
y = ikea['log_price'] # Target variable
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) Árvores de Decisão (Decision Trees)
Vamos treinar o modelo de árvore de decisão.
from sklearn.tree import DecisionTreeRegressortree_model = DecisionTreeRegressor(random_state=42)
tree_model.fit(X_train, y_train)DecisionTreeRegressor(random_state=42)In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
DecisionTreeRegressor(random_state=42)
Vamos criar uma nova coluna com os resultados da predição:
y_pred = tree_model.predict(X_test)Por fim, vamos avaliar o modelo de árvore que treinamos, para isso vamos usar os dados de teste.
mse_tree = mean_squared_error(y_test, y_pred)
r2_tree = r2_score(y_test, y_pred)
rmse_tree = np.sqrt(mse_tree)
print(f'Mean Squared Error: {mse_tree}')
print(f'R-squared: {r2_tree}')
print(f'RMSE: {rmse_tree}')Mean Squared Error: 0.06556498700053338
R-squared: 0.7911592645549473
RMSE: 0.25605660897647886
Os resultados parecem satisfatórios, vamos fazer um gráfico para visualizar o resultado do modelo.
plt.scatter(y_test, y_pred, alpha=0.5)
plt.xlabel("Log Preço verdadeiro (y_test)")
plt.ylabel("Log Preço predito (y_pred)")
plt.title("Precisão do modelo para Árvore de Decisão")
# Addicionar linha diagonal
plt.plot([min(y_test), max(y_test)], [min(y_test), max(y_test)], color='red', linestyle='--')
plt.show()
vamos criar um dataframe com os indicadores para compará-los com os outros modelos.
indicadores = pd.DataFrame({
'R2':[r2_tree],
'MSE':[mse_tree],
'RMSE':[rmse_tree],
'Modelo':['Decision Tree']
})
indicadores| R2 | MSE | RMSE | Modelo | |
|---|---|---|---|---|
| 0 | 0.791159 | 0.065565 | 0.256057 | Decision Tree |
Random Forests
Agora vamos treinar um modelo de RF.
from sklearn.ensemble import RandomForestRegressorrf_model = RandomForestRegressor(random_state=42)
rf_model.fit(X_train, y_train)RandomForestRegressor(random_state=42)In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
RandomForestRegressor(random_state=42)
Vamos calcular a precisão com os dados de teste.
y_pred_rf = rf_model.predict(X_test)
mse_rf = mean_squared_error(y_test, y_pred_rf)
r2_rf = r2_score(y_test, y_pred_rf)
rmse_rf = np.sqrt(mse_rf)
print(f'Random Forest Mean Squared Error: {mse_rf}')
print(f'Random Forest R-squared: {r2_rf}')
print(f'Random Forest RMSE: {rmse_rf}')Random Forest Mean Squared Error: 0.04567220496347414
Random Forest R-squared: 0.8545227062442409
Random Forest RMSE: 0.21371056352804402
Novamente vamos fazer um gráfico para visualizar o desempenho do modelo RF.
plt.scatter(y_test, y_pred_rf, alpha=0.5)
plt.xlabel("Log Preço verdadeiro (y_test)")
plt.ylabel("Log Preço predito (y_pred)")
plt.title("Precisão do modelo para Random Forest")
# Addicionar linha diagonal
plt.plot([min(y_test), max(y_test)], [min(y_test), max(y_test)], color='red', linestyle='--')
plt.show()
E vamos incluir os indicadores do RF no nosso dataframe indicadores.
rf_df = pd.DataFrame({
'R2':[r2_rf],
'MSE':[mse_rf],
'RMSE':[rmse_rf],
'Modelo':['Random Forest']
})
indicadores = pd.concat([indicadores, rf_df], ignore_index=True)
indicadores| R2 | MSE | RMSE | Modelo | |
|---|---|---|---|---|
| 0 | 0.791159 | 0.065565 | 0.256057 | Decision Tree |
| 1 | 0.854523 | 0.045672 | 0.213711 | Random Forest |
Support Vector Regression (SVR)
Primeiro precisamos normalizar os valores dos atributos.
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler().fit(X_train)
X_train_scaled = scaler.transform(X_train)
X_test_scaled = scaler.transform(X_test)Agora vamos treinar um modelo SVR.
from sklearn.svm import SVRAgora podemos treinar o modelo. Têm três tipos de modelos SVR: ‘linear’ é como se fosse uma regressão linear; ‘poly’ é como se fosse uma regressão polinomial; ‘rbf’ uma regressão não-linear.
svr_model = SVR(kernel = 'rbf')
svr_model.fit(X_train, y_train)SVR()In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
SVR()
Vamos calcular a precisão com os dados de teste.
y_pred_svr = svr_model.predict(X_test)
mse_svr = mean_squared_error(y_test, y_pred_svr)
r2_svr = r2_score(y_test, y_pred_svr)
rmse_svr = np.sqrt(mse_svr)
print(f'SVR Mean Squared Error: {mse_svr}')
print(f'SVR R-squared: {r2_svr}')
print(f'SVR RMSE: {rmse_svr}')SVR Mean Squared Error: 0.06872190022184728
SVR R-squared: 0.7811037134287031
SVR RMSE: 0.2621486223916641
Novamente vamos fazer um gráfico para visualizar o desempenho do modelo SVR.
plt.scatter(y_test, y_pred_svr, alpha=0.5)
plt.xlabel("Log Preço verdadeiro (y_test)")
plt.ylabel("Log Preço predito (y_pred)")
plt.title("Precisão do modelo para Support Vector Regression")
# Addicionar linha diagonal
plt.plot([min(y_test), max(y_test)], [min(y_test), max(y_test)], color='red', linestyle='--')
plt.show()
E vamos incluir os valores no nosso dataframe de indicadores.
new_row = pd.DataFrame({
'R2': [r2_svr],
'MSE': [mse_svr],
'RMSE':[rmse_svr],
'Modelo': ['SVR']
})
indicadores = pd.concat([indicadores, new_row], ignore_index=True)
indicadores| R2 | MSE | RMSE | Modelo | |
|---|---|---|---|---|
| 0 | 0.791159 | 0.065565 | 0.256057 | Decision Tree |
| 1 | 0.854523 | 0.045672 | 0.213711 | Random Forest |
| 2 | 0.781104 | 0.068722 | 0.262149 | SVR |
Gráfico comparativo
fig = sns.scatterplot(data=indicadores, x='R2', y='RMSE', hue='Modelo')
fig.set_xlim(0.5, 1)
fig.set_ylim(0,0.5)
plt.title('Gráfico comparativo entre os indicadores RMSE x R2')
plt.show()