SPB Git

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%
2.3 KB · 65 lines python
Raw Blame History
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