spb/wp11_uqo Public
UQO Working Paper No. 11 — Half a million prices, twenty models: a systematic assessment of hedonic specifications.
TeX 54.7%
Python 45.2%
1#!/usr/bin/env python32# Author: Simon-Pierre Boucher — contact@spboucher.ai3"""Step 02 — The horse race.45Fits the twenty registry models under (i) a random 80/20 holdout and6(ii) a forward-in-time holdout (train < 2025, test 2025–2026), plus the7Box–Cox profile likelihood and summary statistics.89Writes: horserace_random.csv, horserace_temporal.csv, boxcox.csv,10summary_stats.csv, sample_counts.csv1112Usage: python scripts/02_horserace.py13"""14import sys15from pathlib import Path1617import pandas as pd1819sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))2021from wp11 import config, models, sample # noqa: E402222324def main() -> None:25 config.ensure_dirs()26 df = sample.load()27 out = config.REPRODUCED2829 # ------------------------------------------------------------ Table 130 desc = {"price": "Sale price ($)", "area": "Floor area (m2)",31 "lot": "Lot area (m2)", "age": "Building age (years)",32 "floors": "Storeys", "units": "Dwelling units"}33 rows = []34 for var, label in desc.items():35 s = df[var]36 rows.append({"variable": label, "n": len(s), "mean": s.mean(),37 "sd": s.std(), "p10": s.quantile(.10), "p50": s.median(),38 "p90": s.quantile(.90)})39 pd.DataFrame(rows).to_csv(out / "summary_stats.csv", index=False)40 counts = {"n_sales": len(df), "n_munis": df["muni"].nunique(),41 "n_grid": df["grid"].nunique(),42 "n_grid5": df["grid5"].nunique()}43 for k, v in df.groupby("prop_class").size().items():44 counts[f"n_{k}"] = int(v)45 pd.Series(counts).to_csv(out / "sample_counts.csv")4647 # ------------------------------------------------------------ Box–Cox profile48 d = models.add_derived(df)49 d["sale_year_c"] = d["sale_year"].astype(str)50 train, _ = models.split(d, "random")51 lam, prof = models.fit_boxcox(train, models.NUM_LEVELS,52 models.CATS + ["muni", "quarter"])53 prof["chosen"] = prof["lambda"] == lam54 prof.to_csv(out / "boxcox.csv", index=False)55 print(f"Box–Cox lambda* = {lam}")5657 # ------------------------------------------------------------ the race58 for scheme in ("random", "temporal"):59 res = models.run_horserace(df, scheme)60 res.to_csv(out / f"horserace_{scheme}.csv", index=False)616263if __name__ == "__main__":64 main()65