Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/370.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add LA-level main residence value calibration targets for all 360 UK local authorities, built from directly observed indicators (HMLR UK HPI × English Housing Survey ownership share × Census household count) and wired into the LA reweighter alongside the existing tenure and rent targets.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- ONS income: ONS small area income estimates
- Tenure: English Housing Survey
- Private rent: VOA/ONS private rental market statistics
- Main residence value: HMLR UK HPI × ownership share × household count
"""

from policyengine_uk import Microsimulation
Expand Down Expand Up @@ -38,6 +39,7 @@
load_tenure_data,
load_private_rents,
)
from policyengine_uk_data.targets.sources.la_land import load_la_avg_prices


def create_local_authority_target_matrix(
Expand Down Expand Up @@ -252,6 +254,47 @@ def create_local_authority_target_matrix(
national_rent * la_household_share,
)

# ── Main residence value (HMLR × ownership share × households) ─
# Derived proxy target: a product of three observed inputs, not a
# directly observed LA total. Mirrors the private-rent target's
# multiplicative shape. Lineage caveat: matrix col is WAS-imputed
# stock wealth; the target uses HMLR HPI transaction prices —
# different price concepts on the two sides of the constraint.
# See targets/sources/la_land.py for full provenance.
la_prices = load_la_avg_prices()
tenure_merged = tenure_merged.merge(
la_prices[["code", "avg_house_price"]], on="code", how="left"
)

matrix["housing/main_residence_value"] = sim.calculate(
"main_residence_value"
).values

ownership_share_la = (
tenure_merged["owned_outright_pct"].fillna(0)
+ tenure_merged["owned_mortgage_pct"].fillna(0)
) / 100
tenure_merged["main_residence_value_target"] = (
tenure_merged["avg_house_price"]
* ownership_share_la
* tenure_merged["households"]
)

has_property = (
tenure_merged["avg_house_price"].notna()
& tenure_merged["owned_outright_pct"].notna()
& tenure_merged["households"].notna()
).values
national_property = (
original_weights * matrix["housing/main_residence_value"].values
).sum()

y["housing/main_residence_value"] = np.where(
has_property,
tenure_merged["main_residence_value_target"].values,
national_property * la_household_share,
)

# ── Country mask ───────────────────────────────────────────────
country_mask = create_country_mask(
household_countries=sim.calculate("country").values,
Expand Down
Loading