Heatmap.

Voorspel de verkoopprijs van je huis met een Random Forest Regressor

In deze blog post zal ik machine learning en Python gebruiken om de verkoopprijs van een huis te voorspellen. Er zal een Random Forest Regressor worden gebruikt en ik zal het resultaat van mijn Random Forest Python algorithme demonstreren!

There is no law except the law that there is no law. – John Archibald Wheeler

Data Science gaat over het ontdekken van verborgen patronen in data. Het observeren van de data is net zo belangrijk als het ontdekken van patronen in data. Zonder observaties zal het ontdekken van patronen moeilijk zijn en zonder patroondetectie is het onmogelijk om conclusies te trekken over de data. Beide concepten zijn onmisbaar voor het maken van conclusies over de data.

De blogpost is onderverdeeld in de volgende hoofdstukken:

  1. Verken de variabelen. Waar gaat het probleem over? Hoe ziet de te voorspellen variabele eruit? Wat stellen de andere variabelen voor?
  2. Variable analyse. Hier zullen we proberen de data zo clean mogelijk te krijgen. Niet alle data is namelijk compleet!
  3. Machine Learning. Hier bouwen we ons patroon detectie algorithm.

Tijd om van start te gaan!

# Loading stuff
import pandas as pd
import numpy as np
import sklearn
import matplotlib.pyplot as plt
import seaborn as sns
import warnings

sns.set()
pd.set_option('max_columns', 1000)
warnings.filterwarnings('ignore')
%matplotlib inline
# Load the data
df_train = pd.read_csv('../input/train.csv')
# Explore the columns
print(df_train.columns.values)
print('No. variables:', len(df_train.columns.values))
['Id' 'MSSubClass' 'MSZoning' 'LotFrontage' 'LotArea' 'Street' 'Alley'
 'LotShape' 'LandContour' 'Utilities' 'LotConfig' 'LandSlope'
 'Neighborhood' 'Condition1' 'Condition2' 'BldgType' 'HouseStyle'
 'OverallQual' 'OverallCond' 'YearBuilt' 'YearRemodAdd' 'RoofStyle'
 'RoofMatl' 'Exterior1st' 'Exterior2nd' 'MasVnrType' 'MasVnrArea'
 'ExterQual' 'ExterCond' 'Foundation' 'BsmtQual' 'BsmtCond' 'BsmtExposure'
 'BsmtFinType1' 'BsmtFinSF1' 'BsmtFinType2' 'BsmtFinSF2' 'BsmtUnfSF'
 'TotalBsmtSF' 'Heating' 'HeatingQC' 'CentralAir' 'Electrical' '1stFlrSF'
 '2ndFlrSF' 'LowQualFinSF' 'GrLivArea' 'BsmtFullBath' 'BsmtHalfBath'
 'FullBath' 'HalfBath' 'BedroomAbvGr' 'KitchenAbvGr' 'KitchenQual'
 'TotRmsAbvGrd' 'Functional' 'Fireplaces' 'FireplaceQu' 'GarageType'
 'GarageYrBlt' 'GarageFinish' 'GarageCars' 'GarageArea' 'GarageQual'
 'GarageCond' 'PavedDrive' 'WoodDeckSF' 'OpenPorchSF' 'EnclosedPorch'
 '3SsnPorch' 'ScreenPorch' 'PoolArea' 'PoolQC' 'Fence' 'MiscFeature'
 'MiscVal' 'MoSold' 'YrSold' 'SaleType' 'SaleCondition' 'SalePrice']
No. variables: 81

Verken de variabelen

Zoals je kan zien, zijn er ongeveer 80 variabelen. Dat zijn er best veel en waarschijnlijk zijn ze niet allemaal nodig. De ‘SalePrice’ variabele is onze te voorspellen variabele. Deze variabele zouden we graag zien te voorspellen aan de hand van de andere variabelen. Wat zijn de andere variabelen en wat zijn de types van deze variabelen?

Het ‘cleanen’ van de data

Enkele variabelen bestaat uit een hoop NaNs (Not-a-Number). Deze variabelen zullen worden weggegooid omdat ze vrijwel niet bijdragen aan het voorspellen van de te voorspellen variabele.

num_missing = df_train.isnull().sum()
percent = num_missing / df_train.isnull().count()

df_missing = pd.concat([num_missing, percent], axis=1, keys=['MissingValues', 'Fraction'])
df_missing = df_missing.sort_values('Fraction', ascending=False)
df_missing[df_missing['MissingValues'] > 0]
MissingValuesFraction
PoolQC14530.995205
MiscFeature14060.963014
Alley13690.937671
Fence11790.807534
FireplaceQu6900.472603
LotFrontage2590.177397
GarageYrBlt810.055479
GarageCond810.055479
GarageType810.055479
GarageFinish810.055479
GarageQual810.055479
BsmtFinType2380.026027
BsmtExposure380.026027
BsmtQual370.025342
BsmtCond370.025342
BsmtFinType1370.025342
MasVnrArea80.005479
MasVnrType80.005479
Electrical10.000685
 Om het probleem eenvoudiger te maken, zullen we variabelen weggooien met missende informatie. Dit maakt het voorspellen niet beter, maar het zorgt er wel voor dat we geen aannames hoeven te maken over de variabelen (wat erg gevaarlijk kan zijn).
variables_to_keep = df_missing[df_missing['MissingValues'] == 0].index
df_train = df_train[variables_to_keep]

Variable analyse

Hier gaan we een snelle analyse doen van de variabelen en de onderliggende relaties. We beginnen met een correlatie matrix.

# Build the correlation matrix
matrix = df_train.corr()
f, ax = plt.subplots(figsize=(16, 12))
sns.heatmap(matrix, vmax=0.7, square=True)
Correlations.

Correlations.

We kunnen nu inzoomen op de verkoopprijs (SalePrice) en we kunnen vaststellen welke variabelen gecorreleerd zijn aan deze variabele.

interesting_variables = matrix['SalePrice'].sort_values(ascending=False)
# Filter out the target variables (SalePrice) and variables with a low correlation score (v such that -0.6 <= v <= 0.6)
interesting_variables = interesting_variables[abs(interesting_variables) >= 0.6]
interesting_variables = interesting_variables[interesting_variables.index != 'SalePrice']
interesting_variables
OverallQual    0.790982
GrLivArea      0.708624
GarageCars     0.640409
GarageArea     0.623431
TotalBsmtSF    0.613581
1stFlrSF       0.605852
Name: SalePrice, dtype: float64

Blijkbaar is de overall quality de meest voorspellende variabele op dit punt. Het ligt voor de hand, maar het is ook best vaag. Wat wordt er namelijk precies bedoeld met “overall quality”? Wie of wat legt deze score vast? Laten we inzoomen op de meest voorspellende variabele:

values = np.sort(df_train['OverallQual'].unique())
print('Unique values of "OverallQual":', values)
Unique values of "OverallQual": [ 1  2  3  4  5  6  7  8  9 10]

Blijkbaar hebben we een semi-categorische variabele “OverallQual” met een score van 1 tot 10. Volgens de omschrijving betekent een score van 1 Very Poor (enorm slecht), 5 betekent Average (gemiddeld) and 10 betekent Very Excellent (uitstekend). Laten we de relatie tussen “OverallQual” en “SalePrice” bekijken:

data = pd.concat([df_train['SalePrice'], df_train['OverallQual']], axis=1)
data.plot.scatter(x='OverallQual', y='SalePrice')
Correlations.

Correlations.

Hier is duidelijk een trend zichtbaar. Nu kunnen we de analyse tussen alle interessant variabelen uitvoeren!

cols = interesting_variables.index.values.tolist() + ['SalePrice']
sns.pairplot(df_train[cols], size=2.5)
plt.show()
Correlations.

Correlations.

Deze plot geeft een hoop prijs. Het vertelt ons iets over de types van de verschillende variabelen. Er zijn een paar discrete variabelen (OverallQual, GarageCars) en enkele continue variabelen (GrLivArea, GarageArea, TotalBsmtSF, 1stFlrSF).

Nu gaan we inzoomen op de eerder gemaakte heatmap door alleen naar de interessante variabelen te kijken.

# Build the correlation matrix
matrix = df_train[cols].corr()
f, ax = plt.subplots(figsize=(8, 6))
sns.heatmap(matrix, vmax=1.0, square=True)
Heatmap.

Heatmap.

Er zijn enkele clusters zichtbaar. GarageCars en GarageArea hebben een sterk verband. Dit is ook vrij logisch. Ook is er een sterk verband tussen TotalBsmtSF (de oppervlakte van de kelder) en 1stFlrSF (de oppervlakte van de eerste verdieping). Dat is ook vrij logisch. Ook is te zien dat alle variabelen bijdragen aan het verband met SalePrice. Nu kunnen we starten met Machine Learning en proberen de verkoopprijs te voorspellen!

Machine Learning (Random Forest regression)

In dit hoofdstuk zal ik een Random Forest regressor gebruiken. De data zal worden gesplitst in een train en een test set. Nu kunnen we ons model op gaan stellen.

from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split

pred_vars = [v for v in interesting_variables.index.values if v != 'SalePrice']
target_var = 'SalePrice'

X = df_train[pred_vars]
y = df_train[target_var]

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.1, random_state=42)

model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)

# Build a plot
plt.scatter(y_pred, y_test)
plt.xlabel('Prediction')
plt.ylabel('Real value')

# Now add the perfect prediction line
diagonal = np.linspace(0, np.max(y_test), 100)
plt.plot(diagonal, diagonal, '-r')
plt.show()
Predictions.

Predictions.

Het ziet er goed uit! De rode lijn laat perfecte voorspellingen zien. Als alle voorspelling precies gelijk zouden zijn aan de verkoopprijzen, dan zouden alle blauwe punten op de rode lijn liggen. Er zijn echter een paar afwijkingen en een paar uitschieters. Om iets te kunnen zeggen over de nauwkeurigheid, kunnen we de RMS error berekenen (Root Mean Squared error):

from sklearn.metrics import mean_squared_log_error, mean_absolute_error

print('MAE:\t$%.2f' % mean_absolute_error(y_test, y_pred))
print('MSLE:\t%.5f' % mean_squared_log_error(y_test, y_pred))
MAE:	$23552.62
MSLE:	0.03613

Een gemiddelde afwijking van $23K,-. Dit komt voornamelijk door extreme uitschieters. Niet slecht voor een snelle poging!

Conclusie (TL;DR)

Met behulp van een Random Forest regressor is het mogelijk om huizenprijzen te voorspellen. Als je op het punt staat een huis te kopen, aarzel dan niet contact op te nemen!

De beschrijving van de competitie is te vinden op Kaggle en mijn uiteindelijk notebook kan hier gevonden worden.