Os modelos de precificação são essenciais para as empresas maximizarem lucros, permanecerem competitivas, segmentarem o mercado, tomarem decisões estratégicas, gerenciarem estoques, construírem a marca e preverem receitas. Eles ajudam a determinar os preços ideais com base na demanda, concorrência e percepção do cliente. Em resumo, os modelos de precificação são fundamentais para o sucesso e a sustentabilidade das empresas. Para esse projeto, vou utilizar o dataset Home Costs — Superior Regression Methods, do Kaggle.
O conjunto de dados “Home Costs: Superior Regression Methods” do Kaggle contém informações sobre casas em Ames, Iowa, EUA, usado em competições de ciência de dados. Ele visa prever preços de casas com base em atributos como tamanho do lote, área construída, número de quartos e qualidade do acabamento.
fonte: https://www.kaggle.com/competitions/house-prices-advanced-regression-techniques
Bibliotecas utilizadas
# Bibliotecas de manipulação
import pandas as pd
import numpy as np
# Bibliotecas de visualização
import matplotlib.pyplot as plt
import seaborn as sns
# Bibliotecas de pré-processamento
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import GridSearchCV
# Bibliotecas de modelagem
from sklearn.ensemble import IsolationForest, RandomForestRegressor
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.svm import SVR
from sklearn.ensemble import GradientBoostingRegressor
from xgboost import XGBRegressor
# Bibliotecas métricas
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
# Importando e visualizando dataset de treino
url_train = r'https://uncooked.githubusercontent.com/MARINHOALEX/Modelo-de-precificacao---Home-Costs/foremost/prepare.csv'prepare = pd.read_csv(url_train)
prepare
# Importando dataset de teste
url_test = r'https://uncooked.githubusercontent.com/MARINHOALEX/Modelo-de-precificacao---Home-Costs/foremost/take a look at.csv'take a look at = pd.read_csv(url_test)
take a look at
# Importando dataset de validação
url_submission = r'https://uncooked.githubusercontent.com/MARINHOALEX/Modelo-de-precificacao---Home-Costs/foremost/sample_submission.csv'submission = pd.read_csv(url_submission)
submission
Como os dataset são muito extensos, vou concatena-los para aplicar as açoes de uma só vez. Também já vou aplicando o pré-processamento conforme necessidade.
# Criando colunas de treino e take a look at para diferenciar datasets
prepare['Train_test'] = 0
take a look at['Train_test'] = 1# Concatenando datasets
df = pd.concat([train, test])
# Visualizando quantidade de valores nulos em cada coluna
df.isnull().sum()[df.isnull().sum()>1].sort_values(ascending=False)
PoolQC 2909
MiscFeature 2814
Alley 2721
Fence 2348
SalePrice 1459
FireplaceQu 1420
LotFrontage 486
GarageFinish 159
GarageYrBlt 159
GarageQual 159
GarageCond 159
GarageType 157
BsmtExposure 82
BsmtCond 82
BsmtQual 81
BsmtFinType2 80
BsmtFinType1 79
MasVnrType 24
MasVnrArea 23
MSZoning 4
Purposeful 2
BsmtFullBath 2
Utilities 2
BsmtHalfBath 2
dtype: int64
Como o dataset tem muitas colunas, e muitas com valores nulos, vou excluir as colunas com mais de 10% de valores nulos
# Criando variável com colunas que correspondem a question
delete_columns = df.isnull().imply()[df.isnull().mean()>0.1].sort_values(ascending=False).index# Removendo coluna alvo da lista
delete_columns = delete_columns.drop('SalePrice')
# Excluindo colunas
df.drop(columns=delete_columns, inplace=True)
O restante das colunas com valores nulos será substituído por -1
# Substituindo valores nulos por -1
df.fillna(-1, inplace=True)# Visualizando estatísticas descritivas
df.describe().T
# Analisando valores das colunas do tipo object
for i in df.columns[df.dtypes == 'object']:
print(f'{i}: {df[i].value_counts().form[0]}')
print(df[i].distinctive())
print('-'*50)
MSZoning: 6
['RL' 'RM' 'C (all)' 'FV' 'RH' -1]
--------------------------------------------------
Road: 2
['Pave' 'Grvl']
--------------------------------------------------
LotShape: 4
['Reg' 'IR1' 'IR2' 'IR3']
--------------------------------------------------
LandContour: 4
['Lvl' 'Bnk' 'Low' 'HLS']
--------------------------------------------------
Utilities: 3
['AllPub' 'NoSeWa' -1]
--------------------------------------------------
LotConfig: 5
['Inside' 'FR2' 'Corner' 'CulDSac' 'FR3']
--------------------------------------------------
LandSlope: 3
['Gtl' 'Mod' 'Sev']
--------------------------------------------------
Neighborhood: 25
['CollgCr' 'Veenker' 'Crawfor' 'NoRidge' 'Mitchel' 'Somerst' 'NWAmes'
'OldTown' 'BrkSide' 'Sawyer' 'NridgHt' 'NAmes' 'SawyerW' 'IDOTRR'
'MeadowV' 'Edwards' 'Timber' 'Gilbert' 'StoneBr' 'ClearCr' 'NPkVill'
'Blmngtn' 'BrDale' 'SWISU' 'Blueste']
--------------------------------------------------
Condition1: 9
['Norm' 'Feedr' 'PosN' 'Artery' 'RRAe' 'RRNn' 'RRAn' 'PosA' 'RRNe']
--------------------------------------------------
Condition2: 8
['Norm' 'Artery' 'RRNn' 'Feedr' 'PosN' 'PosA' 'RRAn' 'RRAe']
--------------------------------------------------
BldgType: 5
['1Fam' '2fmCon' 'Duplex' 'TwnhsE' 'Twnhs']
--------------------------------------------------
HouseStyle: 8
['2Story' '1Story' '1.5Fin' '1.5Unf' 'SFoyer' 'SLvl' '2.5Unf' '2.5Fin']
--------------------------------------------------
RoofStyle: 6
['Gable' 'Hip' 'Gambrel' 'Mansard' 'Flat' 'Shed']
--------------------------------------------------
RoofMatl: 8
['CompShg' 'WdShngl' 'Metal' 'WdShake' 'Membran' 'Tar&Grv' 'Roll'
'ClyTile']
--------------------------------------------------
Exterior1st: 16
['VinylSd' 'MetalSd' 'Wd Sdng' 'HdBoard' 'BrkFace' 'WdShing' 'CemntBd'
'Plywood' 'AsbShng' 'Stucco' 'BrkComm' 'AsphShn' 'Stone' 'ImStucc'
'CBlock' -1]
--------------------------------------------------
Exterior2nd: 17
['VinylSd' 'MetalSd' 'Wd Shng' 'HdBoard' 'Plywood' 'Wd Sdng' 'CmentBd'
'BrkFace' 'Stucco' 'AsbShng' 'Brk Cmn' 'ImStucc' 'AsphShn' 'Stone'
'Other' 'CBlock' -1]
--------------------------------------------------
MasVnrType: 5
['BrkFace' 'None' 'Stone' 'BrkCmn' -1]
--------------------------------------------------
ExterQual: 4
['Gd' 'TA' 'Ex' 'Fa']
--------------------------------------------------
ExterCond: 5
['TA' 'Gd' 'Fa' 'Po' 'Ex']
--------------------------------------------------
Basis: 6
['PConc' 'CBlock' 'BrkTil' 'Wood' 'Slab' 'Stone']
--------------------------------------------------
BsmtQual: 5
['Gd' 'TA' 'Ex' -1 'Fa']
--------------------------------------------------
BsmtCond: 5
['TA' 'Gd' -1 'Fa' 'Po']
--------------------------------------------------
BsmtExposure: 5
['No' 'Gd' 'Mn' 'Av' -1]
--------------------------------------------------
BsmtFinType1: 7
['GLQ' 'ALQ' 'Unf' 'Rec' 'BLQ' -1 'LwQ']
--------------------------------------------------
BsmtFinType2: 7
['Unf' 'BLQ' -1 'ALQ' 'Rec' 'LwQ' 'GLQ']
--------------------------------------------------
Heating: 6
['GasA' 'GasW' 'Grav' 'Wall' 'OthW' 'Floor']
--------------------------------------------------
HeatingQC: 5
['Ex' 'Gd' 'TA' 'Fa' 'Po']
--------------------------------------------------
CentralAir: 2
['Y' 'N']
--------------------------------------------------
Electrical: 6
['SBrkr' 'FuseF' 'FuseA' 'FuseP' 'Mix' -1]
--------------------------------------------------
KitchenQual: 5
['Gd' 'TA' 'Ex' 'Fa' -1]
--------------------------------------------------
Purposeful: 8
['Typ' 'Min1' 'Maj1' 'Min2' 'Mod' 'Maj2' 'Sev' -1]
--------------------------------------------------
GarageType: 7
['Attchd' 'Detchd' 'BuiltIn' 'CarPort' -1 'Basment' '2Types']
--------------------------------------------------
GarageFinish: 4
['RFn' 'Unf' 'Fin' -1]
--------------------------------------------------
GarageQual: 6
['TA' 'Fa' 'Gd' -1 'Ex' 'Po']
--------------------------------------------------
GarageCond: 6
['TA' 'Fa' -1 'Gd' 'Po' 'Ex']
--------------------------------------------------
PavedDrive: 3
['Y' 'N' 'P']
--------------------------------------------------
SaleType: 10
['WD' 'New' 'COD' 'ConLD' 'ConLI' 'CWD' 'ConLw' 'Con' 'Oth' -1]
--------------------------------------------------
SaleCondition: 6
['Normal' 'Abnorml' 'Partial' 'AdjLand' 'Alloca' 'Family']
Vou aplicar o *cat.codes* nas colunas *Neighborhood, Exterior1st e Exterior2nd*, que possuem mais de dez elementos únicos e o get_dummies ao restante.
# Atribuindo valores numéricos á coluna Exterior2nd
df.Neighborhood = df.Neighborhood.astype('class').cat.codes
df.Exterior1st = df.Exterior1st.astype('class').cat.codes
df.Exterior2nd = df.Exterior2nd.astype('class').cat.codes# Criando variáveis de 0 e 1 para o restante das colunas do tipo object utilizando get_dummies
df = pd.get_dummies(df, columns=df.columns[df.dtypes == 'object'].to_list())
print(df.form)
print(df.columns)
(2919, 235)
Index(['Id', 'MSSubClass', 'LotArea', 'Neighborhood', 'OverallQual',
'OverallCond', 'YearBuilt', 'YearRemodAdd', 'Exterior1st',
'Exterior2nd',
...
'SaleType_ConLw', 'SaleType_New', 'SaleType_Oth', 'SaleType_WD',
'SaleCondition_Abnorml', 'SaleCondition_AdjLand',
'SaleCondition_Alloca', 'SaleCondition_Family', 'SaleCondition_Normal',
'SaleCondition_Partial'],
dtype='object', size=235)
Modelagem
# Separando o dataframe em conjunto principal e de validação com a coluna Train_test
legitimate = df[df.Train_test == 1].drop(columns='Train_test')# Instanciando e treinando modelo para idenficicação de outlier
clf = IsolationForest(random_state=123).match(df)
# Identificando Outliers
outliers = clf.predict(df)
# Excluindo Outliers
print(f'Complete de linhas excluidas: {df.index[outliers == -1].form[0]}')
df.drop(index=df.index[outliers == -1], inplace=True)
print(f'Dimenssões atuais do DataFrame: {df.form}')
Complete de linhas excluidas: 57
Dimenssões atuais do DataFrame: (2805, 235)
# Separando conjunto em X e y
X = df[df.Train_test == 0].drop(columns=['SalePrice', 'Train_test'])
y = df[df.Train_test == 0].SalePrice# Separando dataset em teino e teste
SEED = 123
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=SEED)
X_train.form, X_test.form, y_train.form, y_test.form
Vou utilizar o modelo GradientBoostingRegresso
# Modelo GradientBoostingRegressor sobre dataset
gb_model = GradientBoostingRegressor(
n_estimators = 400,
learning_rate = 0.1,
max_depth= 3,
min_samples_split = 4,
min_samples_leaf = 2,
random_state=SEED)# Treinando modelo
gb_model.match(X_train, y_train)
# Predizendo valores
gb_pred = gb_model.predict(X_test)
# Imprimindo métricas
print('GradientBoostingRegressor')
print(f'MSE: {mean_squared_error(y_test, gb_pred):.2f}')
print(f'MAE: {mean_absolute_error(y_test, gb_pred)}')
print(f'R2 {r2_score(y_test, gb_pred)*100:.2f}%')
# Gráfico de dispersão e linha de referência
plt.scatter(x=gb_pred, y=y_test, coloration='pink', alpha=0.75, label='Valores Observados')
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], coloration='blue', linestyle='--', label='Linha de Referência')
# Informações do gráfico
plt.title('Modelo GradientBoostingRegressor', fontsize=14)
plt.xlabel('Valores preditos', fontsize=12)
plt.ylabel('Valores reais', fontsize=12)
plt.legend(fontsize=12)
plt.grid(seen=True, linestyle='--', alpha=0.5)
plt.present()
GradientBoostingRegressor
MSE: 751232103.97
MAE: 16415.92800304439
R2 89.80%
O modelo conseguiu explicar aproximadamente 89.80% da variabilidade nos preços das casas, com um erro médio absoluto de cerca de 16415.93 unidades monetárias e um erro quadrático médio de aproximadamente 751232103.97
# Aplicando modelo nos dados de validação
legitimate['Price'] = gb_model.predict(legitimate)# Visualizando valores preditos
legitimate.iloc[:,-1:]
Value
0 128289.279338
1 169422.306795
2 185572.422262
3 198060.484123
4 194920.587751
... ...
1454 81998.755263
1455 71779.921826
1456 159842.075573
1457 122600.504502
1458 222137.714890
Pocket book completo no GityHub:
https://github.com/MARINHOALEX/Modelo-de-precificacao—House-Prices/blob/main/House_Prices.ipynb