Predictive modeling for real estate sales pricing
RealAgents is a real estate company that focuses on selling houses.
RealAgents sells a variety of types of house in one metropolitan area.
Some houses sell slowly and sometimes require lowering the price in order to find a buyer.
In order to stay competitive, RealAgents would like to optimize the listing prices of the houses it is trying to sell.
They want to do this by predicting the sale price of a house given its characteristics.
If they can predict the sale price in advance, they can decrease the time to sale.
Data
The dataset contains records of previous houses sold in the area.
| Column Name | Criteria |
|---|---|
| house_id | Nominal. Unique identifier for houses. Missing values not possible. |
| city | Nominal. The city in which the house is located. One of 'Silvertown', 'Riverford', 'Teasdale' and 'Poppleton'. Replace missing values with "Unknown". |
| sale_price | Discrete. The sale price of the house in whole dollars. Values can be any positive number greater than or equal to zero. Remove missing entries. |
| sale_date | Discrete. The date of the last sale of the house. Replace missing values with 2023-01-01. |
| months_listed | Continuous. The number of months the house was listed on the market prior to its last sale, rounded to one decimal place. Replace missing values with mean number of months listed, to one decimal place. |
| bedrooms | Discrete. The number of bedrooms in the house. Any positive values greater than or equal to zero. Replace missing values with the mean number of bedrooms, rounded to the nearest integer. |
| house_type | Ordinal. One of "Terraced" (two shared walls), "Semi-detached" (one shared wall), or "Detached" (no shared walls). Replace missing values with the most common house type. |
| area | Continuous. The area of the house in square meters, rounded to one decimal place. Replace missing values with the mean, to one decimal place. |
Task 1
The team at RealAgents knows that the city that a property is located in makes a difference to the sale price.
Unfortuntately they believe that this isn't always recorded in the data.
Calculate the number of missing values of the city.
-
You should use the data in the file "house_sales.csv".
-
Your output should be an object
missing_city, that contains the number of missing values in this column.
# Import necessary library
import pandas as pd
import numpy as np
# Load the dataset
house_sales = pd.read_csv('house_sales.csv')
# Replace '--' with NaN
house_sales['city'] = house_sales['city'].replace('--', np.nan)
# Count missing values in the 'city' column
missing_city = house_sales['city'].isna().sum()
# Display the result
print(missing_city)Task 2
Before you fit any models, you will need to make sure the data is clean.
The table below shows what the data should look like.
Create a cleaned version of the dataframe.
-
You should start with the data in the file "house_sales.csv".
-
Your output should be a dataframe named
clean_data. -
All column names and values should match the table below.
| Column Name | Criteria |
|---|---|
| house_id | Nominal. Unique identifier for houses. Missing values not possible. |
| city | Nominal. The city in which the house is located. One of 'Silvertown', 'Riverford', 'Teasdale' and 'Poppleton' Replace missing values with "Unknown". |
| sale_price | Discrete. The sale price of the house in whole dollars. Values can be any positive number greater than or equal to zero. Remove missing entries. |
| sale_date | Discrete. The date of the last sale of the house. Replace missing values with 2023-01-01. |
| months_listed | Continuous. The number of months the house was listed on the market prior to its last sale, rounded to one decimal place. Replace missing values with mean number of months listed, to one decimal place. |
| bedrooms | Discrete. The number of bedrooms in the house. Any positive values greater than or equal to zero. Replace missing values with the mean number of bedrooms, rounded to the nearest integer. |
| house_type | Ordinal. One of "Terraced", "Semi-detached", or "Detached". Replace missing values with the most common house type. |
| area | Continuous. The area of the house in square meters, rounded to one decimal place. Replace missing values with the mean, to one decimal place. |
# Import necessary libraries
import pandas as pd
import numpy as np
# Load the dataset
house_sales = pd.read_csv('house_sales.csv')
# Replace '--' with NaN (if not done already)
house_sales['city'] = house_sales['city'].replace('--', np.nan)
# Replace missing values with 'Unknown'
house_sales['city'] = house_sales['city'].fillna('Unknown')
# Convert 'city' to nominal categorical (unordered)
house_sales['city'] = house_sales['city'].astype('category')
# Handling missing 'sale_price' (remove missing entries as per instructions)
house_sales = house_sales.dropna(subset=['sale_price'])
# Convert to datetime
house_sales['sale_date'] = pd.to_datetime(house_sales['sale_date'], errors='coerce')
# Handling missing 'sale_date' with the specified default date
house_sales['sale_date'] = house_sales['sale_date'].fillna(pd.Timestamp('2023-01-01'))
# Replace with mean (rounded to 1 decimal place)
mean_months_listed = house_sales['months_listed'].mean()
house_sales['months_listed'] = house_sales['months_listed'].fillna(mean_months_listed)
house_sales['months_listed'] = round(house_sales['months_listed'], 1)
# Handling missing 'bedrooms'
mean_bedrooms = round(house_sales['bedrooms'].mean())
house_sales['bedrooms'] = house_sales['bedrooms'].fillna(mean_bedrooms)
# Mapping inconsistent values to standardized ones
house_sales['house_type'] = house_sales['house_type'].replace({
'Det.': 'Detached',
'Semi': 'Semi-detached',
'Terr.': 'Terraced'
})
# Convert 'house_type' to ordinal categorical (ordered)
house_type_order = ['Terraced', 'Semi-detached', 'Detached']
house_sales['house_type'] = pd.Categorical(house_sales['house_type'],
categories=house_type_order,
ordered=True)
# Remove 'sq.m.' and convert to numeric
house_sales['area'] = house_sales['area'].str.replace(' sq.m.', '', regex=False)
house_sales['area'] = pd.to_numeric(house_sales['area'], errors='coerce')
house_sales['area'] = round(house_sales['area'], 1)
# Check for duplicate rows in house_sales
duplicates = house_sales.duplicated().sum()
clean_data = house_sales.copy()Task 3
The team at RealAgents have told you that they have always believed that the number of bedrooms is the biggest driver of house price.
Producing a table showing the difference in the average sale price by number of bedrooms along with the variance to investigate this question for the team.
-
You should start with the data in the file 'house_sales.csv'.
-
Your output should be a data frame named
price_by_rooms. -
It should include the three columns
bedrooms,avg_price,var_price. -
Your answers should be rounded to 1 decimal place.
# Load dataset
df = pd.read_csv('house_sales.csv')
# Produce a new dataframe with average sales price by number of bedrooms along with the variance
price_by_rooms = df.groupby('bedrooms')['sale_price'].agg(
avg_price=np.mean,
var_price=np.var
).round(1).reset_index()
price_by_roomsTask 4
Fit a baseline model to predict the sale price of a house.
-
Fit your model using the data contained in “train.csv”
-
Use “validation.csv” to predict new values based on your model. You must return a dataframe named
base_result, that includeshouse_idandprice. The price column must be your predicted values.
# 1. Import necessary libraries
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import OneHotEncoder
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.metrics import mean_squared_error
# 2. Load the datasets
train = pd.read_csv('train.csv')
validation = pd.read_csv('validation.csv')
# 3. Drop 'sale_date' (not used in the baseline model)
train = train.drop(columns=['sale_date'])
validation = validation.drop(columns=['sale_date'])
# 4. Identify categorical and numerical columns
categorical_cols = ['city', 'house_type']
# 5. Preprocessing Pipeline (One-Hot Encoding for categorical variables)
preprocessor = ColumnTransformer(
transformers=[('cat', OneHotEncoder(handle_unknown='ignore'), categorical_cols)],
remainder='passthrough'
)
# 6. Define features (X) and target (y)
X_train = train.drop(['house_id', 'sale_price'], axis=1)
y_train = train['sale_price']
X_validation = validation.drop(['house_id'], axis=1)
# 7. Model Pipeline (Preprocessing + Linear Regression)
pipeline = Pipeline([
('preprocessor', preprocessor),
('regressor', LinearRegression())
])
# 8. Model Training
pipeline.fit(X_train, y_train)
# 9. Predictions on validation data
predictions = pipeline.predict(X_validation)
predictions = np.maximum(0, predictions) # Ensure no negative prices
# 10. RMSE Calculation (on Training Set)
train_predictions = pipeline.predict(X_train)
train_rmse = np.sqrt(mean_squared_error(y_train, train_predictions))
# 11. Prepare Submission
base_result = pd.DataFrame({
'house_id': validation['house_id'],
'price': np.round(predictions, 0).astype(int) # Whole-dollar rounding
})
# 12. Display Results
print(base_result.head())
print(f'\nRoot Mean Squared Error (Training Set): {train_rmse:.1f}')
# 13. RMSE Threshold Check
if train_rmse < 30000:
print('Model passed with RMSE below 30,000!')
else:
print('Model did NOT pass. RMSE is above 30,000.')Task 5
Fit a comparison model to predict the sale price of a house.
-
Fit your model using the data contained in “train.csv”
-
Use “validation.csv” to predict new values based on your model. You must return a dataframe named
compare_result, that includeshouse_idandprice. The price column must be your predicted values.
# 1. Import necessary libraries
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder
from sklearn.compose import ColumnTransformer
from sklearn.metrics import mean_squared_error
# 2. Load datasets
train = pd.read_csv('train.csv')
validation = pd.read_csv('validation.csv')
# 3. Drop 'sale_date' (as before)
train = train.drop(columns=['sale_date'])
validation = validation.drop(columns=['sale_date'])
# 4. Identify categorical columns
categorical_cols = ['city', 'house_type']
# 5. Preprocessing pipeline
preprocessor = ColumnTransformer(
transformers=[('cat', OneHotEncoder(handle_unknown='ignore'), categorical_cols)],
remainder='passthrough' # Keep numerical features as-is
)
# 6. Define features (X) and target (y)
X_train = train.drop(['house_id', 'sale_price'], axis=1)
y_train = train['sale_price']
X_validation = validation.drop(['house_id'], axis=1)
# 7. Model Pipeline (Preprocessing + Gradient Boosting)
pipeline = Pipeline([
('preprocessor', preprocessor),
('regressor', GradientBoostingRegressor(random_state=42))
])
# 8. Model Training
pipeline.fit(X_train, y_train)
# 9. Predictions on validation data
predictions = pipeline.predict(X_validation)
predictions = np.maximum(0, predictions) # Ensure no negative prices
# 10. Prepare Submission
compare_result = pd.DataFrame({
'house_id': validation['house_id'],
'price': np.round(predictions, 0).astype(int)
})
# 11. Evaluate on Training Data (since validation set has no actual prices)
train_predictions = pipeline.predict(X_train)
rmse = np.sqrt(mean_squared_error(y_train, train_predictions))
# 12. Display Results
print(compare_result.head())
print(f'\nRoot Mean Squared Error (Training Set): {rmse:.2f}')
# 13. RMSE Threshold Check
if rmse < 30000:
print('Model passed with RMSE below 30,000!')
else:
print('Model did NOT pass. RMSE is above 30,000!')