AUDIT — UQO Working Paper 9 (immo-wp9-spb-20260529)
Audit date: 2026-08-05
Original location: ~/Desktop/UQO/UQO_WP/immo-wp9-spb-20260529 (left untouched)
New clean repository: ~/Desktop/wp9_uqo
1. What the project is
UQO Working Paper No. 9, "A Grand Hedonic Model of the Canadian Housing Market" — a semi-logarithmic hedonic price model estimated on 140,931 Canadian MLS listings with 1,153 FSA (Forward Sortation Area) neighbourhood fixed effects, plus out-of-sample validation, robustness, quantile regressions, spatial diagnostics (Moran's I), leave-one-province-out cross-validation, and a variance decomposition.
2. File inventory (original, 55 files, ~749 MB)
Data
| File | Role |
|---|---|
realtor_mls_unique.duckdb (747 MB) |
Raw data. Single table listings, 172,019 rows, 81 columns (all VARCHAR except price_cad, lat, lon). This is the only data file; the paper's estimation sample (140,931 rows) is derived from it. |
Analysis code (analysis/)
| Script | What it does | Outputs |
|---|---|---|
estimate_ext.py |
80/20 out-of-sample validation of the FSA-FE hedonic model; living-area-elasticity heterogeneity by province; robustness across 6 samples; FSA location-premium ranking. | oos.json, heterogeneity.csv, robustness.csv, fsa_premia.csv |
estimate_ext2.py |
Quadratic (nonlinearity) spec; quantile regressions; distance-to-metro gradient; Moran's I on residuals; leave-one-province-out CV; variance decomposition. | ext2.json, lopo.csv, tables/quantile.tex, figures fig_nonlinear/quantile/gradient/moran/decomp.png |
make_assets.py |
Copies 9 upstream figures; builds fig_heterogeneity/premia/oos.png; writes tables/summary_stats/regression/robustness/oos.tex. |
figures + tables |
ml_benchmark.py |
XGBoost / LightGBM / PyTorch-MLP benchmarks vs. the hedonic model. | ml_compare.csv/json, tables/ml.tex, fig_ml_compare/importance.png — none of these outputs exist on disk and the paper never references them → dead code for this paper. |
Stored analysis outputs (reference values for verification)
analysis/oos.json, ext2.json, robustness.csv, lopo.csv, heterogeneity.csv,
fsa_premia.csv — all present and internally consistent with the numbers quoted in the
paper. These are the ground truth I verify the rebuilt pipeline against.
Figures (figures/, 7 present of 17 referenced by the paper)
Present: fig_price_dist, fig_fit, fig_resid (copied from upstream),
fig_decomp, fig_nonlinear, fig_gradient, fig_premia (locally generated).
Missing (10): fig_province_ppm2, fig_r2, fig_forest, fig_size_gradient,
fig_map, fig_fsa_map (upstream-only), and fig_quantile, fig_moran,
fig_heterogeneity, fig_oos (locally generated but deleted at some point).
Tables (tables/, 6 files)
summary_stats.tex, regression.tex, robustness.tex, quantile.tex, oos.tex,
lopo.tex. All referenced by the paper. Note: lopo.tex is hand-written — no script
produces it (estimate_ext2.py only writes lopo.csv).
LaTeX
| File | Role |
|---|---|
main.tex |
Real paper: preamble + metadata + \input{sections/*}. Already reasonably modular. |
main_web.tex |
Duplicate of main.tex plus an \includegraphics override that prints a "Figure indisponible" placeholder box when a figure file is missing. |
sections/*.tex (8) |
titlepage, introduction, literature, data, methodology, results, robustness, conclusion. |
references.bib |
32 entries, aer style with natbib. |
uq_logo.jpg |
UQO logo for the title page. |
Makefile, .latexmkrc |
latexmk build config. |
Dead / generated / junk files
- Build artifacts:
main*.aux/log/out/fls/fdb_latexmk,main_web.bbl/blg/synctex.gz— regenerate on compile. main_web.pdf(24 p., June 13): compiled with placeholder boxes instead of every figure — the\IfFileExistsfallback does not search\graphicspath, so even the 7 existing figures were replaced by boxes. There is no valid compiled PDF of the actual paper.main.pdf: does not exist (never successfully compiled, or deleted).ml_benchmark.py: dead for this paper (outputs unused, see above)..DS_Storefiles.
3. Critical findings
- The upstream pipeline is gone. All three live scripts read
~/Desktop/RE_DB_QC/hedonic/data/analysis.parquetand~/Desktop/RE_DB_QC/hedonic/output/fit.json/coef_M*.csv. That directory no longer exists anywhere on disk. The cleaning code (duckdb →analysis.parquet) and the M1–M5 estimation code were never inside this project. Consequence: the pipeline had to be reconstructed from (a) the raw DuckDB, which is in the project, (b) the precise description of sample construction insections/data.tex, and (c) the stored outputs/tables used as exact numerical targets. - No usable compiled PDF existed (see above).
- 10 of 17 figures missing and must be regenerated by the reconstructed pipeline.
- Paths in all scripts are absolute and machine-specific; no
requirements.txt, no README, no reproducibility instructions. - Hard-coded constants in scripts: the dashed reference line
0.547infig_nonlinearand the metro coordinates list (duplicated in two scripts). \WPemailin the paper issimon-pierre.boucher@uqo.ca(kept as-is in the rewrite; code headers usecontact@spboucher.aiper instruction).
4. Reference targets used for verification (from the original outputs)
- Sample: N = 140,931 (houses 82,334; condos 57,857); 1,153 absorbed FSAs; raw N = 172,019.
- Grand model M5: R² = 0.767; ln-living 0.547 (0.009); bath 0.109 (0.004); ln-lot 0.030 (0.004).
- Ladder R²: M1 0.464, M2 0.469, M3 0.567 (houses); M5 0.767.
- OOS: R² 0.764, RMSE(log) 0.282, median APE 15.8%, within ±10% 33.8%, ±20% 59.5% (train 112,732 / test 28,187).
- Moran's I: 0.459 → 0.082; gradient −0.0854; nonlinearity b1 = 1.065, b2 = −0.0526; LOPO mean 0.362.
- Full details in
analysis/*.{json,csv}andtables/*.texof the original.
5. Reproduction verification — rebuilt pipeline vs. original outputs
The cleaning pipeline was reconstructed from the paper's data section and calibrated
against the stored outputs. Decisive validated choices: _province (scraper seed) is the
province variable; living area prefers building.floor_area_measurements with banded
entries mapped to the upper bound of the band (this uniquely matches the published
living-area distribution and the M3 elasticity of 0.585 exactly); bedrooms "3 + 1" summed;
FSAs with <25 listings pooled to <PROV>_other; 1%/99% trim of price and living area.
Everything below refers to results/reproduced/ (script output). The paper itself uses
results/reference/ — the original stored outputs — so no published number changed.
| Quantity | Original (paper) | Reproduced | Note |
|---|---|---|---|
| Sample N | 140,931 | 142,146 | +0.86% |
| Houses / Condos | 82,334 / 57,857 | 83,542 / 57,860 | condos match to 3 listings |
| Absorbed FSA levels | 1,153 | 1,155 | |
| R² M1 / M2 / M3 (houses) | 0.464 / 0.469 / 0.567 | 0.467 / 0.470 / 0.569 | |
| R² M4 / M5 | 0.762 / 0.767 | 0.766 / 0.770 | |
| M5 ln living | 0.547 (0.009) | 0.530 (0.010) | ~2 SE apart |
| M5 full bathrooms | 0.109 (0.004) | 0.111 (0.003) | ✓ |
| M5 half baths / bedrooms | −0.036 / −0.002 | −0.032 / −0.003 | ✓ |
| M5 ln lot / has lot | 0.030 / −0.196 | 0.061 / −0.417 | see (a) below |
| OOS R² / RMSE / med. APE | 0.764 / 0.282 / 15.8% | 0.770 / 0.280 / 15.6% | ✓ |
| OOS within ±10 / ±20% | 33.8% / 59.5% | 34.2% / 60.0% | ✓ |
| Moran's I struct → grand | 0.459 → 0.082 | 0.494 → 0.072 | same conclusion (−85%) |
| Gradient (β log-dist) | −0.0854 | −0.0821 | ✓ |
| Nonlinearity b1 / b2 | 1.065 / −0.0526 | 1.145 / −0.0624 | same shape |
| LOPO mean R² | 0.362 | 0.315 | same ranking (ON best, AB negative) |
| Elasticity by province | 0.44 (NL) – 0.66 (MB) | 0.42 (NL) – 0.72 (SK) | same coastal-vs-Prairies pattern |
| Quantile elasticity τ=.1→.9 | 0.557 → 0.598 | 0.591 → 0.585 | flatter but same level |
Remaining discrepancies (flagged, not silently changed)
(a) Lot-size parsing. The free-text land.size_total parser could not be recovered
exactly. The original summary stats (mean 2,621 / SD 10,818 m²) imply the original
parser bounded large acreages; the reconstruction treats the top 1% of positive parsed
lots as missing, which matches the SD but shifts the two collinear lot coefficients
(ln_lot, has_lot). All other structural coefficients are essentially unaffected.
(b) Sample off by +1,215 listings (+0.86%), concentrated in houses at the 5,000-sqft
living-area band boundary — an artifact of how the original trim treated the mass of
listings at exactly the 99th-percentile value. Counts by province and condo counts
match almost exactly.
(c) fig_size_gradient / condominium claim. The original figure was lost. Regenerated
raw medians show condominiums listing above houses of equal size (they concentrate
in expensive metros), contradicting one descriptive sentence in the original results
section; the sentence was rewritten to describe the regenerated figure and the
(supported) conditional claim. See CHANGES.md — requires author review.
(d) analysis/ml_benchmark.py (XGBoost/LightGBM/PyTorch benchmark) was dropped: its
outputs do not exist and the paper never references them.
(e) Original main_web.pdf had placeholder boxes instead of all figures (its
\IfFileExists fallback ignored \graphicspath), so no valid compiled PDF of the
original paper existed; page-level comparison against the original PDF was therefore
impossible. The 7 surviving original PNGs were compared instead — the regenerated
versions are visually identical for the results-driven figures (e.g. fig_premia).