available · data science and machine learning roles · UKManchester, UK
bright@uzosike:~$

AlloyTower AVM

A property valuation model that picks one of two models depending on whether an assessed value is known, and explains every estimate it gives.

LIVEProperty valuation20262 min readLive console

MAPE · Model A · with assessment
8.0%
5-fold CV, ±0.3 points
MAPE · Model B · fundamentals only
44.9%
5-fold CV, ±2.8 points
Training data
1,956
cleaned rows · 11 data-quality findings

Problem

A property team needs an estimate it can defend. That means seeing why the model landed where it did, and knowing when not to trust it.

AlloyTower was a three-week internship project to build that for a property platform. The brief covered data validation, an automated valuation model, a comparables search and an app that people outside the data team could use.

Data

The source file had 2,090 sales and 28 columns. Before any modelling I wrote a data-quality report. It logged 11 findings and ranked each one by severity.

Severity Finding What I did
Critical Latitude and longitude are random. Only 3% of points fall in their stated state. Dropped both columns
Critical ZIP codes are randomly generated Dropped the column
High 6.4% of rows have implausible price for their size Dropped 134 rows below $50 per sqft
High Sale prices show no real trend over time Dropped date features
High Physical attributes carry weak signal Built two models and set expectations
Medium Assessed value correlates 0.99 with sale price Built one model with it and one without
Medium Some columns are derived from the target Dropped the derived price-per-sqft, annual-tax and building-age columns

Four more findings were low-severity or informational, such as file-encoding problems and a duplicate key. After cleaning, 1,956 rows remained.

The charts below are computed from the raw file, so each finding can be checked.

Where properties labelled California actually sit
2025.631.236.842.448-160-141-122-103-84-65California-108, 31.9-134, 23.3-149, 21-79.5, 47.4-92.2, 44.2-154, 42.3-71.4, 39-96.4, 28.6-140, 42.6-87.5, 25.9-111, 33-133, 21.1-127, 21.7-145, 43.8-140, 37.2-111, 21.3-134, 39.9-137, 46.6-144, 34-123, 37.2-129, 29.3-119, 44.5-149, 31.8-153, 39.2-117, 26.2-133, 23.9-121, 31.9-106, 28.7-126, 38.6-98.5, 23.6-102, 34-146, 43.7-92.7, 39.8-111, 33.7-132, 35.1-121, 26.5-114, 23.4-93.8, 33.7-118, 40.7-131, 44.6-99.1, 43.6-78.5, 38.5-89.2, 35.1-81.2, 23.3-107, 43.7-107, 21.6-86.8, 33.3-113, 30-120, 42.5-73, 38.6-152, 45.2-90.3, 29.1-151, 33.7-149, 45.5-102, 26.3-81.8, 40.1-100, 39.9-133, 36.1-127, 27.7-107, 46.1-107, 44.5-129, 33.9-83, 28.7-120, 23-86.1, 37.3-71, 39.2-121, 28.9-92.5, 26.6-84.8, 35.1-145, 35.3-137, 24-155, 38.2-73.8, 37.9-79.5, 39.8-115, 30-119, 27.8-105, 41-72.9, 34.3-144, 45.4-118, 42-120, 43.4-105, 24.6-147, 28.9-70.1, 30.4-82.4, 39.6-93.7, 39-120, 37.5-135, 36.1-82.1, 23.5-107, 40.3-108, 35.5-125, 30.6-102, 39.9-123, 28.5-72.4, 44.9-109, 44.9-117, 41.7-100, 25.5-119, 36.2-119, 38.2-128, 44.4-102, 38.5-80.7, 21-119, 24.3-128, 33.3-98.3, 31.5-86.2, 26.8-72.3, 34.4-130, 22.3-121, 40.9-145, 23.2-93, 30-97.9, 37-136, 30.1-157, 23.9-92.6, 33-98.9, 38.7-153, 33.2-118, 37.6-75.7, 27.7-107, 22.6-86.2, 39.2-145, 29.2-82.1, 27.3-77.7, 34-76.3, 23.4-74.6, 29-108, 46-82, 42.5-118, 39.7-150, 34.1-78.9, 33.9-146, 22.3-97.1, 33-148, 29.2-115, 22.9-97.1, 32.3-76.7, 37.7-126, 37-96.5, 40.7-120, 45.8-105, 32.7-135, 41.5-79.4, 38.3-136, 46.9-140, 23.1-73.7, 26.9-129, 21.9-131, 46-106, 39.8-115, 26.7-88.2, 29.8-89.4, 35.6-79.5, 40.7-82.3, 46.6-94.3, 36.9-117, 44-94.1, 44.7-148, 33.5-91.8, 29.3-87.8, 42.7-96.5, 30.6-86.8, 23.2-153, 42-82.7, 25.4-111, 41-147, 35.7-75.3, 22.6-139, 22.3-122, 45.5-151, 26.9-88.7, 22.8-86.3, 29.8-138, 26.6-150, 45.1-154, 38.9-107, 37.3-139, 33-139, 40.7-90.9, 40.9-80.5, 23.4-135, 26.1-152, 27-102, 43.9-143, 43.6-127, 41.7-137, 36.8-131, 46.9-151, 22.7-147, 27.5-75.9, 42-156, 33.6-113, 31.6-139, 23-89.4, 33.9-94.9, 34.8-124, 26.1-154, 42.1-124, 25.3-147, 31.2-87.3, 40.5-142, 26.7-155, 30.2-127, 46.1-105, 23.1-84.9, 31.5-143, 38.1-139, 29-132, 37.5-109, 24.3-111, 23.7-138, 39.4-149, 32-70.7, 33.1-155, 39.9-150, 44.2-88.8, 45-77, 33.4-103, 29.1-126, 28.5-150, 21.5-119, 42.8-115, 39.9-145, 31.8-114, 32.3-102, 47.1-155, 34-132, 45.9-143, 40.5-76.7, 41.1-83.4, 23-117, 44.9-149, 32-148, 31.3-121, 24.4-113, 31.9-152, 33.5-113, 35.6-119, 34.8-96.6, 29.1-130, 40.9-102, 25.4-141, 34.2-135, 25.9-107, 38.3-103, 33.8-148, 21.6-110, 27.4-136, 36.9-155, 45.5-78.2, 31.6-146, 43.5-102, 39-150, 29.4-92.6, 31-132, 29.8-77.1, 25.9-73.9, 44.4-89.5, 46.5-92.5, 43.7-138, 34.3-139, 33.6-130, 26-111, 45.3-149, 44.6-84.7, 25.3-99.3, 35.5-71, 21.7-142, 37-146, 41.9-96.9, 46-132, 30.6-135, 44.2-107, 36.3-110, 24.4-102, 34.4-82.4, 24.5-109, 41.5-143, 33.2-104, 30.3-114, 31.3-99.3, 36.8-90.9, 30.5-82.5, 27.6-130, 44.7-90.6, 35.1-88.2, 33.2-89.8, 33.7-110, 42.4-133, 34.4-100, 23.4-113, 29.4-144, 31.3-133, 41.2-138, 32.9-85.6, 38.4-81.3, 24.2-72.7, 42.9-134, 42.5-108, 34.4-83.2, 44.4-90.1, 35.4-124, 37.2-82.1, 24.3-112, 28.7-147, 35.7-132, 31.2-155, 47.4-142, 43.2-131, 46.8-142, 42.8-72.8, 43.6-104, 34.5-90.5, 29.3-90.3, 25.4-102, 22.9-128, 44.5-109, 29.6-80.5, 23.4-152, 42.4-85.3, 32.3-90.4, 31.6-98.6, 42.1-143, 45.7-130, 40.7-124, 46.6-132, 21.6-124, 25.8-93, 35.3-96, 34.1-80.9, 25.4-127, 40.1-113, 30.6-150, 42-138, 23.5-110, 24.8-94.6, 33.1-136, 39.6-141, 39.3-115, 34.8-98.3, 21.3-146, 42.8-74.2, 38.4-123, 36.3-70.8, 23.8-96.6, 43-141, 47.1-144, 27.5-76.5, 24-142, 33.1-134, 21.9-127, 27.3-105, 25.3-71.5, 45.3-87.4, 31.5-90, 41.4-147, 44.2-141, 28.7-123, 28.9-123, 33.5-96.5, 40.4-82.9, 24.6-154, 22.1-126, 26.5-124, 29.3-74.9, 38.9-114, 44.3-72.6, 44.9-134, 34.8-101, 23.2-119, 21.5-97.8, 47.1-121, 29.7-72.8, 31.5-138, 33-90.3, 36.8-123, 39.7-157, 31.6-155, 21-71.6, 28-80.2, 32.4-84.7, 33.7-88.3, 24.1-93.9, 40.9-131, 29.8-113, 22.5-88.2, 40.5-142, 27.4-99.7, 47-134, 41.8-149, 31.8-134, 23-81, 38.2-111, 42.9-136, 33-128, 42.5-95.3, 32.5-117, 25.1-112, 47-127, 36.6-84.1, 45.6-96, 26.3-85.3, 42.6-118, 37.4-115, 29.1-131, 21.3-134, 21.8-90.1, 31.1-135, 35.6-88.7, 24-145, 31.7-94.3, 25.2-105, 23.1-121, 36.1-152, 23.9-115, 36.9-139, 36.4-149, 43.4-125, 30.2-95.2, 29.7-146, 36.5-105, 45-145, 31-76.5, 41.8-117, 41.6-121, 39.3-71.7, 41.8-151, 21-135, 45.7-84.9, 22.1-142, 33.8-137, 22-136, 38.9-142, 21.8-92.7, 31-79.4, 37.6-96.3, 41-149, 46.7-86.8, 41.6-145, 24.6-83.5, 25-121, 44.7-114, 37.5-112, 36.5-153, 32.6-89.1, 25.9-154, 44.1-119, 26.3-122, 31.5-128, 36.4-89.8, 46.3-88.3, 23.1-72.9, 23.5-157, 32.3-154, 41.6-96.3, 29.8-132, 44.4-150, 43-139, 29.4-138, 28.8-121, 21.2-127, 41.9-77.5, 45.6-111, 32.5-111, 46.9-71.2, 36.6-153, 22.5-120, 25.3-95.1, 24.4-74.8, 34.8-84.3, 47.2-122, 27.8-88.8, 30.1-151, 31-150, 30-128, 26.3-77.2, 33-120, 44.2-144, 21.4-84.2, 46.1-71.9, 32.3-108, 42.1-117, 22.8-113, 42.3-116, 26.5-154, 31.8-152, 41.7-95.2, 23.4-82.3, 27.7-128, 34.3-72.5, 40.6-103, 21.9-83.1, 44.7-94.7, 45.3-94.2, 25.7-130, 32.7-89.8, 40.4-109, 46.1-110, 43.2-79.6, 39.9-144, 31.2-126, 37.1-81.3, 26.7-76.9, 21.5-130, 21.5-88.2, 26.3-150, 39.7-137, 43.3-144, 23.2-124, 30.8-133, 29.6-130, 47.1-84.3, 23.9-95.7, 29.3-86.2, 21.9-151, 41.6-129, 37.8-103, 23.4-85.6, 44.8-90.3, 35.6-95.4, 37.9-98.9, 39.3-115, 41.8-83.6, 26.3-127, 43.2-143, 30.5-92.6, 40.2-114, 36.5-138, 28.5-101, 40.9-77.3, 26.2-105, 41.3-147, 22.4-88.2, 28.2-79.4, 41.1-82.8, 21.2-74.1, 33.8-135, 46.4-123, 37.2-95.6, 26.5-84.3, 28.1-88.6, 27.1-92.6, 37.7-97.1, 22-94, 32.6-70.4, 36.1-116, 32.7-79.5, 31.1-124, 32.8-137, 22-82.4, 37.4-117, 36.8-155, 31.8-146, 22.9-110, 43.1-147, 43.7-77.8, 23.4-80, 40.5-95, 25.1-145, 30.8-148, 45.3-117, 22.2-81.9, 29.9-140, 37-109, 45.3-143, 38-135, 35.2-108, 37.5-84.4, 42-105, 34.8-155, 25.9-132, 36-94.4, 30.7-133, 37.4-98.2, 45.7-149, 42.5-83, 37.4-75.5, 46.6-101, 21.5-139, 29.3-148, 31.6-128, 29-130, 27.8-152, 23-98.3, 27.5-89.8, 43-149, 27.2-73.5, 21.8-139, 34.5-147, 28.9-79.8, 24.2-126, 32.4-144, 23.6-127, 37.5-129, 42.7-137, 28.6-78, 30-126, 44.9-109, 23.9-70.5, 44.1-120, 44.6-138, 24.2-139, 43.3-72.4, 34.7-137, 26.3-91, 27.4-114, 35.5-108, 26.2-101, 22.6-141, 43.1-101, 25.4-140, 32.3-157, 26.4-95.9, 28-134, 28.4-129, 37.8-82.9, 43.6-72, 33.9-128, 23.9-156, 22.4-131, 44.6-128, 33.1-129, 24.2-135, 41.4-101, 30-122, 41.2-155, 40.7-117, 42.8-88.8, 43.9longitudelatitude
Only 6 of 129 properties labelled California fall inside the state's bounding box. The same check finds 10 of 148 for TX, 1 of 146 for FL, 4 of 124 for NY.
Show as table (first 25 points)
longitudelatitudeGroup
-14431.2labelled CA
-12637.1labelled CA
-81.326.7labelled CA
-76.921.5labelled CA
-13021.5labelled CA
-88.226.3labelled CA
-15039.7labelled CA
-13743.3labelled CA
-14423.2labelled CA
-12430.8labelled CA
-13329.6labelled CA
-13047.1labelled CA
-84.323.9labelled CA
-95.729.3labelled CA
-86.221.9labelled CA
-15141.6labelled CA
-12937.8labelled CA
-10323.4labelled CA
-85.644.8labelled CA
-90.335.6labelled CA
-95.437.9labelled CA
-98.939.3labelled CA
-11541.8labelled CA
-83.626.3labelled CA
-12743.2labelled CA
Price per square foot across all sales
Cut-off at $50 per sqft. 134 sales fall below it and were dropped.
US$ per sqft, in bands of 50 (the last band includes everything above 1,050)
Show as table
US$ per sqft, in bands of 50 (the last band includes everything above 1,050)Value
0134
50349
100423
150260
200181
250115
300104
35099
40067
45065
50045
55055
60037
65032
70030
75029
80017
85019
90016
95013
10000
10500
Median sale price by year, US$ thousands
Real US prices rose by roughly 40% over this decade. Here the median barely moves, so the sale dates carry no market signal and the models ignore time.
Show as table
GroupValue
2016595k
2017643k
2018572k
2019600k
2020604k
2021613k
2022588k
2023638k
2024646k
2025636k
What correlates with sale price, 1,956 cleaned rowsPearson r, from −1 to 1
Assessed value tracks price almost perfectly. Size, the strongest physical signal, manages only 0.22, and bedrooms and bathrooms are close to zero.
Show as table
Featurer
Assessed value0.990
Living area (sqft)0.221
Lot size0.128
Year built-0.010
Bathrooms0.004
Bedrooms0.014

Approach

I trained two LightGBM models on the log of the sale price. Location is target-encoded at state, county and city level. Three engineered features capture sqft per bedroom, location times size, and age worked out from the year built.

  • Model A uses the county’s assessed value when one exists.
  • Model B uses only the property’s own facts, for new builds and anything without an assessment.

The service picks the model from the request. If an assessed value is sent, Model A answers. Otherwise Model B does.

How a valuation request flows
  1. Console or API clientproperty details
  2. FastAPI servicevalidates every field
  3. Routerassessed value given?yes → Model Ano → Model B
  4. SHAP TreeExplainerper-feature contributions
  5. Responseestimate, range and reasons

Every estimate comes back with its SHAP contributions, so a reviewer sees which features pushed the value up or down. A separate endpoint returns the five most similar sold properties.

use_model_a = raw.get("assessed_value") is not None
bundle = self.bundle_a if use_model_a else self.bundle_b

Evaluation

Both models were scored with 5-fold cross-validation and compared against simple baselines. These include the overall mean, the mean per state, and a linear regression.

Mean absolute percentage error, 5-fold CVlower is better
The gap between the two models is the main finding. Most of Model A's accuracy comes from the assessment itself, not from learned valuation skill.
Show as table
ItemValueNote
Model A · with assessment8.0%R² 0.98
Model B · fundamentals only44.9%R² 0.55

Model B is the honest test of what the other columns can predict. Its 45% error says more about this dataset than about the method. In real US data, size correlates 0.6 to 0.8 with price. Here it correlates 0.22.

What didn’t work

The physical attributes never carried much signal. Bedrooms and bathrooms correlate with price at close to zero, so location dominates both models.

Limitations

  • The coordinates and sale dates in the source are synthetic, so location comes only from state, county and city, and the model ignores time.
  • 1,956 rows is small for a valuation model. Estimates for rare states and for very large or very small homes are less certain.
  • Model A mostly echoes the assessed value. It is useful when an assessment exists, but it is not evidence of independent valuation skill.

Stack

  • Python
  • LightGBM
  • SHAP
  • scikit-learn
  • MLflow
  • FastAPI
  • Streamlit
  • Docker
  • Render

All work

shell · brightuzosike.devEsc to close · every command is also a link