SPB Git forge

spb/wp14_uqo

Public
3commits 1branches 1releases
1.5 MBsize
maindefault branch
1 h agolast push
Python 52.4% TeX 47.6%

révision scientifique complète (editorial board, 2026-10-17)

Version 2.1 (October 2026) : fuite du prix demandé dans les descriptions DuProprio documentée et quantifiée ;
indicatrice de langue (43 % EN) ; EF RTA ; validation croisée spatiale ; bootstrap en grappes ; accord inter-sources
des extractions ; sensibilité du blend ; affirmation « largest gain of any feature block » retirée ; température
vision / alias de modèles / 15 aménités / 21 régresseurs vision corrigés ; bibliographie 35 -> 53 références vérifiées
(DOI) ; docs/REVIEW-REPORT.md + docs/CHANGELOG-REVISION.md ; scripts 18-19 ; chemins exec des scripts 11-16 réparés.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Simon-Pierre Boucher committed 1 h ago (Oct 18, 2026) parent 5f1b8f8

32 changed files +3,872 −506

modified README.md +77 −47
@@ -3,7 +3,7 @@
3 3
4 4 **UQO Working Paper No. 14** — *Evidence from Quebec Real-Estate Listings*
5 5
6 −[![Paper](https://img.shields.io/badge/paper-PDF%20(33%20p.)-b31b1b?logo=latex&logoColor=white)](paper/main.pdf)
6 +[![Paper](https://img.shields.io/badge/paper-PDF-b31b1b?logo=latex&logoColor=white)](paper/main.pdf)
7 7 [![Python](https://img.shields.io/badge/python-3.11%2B-3776AB?logo=python&logoColor=white)](requirements.txt)
8 8 [![Data](https://img.shields.io/badge/data-19%2C123%20house%20listings%20·%20Realtor.ca%20%2B%20DuProprio-4051b5)](data/raw/README.md)
9 9 [![Reproducible](https://img.shields.io/badge/reproducible-end--to--end%20pipeline-2e7d32)](scripts/)
@@ -12,11 +12,14 @@
12 12
13 13 > **TL;DR** — Much of a home's value is recorded only in the free text and the photographs of its listing. We use a
14 14 > large language model to turn each description into structured specifications and a vision–language model to turn the
15 −> façade photo into objective visual attributes, both under strict JSON schemas. On 19,123 Quebec house listings the two
16 −> AI layers lift the R² of an already rich log-linear hedonic model from 0.717 to 0.755; the vision layer is the largest
17 −> out-of-sample gain of any feature block for every estimator (OLS, gradient boosting, neural nets); the best ensemble
18 −> reaches out-of-sample R² 0.779 with a 14.0% median absolute error. Implicit prices are economically sensible (+2.3% per
19 −> point of curb appeal, +3–4% for brick or stone) and survive municipality-clustered inference.
15 +> façade photo into visual attributes, both under strict JSON schemas. On 19,123 Quebec house listings the two AI
16 +> layers lift the R² of an already rich log-linear hedonic model from 0.717 to 0.755 (3.0 of the 3.8 points come from
17 +> the photo); the vision layer lifts every estimator (OLS, random forest, gradient boosting, neural nets) and adds more
18 +> to OLS than switching to a random forest; the best ensemble reaches out-of-sample R² 0.779 with a 14.0% median
19 +> absolute percentage error. Implicit prices are economically sensible (+2.3% per point of curb appeal, +3–4% for
20 +> brick or stone) and survive municipality-clustered inference, a cluster bootstrap, postal-code (RTA) fixed effects
21 +> and language controls. Caveat documented in v2.1: every DuProprio description embeds the asking price, so the text
22 +> layer's gain is credible only on Realtor.ca (+0.6 pt in-sample).
20 23
21 24 ---
22 25
@@ -24,38 +27,44 @@
24 27
25 28 | Item | Value |
26 29 |---|---|
27 −| Version | 2.0 (July 2026) — revised draft after an internal review round (`scripts/13_revision_extras.py`) |
30 +| Version | **2.1 (October 2026)** — revised 2026-10-17 by a full editorial-board review (`docs/REVIEW-REPORT.md`, `docs/CHANGELOG-REVISION.md`); 2.0 (July 2026) after an internal review round (`scripts/13_revision_extras.py`) |
28 31 | SSRN | not yet submitted |
29 −| Journal | Elsevier-format highlights prepared (`docs/highlights_elsevier.tex`); target to be decided |
32 +| Journal | Elsevier-format highlights prepared (`docs/highlights_elsevier.tex`); target to be decided (Journal of Housing Economics considered) |
30 33 | Code & data | reproducible from `scripts/` run from the repository root; raw crawls and LLM caches not committed |
34 +| Pre-revision reference | `docs/main_pre-revision-20261017.pdf` (v2.0), git tag `pre-revision-20261017` |
31 35
32 36 ## 🎯 What this paper does
33 37
34 38 Hedonic models are bounded by the characteristics an analyst can measure. We treat general-purpose AI models as a
35 39 **feature-engineering technology**: each listing description is parsed by an LLM into a fixed schema of property
36 40 specifications (renovations, finishes, systems, extras), and each façade photograph is parsed by a vision–language model
37 −into objective attributes (cladding, storeys, garage, curb appeal). The outputs are ordinary econometric variables and
38 −enter a log-linear hedonic regression with city fixed effects:
41 +into visual attributes (cladding, storeys, garage, curb appeal). The outputs are ordinary econometric variables and
42 +enter a log-linear hedonic regression with municipality fixed effects:
39 43
40 44 ```
41 −ln P_i = α_city(i) + X_i'β + S_i'γ (LLM text layer) + V_i'δ (vision layer) + ε_i
45 +ln P_i = α + β ln Area_i + γ ln Lot_i + controls_i'δ + S_i'δ_g (LLM text layer) + V_i'δ_v (vision layer) + μ_city(i) + τ_type(i) + ε_i
42 46 ```
43 47
44 −We then measure, under identical 5-fold splits, how much each layer adds for OLS, histogram gradient boosting, a
45 −residual MLP with city/type embeddings, and a stacked blend.
48 +We then measure, under identical 5-fold splits (random and spatially grouped by RTA), how much each layer adds for OLS,
49 +random forest, histogram gradient boosting, a residual MLP with city/type embeddings, and an equal-weight blend; and we
50 +test the AI implicit prices against clustered, bootstrap and RTA-level inference, language controls and the
51 +platform-specific leakage of the asking price into the text.
46 52
47 53 ## 🏆 Headline results
48 54
49 55 | Object | Estimate |
50 56 |---|---|
51 −| Base hedonic OLS (structural + location), in-sample R² | 0.717 |
52 −| + LLM text layer + vision layer | 0.755 |
53 −| Out-of-sample R², OLS: base → +text → +text+vision (5-fold) | 0.52 → 0.62 → … (see `paper/tables/ablation.tex`) |
54 −| Out-of-sample R², GBM: base → +text | 0.69 → 0.73 |
55 −| Best ensemble (robust NN + GBM blend) | R²_OOS = 0.779, median absolute error 14.0% |
56 −| Curb-appeal score (1–5) | +2.3% per point |
57 −| Brick / stone façade | +3–4% |
58 −| Inference | HC3 and municipality-clustered standard errors agree |
57 +| Base hedonic OLS (structural + amenity + rooms + listing, city FE), in-sample R² (Model A, N = 9,446) | 0.717 |
58 +| + LLM text layer / + vision layer | 0.725 / 0.755 (+0.8 / +3.0 pt) |
59 +| Same, Realtor.ca only (text never contains the price) | 0.779 → 0.786 → 0.810 (+0.6 / +2.4 pt) |
60 +| Out-of-sample R², OLS: rooms+listing → +text → +vision (random 5-fold) | 0.619 → 0.628 → 0.686 |
61 +| Out-of-sample R², GBM: rooms+listing → +text → +vision | 0.729 → 0.743 → 0.770 |
62 +| Same under spatial (RTA-grouped) folds, OLS / GBM | 0.561 → 0.568 → 0.638 / 0.690 → 0.703 → 0.734 |
63 +| Best ensemble (robust NN + GBM, w = 0.5 fixed) | R²_OOS = 0.779, MdAPE 14.0% (grid 0.3–0.7: 0.771–0.780; nested weight 0.775) |
64 +| Curb-appeal score (1–10) | +2.3% per point (cluster-bootstrap 95% CI [0.1%, 3.6%]) |
65 +| Brick / stone / vinyl façade | +3.4% / +3.7% / −4.4% |
66 +| English-language description (conditional on everything, within municipality) | +7.2% (Model A), +11.8% (Model B); AI coefficients unchanged |
67 +| Inference | municipality-clustered SEs (195), HC3, pairs cluster bootstrap (299 reps), RTA FE (250) agree on the AI block |
59 68
60 69 ## 🗂 Repository layout
61 70
@@ -73,9 +82,11 @@ hedonic-llm-vision-features/
73 82 │ ├── 06_hedonic_ml_cv.py / 07_torch_nn.py / 08_torch_nn_robust.py
74 83 │ ├── 09_v5_ml_text_vision.py / 10_final_blend.py
75 84 │ ├── 11_extras_v5_robustness.py / 12_ablation_extras.py / 13_revision_extras.py
76 −│ └── 14_figures_v5.py / 15_make_tables.py / 16_make_tables_v5.py / 17_gen_regression_table.py
77 −├── results/ # hedonic summaries (txt), ablation_extras.json, revision_extras.json
78 −├── docs/ # METHODOLOGIE.md + methodologie.tex/pdf (French data & method note), Elsevier highlights
85 +│ ├── 14_figures_v5.py / 15_make_tables.py / 16_make_tables_v5.py / 17_gen_regression_table.py
86 +│ └── 18_revision_language_leakage_spatial.py # v2.1: language, leakage, RTA FE, spatial CV, bootstrap, agreement, blend weights
87 +├── results/ # hedonic summaries (txt), ablation_extras.json, revision_extras.json, revision_2026-10-17.json (+ .log)
88 +├── docs/ # METHODOLOGIE.md + methodologie.tex/pdf (French data & method note), Elsevier highlights,
89 +│ # REVIEW-REPORT.md, CHANGELOG-REVISION.md, main_pre-revision-20261017.pdf
79 90 └── paper/ # main.tex + sections/ + tables/ + figures/ + references.bib → main.pdf
80 91 ```
81 92
@@ -92,46 +103,58 @@ python3 scripts/05_hedonic_ols_v4_text.py
92 103 python3 scripts/08_torch_nn_robust.py && python3 scripts/09_v5_ml_text_vision.py && python3 scripts/10_final_blend.py
93 104 python3 scripts/11_extras_v5_robustness.py && python3 scripts/12_ablation_extras.py && python3 scripts/13_revision_extras.py
94 105 python3 scripts/14_figures_v5.py && python3 scripts/16_make_tables_v5.py && python3 scripts/17_gen_regression_table.py
106 +python3 scripts/18_revision_language_leakage_spatial.py # v2.1 robustness tables (≈5 min, no API calls)
95 107 cd paper && make # latexmk → main.pdf
96 108 ```
97 109
98 −LLM extraction ≈ 600 tokens in / 110 out per listing (24 threads); vision ≈ 2,470 tokens in per image (16 threads);
99 −both are resumable. Econometrics and ML ≈ 20 minutes on an Apple-silicon laptop (PyTorch on MPS).
110 +LLM extraction ≈ 600 tokens in / 110 out per listing (24 threads, temperature 0); vision ≈ 2,470 tokens in per image
111 +(16 threads, API default temperature); both are resumable. Econometrics and ML ≈ 25 minutes on an Apple-silicon laptop
112 +(PyTorch on MPS). Scripts 11–14, 16 and 18 rebuild the design matrix by executing the preamble of
113 +`scripts/09_v5_ml_text_vision.py`.
100 114
101 115 ## 🗃 Data
102 116
103 117 27,302 Realtor.ca and 7,693 DuProprio listings crawled on 2026-07-03; houses (single-family and townhouse) with a valid
104 −price retained, outliers removed by the rules in `docs/METHODOLOGIE.md` §3, giving 19,123 observations. Structural
105 −variables (living area, lot, rooms, year built, type, city), text-derived specifications and façade attributes. See
106 −`data/raw/README.md` and `data/processed/README.md`.
118 +price retained, outliers removed by the rules in `docs/METHODOLOGIE.md` §3, giving 19,123 observations (15,356
119 +Realtor.ca, 3,767 DuProprio). Structural variables (living area, lot, rooms, year built, type, city, postal code),
120 +15 bilingual keyword amenities, text-derived specifications (18 regressors) and façade attributes (21 regressors).
121 +Language of the description (stop-word rule): 44.6% French, 43.1% English, 12.3% mixed. **Every DuProprio
122 +"description" embeds the platform's structured sheet including the asking price**; Realtor.ca descriptions never do.
123 +See `data/raw/README.md` and `data/processed/README.md`.
107 124
108 125 ## 🔬 Methodology
109 126
110 −1. **AI feature layers** with strict JSON schemas and retry/backoff — `scripts/01_…`, `scripts/02_…`.
111 −2. **Log-linear hedonic OLS** with city fixed effects, HC3 and municipality-clustered SEs — `scripts/05_…`, `scripts/13_…`.
112 −3. **Ablation by feature block × estimator** under identical folds (OLS, GBM, NN) — `scripts/09_…`, `scripts/12_…`.
113 −4. **Stacked blend** of the robust NN and HistGradientBoosting — `scripts/10_final_blend.py`.
114 −5. **Robustness** by segment, extreme city effects, calibration and importance plots — `scripts/11_…`, `scripts/14_…`.
127 +1. **AI feature layers** with strict JSON schemas and retry/backoff — `scripts/01_…`, `scripts/02_…` (raw outputs archived; regressors regenerable without API calls).
128 +2. **Log-linear hedonic OLS** with municipality fixed effects, HC3, municipality-clustered SEs and a pairs cluster bootstrap — `scripts/05_…`, `scripts/13_…`, `scripts/18_…`.
129 +3. **Ablation by feature block × estimator** under identical random folds and under RTA-grouped (spatial) folds — `scripts/09_…`, `scripts/12_…`, `scripts/18_…`.
130 +4. **Equal-weight blend** of the robust NN and HistGradientBoosting, with weight-grid and nested-weight checks — `scripts/10_…`, `scripts/18_…`.
131 +5. **Robustness** by segment, language, platform (leakage), RTA fixed effects; inter-source agreement of the AI attributes — `scripts/11_…`, `scripts/18_…`.
115 132
116 133 ## 🖼 Figures & tables inventory
117 134
118 135 | Figure | Content |
119 136 |---|---|
120 −| `fig_ablation` | out-of-sample gain by feature block and estimator |
137 +| `fig_ablation` | out-of-sample gain by feature block and estimator (random folds) |
121 138 | `fig_calibration` | predicted vs actual, blend |
122 −| `fig_importance` | feature importance |
123 −| `fig_premia` | location premia |
124 −| `fig_validation` | validation of AI-extracted attributes |
139 +| `fig_importance` | feature-block permutation importance (GBM) |
140 +| `fig_premia` | municipality fixed effects (extremes) |
141 +| `fig_validation` | median price by vision score |
125 142 | `nn_diagram` (TikZ) | architecture of the robust NN |
126 143
127 −Tables (`paper/tables/*.tex`): summary stats, regression (A/B panels), models, ablation, robustness, AI summary.
144 +Tables (`paper/tables/*.tex`): summary stats, AI summary, regression (A/B, clustered SEs), models (two panels),
145 +ablation, robustness by subsample, and — new in v2.1 — `rob_ai_coefs` (language / RTA FE / Realtor-only / bootstrap CI),
146 +`rob_language_source` (gains by language, platform, price-in-text), `rob_spatial` (random vs RTA-grouped CV),
147 +`agreement` (AI attributes vs independent sources). Appendix: feature definitions, sample flow, language rule.
128 148
129 149 ## ⚠️ Limitations
130 150
131 −- Asking prices from listings, single cross-section (July 2026).
132 −- Realtor.ca listings lack coordinates; location enters through city fixed effects and postal codes.
133 −- LLM/vision outputs are model-dependent; schemas and prompts are versioned in the scripts.
134 −- API costs make the extraction layers the expensive part of replication.
151 +- Asking prices from listings, single cross-section (3 July 2026); no forward-in-time holdout possible.
152 +- The LLM read the asking price for the 19.7% of listings from DuProprio; text-layer gains on that platform are an upper bound.
153 +- No human-labelled validation set: extraction error is measured only by agreement with keyword rules and structured fields (high for garage/fireplace/pool, low for waterfront, visual setting and visual building type).
154 +- Vision extraction at API default temperature; model identifiers are provider aliases (snapshots not pinned); test–retest variability not measured.
155 +- Realtor.ca listings lack coordinates; location enters through municipality or RTA fixed effects.
156 +- English-language indicator carries a large conditional premium that proxies for unobserved location/clientele.
157 +- API costs (~$60) make the extraction layers the expensive part of replication.
135 158
136 159 ## 📝 Citation
137 160
@@ -143,7 +166,8 @@ Tables (`paper/tables/*.tex`): summary stats, regression (A/B panels), models, a
143 166 type = {Working Paper},
144 167 number = {14},
145 168 year = {2026},
146 − month = {July}
169 + month = {October},
170 + note = {Version 2.1}
147 171 }
148 172 ```
149 173
@@ -155,10 +179,16 @@ Université du Québec en Outaouais (UQO), Gatineau, QC.
155 179
156 180 ## 🗒 Notes
157 181
182 +- **Revision 2026-10-17 (v2.1)**: full scientific review by an editorial-board agent — report in `docs/REVIEW-REPORT.md`,
183 + audit trail in `docs/CHANGELOG-REVISION.md`. Main corrections: the claim that the vision layer was "the largest gain
184 + of any feature block" was wrong (the rooms/listing block adds more) and is withdrawn; the asking-price leakage in
185 + DuProprio descriptions is documented and quantified; vision temperature/model-version statements corrected; 15 (not 17)
186 + keyword amenities; 12 vision fields → 21 regressors; reference category of the city FE made explicit; bibliography
187 + verified (all DOIs) and extended from 35 to 53 references. Headline numbers of v2.0 are unchanged.
158 188 - Numbering: this paper was first issued as "Working Paper No. 11" (July 2026); number 11 belongs to *Half a Million
159 − Prices, Twenty Models*. Renumbered **14** on 2026-10-17; cover date fixed to July 2026 (was `\today`).
189 + Prices, Twenty Models*. Renumbered **14** on 2026-10-17.
160 190 - Harmonised 2026-10-17: flat scripts moved to `scripts/` (numbered), data to `data/`, logs removed, summaries to
161 191 `results/`, methodology note to `docs/`; file paths inside the scripts now point to `data/…` and `results/…`
162 − (run them from the repository root). Bibliography switched from `plainnat` to `aer`.
192 + (run them from the repository root). Bibliography style `aer`.
163 193 - The script `13_revision_extras.py` was written for a Journal of Housing Economics revision round (clustered SEs,
164 − in-sample ablation, extreme city effects).
194 + in-sample ablation, extreme city effects); `18_…` for the October 2026 review.
added docs/CHANGELOG-REVISION.md +131 −0
@@ -0,0 +1,131 @@
1 +<!-- Author: Simon-Pierre Boucher — simon-pierre.boucher@uqo.ca -->
2 +# CHANGELOG — révision scientifique complète (editorial board, 2026-10-17)
3 +
4 +**Papier** : UQO Working Paper No. 14, *Hedonic House Pricing with Language-Model and Vision Features: Evidence from
5 +Quebec Real-Estate Listings*. Version examinée : 2.0 (juillet 2026, 33 p., 35 réf.). Version produite : **2.1
6 +(octobre 2026)**. État de départ figé : tag git `pre-revision-20261017`, copie `docs/main_pre-revision-20261017.pdf`.
7 +Rapport de l'editorial board : `docs/REVIEW-REPORT.md`.
8 +
9 +Convention : chaque entrée donne *où* / *avant* / *après* / *pourquoi* / *preuve*. Aucun chiffre du texte n'a changé sans
10 +résultat recalculé ; tous les nouveaux chiffres proviennent de `scripts/18_revision_language_leakage_spatial.py`
11 +(sortie `results/revision_2026-10-17.json` ; journal local `results/revision_2026-10-17.log`, non commité car `*.log` est ignoré) et des tables générées par
12 +`scripts/19_make_revision_tables.py`.
13 +
14 +---
15 +
16 +## 1. Corrections majeures (fond)
17 +
18 +| # | Où | Avant (v2.0) | Après (v2.1) | Pourquoi / preuve |
19 +|---|---|---|---|---|
20 +| 1 | Résumé, intro (contribution 1), §Benchmark, §Ablation, conclusion, légende fig. ablation, highlights, README | « the vision layer delivers the **largest out-of-sample gain of any feature block** for every estimator » ; « the vision layer the single largest jump for every estimator » | La couche vision est le plus grand des **deux incréments IA** et apporte plus à OLS que le passage OLS → forêt aléatoire ; le bloc **pièces + listing** apporte davantage (+9,8 pt OLS, +3,7 pt GBM contre +5,8 / +2,7 pour la vision) | Contredit par la propre table d'ablation du papier (`results/ablation_extras.json` : L1→L2 0,521→0,619 OLS, 0,692→0,729 GBM) |
21 +| 2 | §Data (nouvelle §3.4), §Robustness (nouvelle §7.3), §Limitations, intro, résumé, conclusion | « Because both extractions are … **blind to the listing price** » ; « The models never observe the price » | 100 % des 3 767 descriptions DuProprio contiennent la fiche structurée dont « Asking Price $… » (= prix enregistré dans 3 761 cas) et 46,6 % l'évaluation municipale ; 2,5 % des descriptions Realtor.ca contiennent un montant, aucune le prix demandé. Le LLM a lu la variable dépendante pour 19,7 % de l'échantillon. Gains texte reportés par plateforme ; Realtor.ca retenu comme estimation crédible (+0,6 pt in-sample vs +3,3 pt DuProprio) ; la vision n'est pas exposée | Inspection des descriptions (`data/processed/maisons_avec_prix.json`) ; `revision_2026-10-17.json["text_leakage"]`, `["ablation_A"]` |
22 +| 3 | §Methodology (vision), Annexe A, §Limitations | « Decoding temperature is set to zero for determinism » appliqué aux deux couches ; « deterministic » | Température 0 pour le texte seulement ; l'appel vision (`02_facade_vision.py`) ne fixe pas la température → défaut API ; alias de modèles non épinglés ; sorties brutes archivées ; variabilité test–retest non mesurée ; « deterministic » retiré | Lecture de `scripts/02_facade_vision.py` (pas de clé `temperature`) |
23 +| 4 | §Methodology, §Data (nouvelle §3.4), §Robustness (§7.2), table `rob_ai_coefs` col. (2), annexe C | Langue jamais mentionnée | Détection FR/EN/mixte (stop-words, ratio 1,5 comme WP 2) : 44,6 % FR, 43,1 % EN, 12,3 % mixte ; Realtor.ca 41,9 % EN ; indicatrice EN +7,2 % (A), +11,8 % (B) conditionnel ; coefficients IA inchangés ; gains des deux couches présents dans chaque langue | `revision_2026-10-17.json["language"]`, `["ols"]["A_lang"]`, `["ablation_A"]["fr"/"en"]`, `["cv"]` |
24 +| 5 | §Methodology (localisation), table `rob_ai_coefs` col. (3), §7.4 | EF municipalité seulement | EF **RTA** (419 RTA, 250 avec ≥ 20 annonces) : R² A 0,755 → 0,764, B 0,725 → 0,726 ; coefficients IA stables ; SE groupées par RTA | `["ols"]["A_fsa"]`, `["fsa"]` |
25 +| 6 | §Methodology (validation OOS), §7.4, table `rob_spatial` | Validation croisée aléatoire seulement | GroupKFold par RTA : OLS 0,561 → 0,568 → 0,638, GBM 0,690 → 0,703 → 0,734 (vs 0,619/0,628/0,686 et 0,729/0,743/0,770 en aléatoire) ; incréments IA préservés ; MdAPE +1,5 pt | `["cv"]["spatial_fsa"]` |
26 +| 7 | §Methodology (inférence), §7.1, table `rob_ai_coefs` dernière colonne | SE groupées analytiques + HC3 | Bootstrap en grappes par paires (299 réplications, 195 municipalités) ; IC 95 % ; discussion des grappes très inégales (regroupement « autres » = 5 006 annonces de 999 municipalités) | `["cluster_bootstrap_A"]` |
27 +| 8 | §Results (nouvelle partie de §5.3 Validation), table `agreement`, §Limitations | « we do not perform systematic human validation » (sans mesure d'erreur) | Mesure indirecte de l'erreur d'extraction : accord LLM vs mots-clés (κ 0,96 garage … 0,50 bord de l'eau, précision 40 %), vision vs texte/structuré (garage κ 0,35 ; étages exacts 60 %, ± ½ étage 89 % ; « maison en rangée » précision 18 %) ; champs non fiables identifiés et relégués au rôle de contrôles | `["agreement"]` |
28 +| 9 | §Benchmark (blend), table `models` note | « simple 50/50 average », poids non discuté (le script 10 cherchait le meilleur poids sur les prédictions test) | Poids fixé a priori ; grille w ∈ {0,3…0,7} → 0,771–0,780 ; poids choisi en CV emboîtée → 0,775 ; w = 0,5 exactement optimal pour la version texte seule | `["blend"]` (prédictions NN v5 récupérées exactement : R² 0,747) |
29 +| 10 | §Methodology (aménités) | « Seventeen amenity indicators » | **Fifteen** (15 clés dans `AMEN`) | `scripts/09_v5_ml_text_vision.py` |
30 +| 11 | §Methodology, tables `models`/`ablation` notes, annexe A | « twelve façade vision attributes » (comme régresseurs) ; curb appeal « 1–5 » dans le README | 12 champs vision → **21 régresseurs** ; 20 champs texte → 18 régresseurs ; curb appeal 1–10 partout | Comptage `vv`, `gv` ; `# feats` 56 → 77 |
31 +| 12 | §Results (EF ville), légende fig. premia, intro | « relative to the reference city » | Catégorie de référence = municipalités regroupées (< 20 annonces) ; Sainte-Anne-des-Plaines (+143 %) identifié sur 2 observations (dont un domaine à 5,5 M$) | Ordre des niveaux patsy (`__other__` premier) ; `maisons_hedonic_v4.csv` |
32 +| 13 | §Results (âge) | « small negative linear term and a positive quadratic » | Chiffré : −0,4 %/an, +2,6 × 10⁻⁵, minimum vers 70 ans | `results/hedonic_v4_summary.txt` (age2 = 2,612e-05) |
33 +| 14 | §Results (vision) | Score de condition visuelle non discuté | Non significatif dans A (p = 0,15) ; colinéarité des trois scores visuels → lecture en bloc | `results/revision_extras.json` (`v_cond`) |
34 +| 15 | Résumé | « render the outputs reproducible econometric variables » | « auditable » ; ajout des résultats langue, fuite, validation spatiale | — |
35 +
36 +## 2. Corrections éditoriales (forme)
37 +
38 +- Intro : ordre des blocs de la couche NN corrigé (BN → Lin₅₁₂ → GELU → Drop → Lin₂₅₆ → Drop, conforme au code) dans §6.5 et
39 + le diagramme TikZ ; dimension d'embedding « about 29 » → « about 30 » (1,6 × 195^0,56 = 30,7).
40 +- Éq. (hedonic) : blocs explicités (δ_s, δ_a, δ_r, δ_m, δ_g, δ_v) au lieu de `X_i'δ` qui dupliquait ln Area / ln Lot ;
41 + équation scindée sur deux lignes (débordement de 31 pt corrigé).
42 +- Éq. (consumer) : numéraire `x` scalaire (était en gras).
43 +- Paragraphe « Evaluation » : MdAPE indépendante de la constante de rétrotransformation ; parts ± 15 % / ± 25 % définies.
44 +- Forêt aléatoire : hyperparamètres indiqués (400 arbres, max_features 0,5, feuille ≥ 3).
45 +- Vocabulaire du programme de recherche : « median absolute percentage error (MdAPE) », « neighbourhood/municipality fixed
46 + effects », « out-of-sample », « automated valuation model (AVM) », « implicit price » ; orthographe britannique
47 + harmonisée (labelled, neighbourhood, modelled, centred).
48 +- Déclarations : disponibilité des sorties brutes IA ; mention de l'assistant IA utilisé pour la révision ; chemin de
49 + script en `\path{}` (débordement de 150 pt corrigé).
50 +- Tables `rob_ai_coefs` et `agreement` en `\footnotesize` ; ligne « Realtor.ca, dollar amount in text » (141 obs., régression
51 + saturée R² 0,998) retirée de la table et expliquée en note.
52 +- README : statut, résultats-phares, inventaire des tables, limites, notes de révision ; `docs/METHODOLOGIE.md` : §3 (19 115 vs
53 + 19 123 expliqué) et nouveau §10 ; `docs/highlights_elsevier.tex` réécrit (5 puces ≤ 85 caractères).
54 +- Scripts 11–14 et 16 : `exec(open("v5_ml.py"))` → `exec(open("scripts/09_v5_ml_text_vision.py"))` (chemin cassé par
55 + l'harmonisation du 2026-10-17 ; les scripts ne tournaient plus). Script 16 : note de table « twelve attributes » corrigée.
56 +
57 +## 3. Références
58 +
59 +Avant : 35 entrées, aucune avec DOI. Après : **53 entrées, toutes vérifiées via Crossref (`api.crossref.org/works`) ou la page
60 +arXiv, DOI inscrit dans le .bib** (sauf Kennedy 1981, sans DOI Crossref ; vérifié via RePEc/Statalist : AER 71(4), p. 801).
61 +
62 +Corrections d'entrées existantes : Lindenthal & Johnson (2021) — volume 71(3), pages 353–384 ajoutés (DOI 10.1007/s11146-021-09845-1) ;
63 +Gorishniy et al. — NeurIPS 34, arXiv:2106.11959 ; Grinsztajn et al. — NeurIPS 35, DOI 10.52202/068431-0037 ; Loshchilov & Hutter —
64 +arXiv:1711.05101 ; Guo & Berkhahn — arXiv:1604.06737 ; Malpezzi (2003) — DOI chapitre Wiley 10.1002/9780470690680.ch5.
65 +
66 +Références ajoutées (18) et usage :
67 +
68 +| Clé | Référence | DOI | Rôle |
69 +|---|---|---|---|
70 +| kennedy1981estimation | Kennedy, AER 71(4):801, 1981 | — | correction petit échantillon de Halvorsen–Palmquist |
71 +| nowak2020quality | Nowak & Smith, AER: Insights 2(3):339–356, 2020 | 10.1257/aeri.20190337 | qualité omise et indices |
72 +| nowak2021dictionaries | Nowak, Price & Smith, JREFE 62(1):139–163, 2021 | 10.1007/s11146-019-09740-w | instabilité du vocabulaire |
73 +| zhang2024describe | Zhang, Li & Branco, Natural Language Engineering 30(4):661–695, 2024 | 10.1017/S1351324923000360 | complémentarité texte / structuré |
74 +| boucher2026decoding | Boucher, UQO WP 2, 2026 | — | papier frère (langue, embeddings) |
75 +| bessinger2016quantifying | Bessinger & Jacobs, ICIP 2016, 4388–4392 | 10.1109/ICIP.2016.7533189 | curb appeal appris |
76 +| naik2017computer | Naik et al., PNAS 114(29):7571–7576, 2017 | 10.1073/pnas.1619003114 | vision urbaine |
77 +| law2019take | Law, Paige & Russell, ACM TIST 10(5):1–19, 2019 | 10.1145/3342240 | street view + satellite |
78 +| zhang2018greenery | Zhang & Dong, ISPRS IJGI 7(3):104, 2018 | 10.3390/ijgi7030104 | verdure visible |
79 +| kang2021understanding | Kang et al., Land Use Policy 111:104919, 2021 | 10.1016/j.landusepol.2020.104919 | géo-données multi-sources |
80 +| dell2025deep | Dell, JEL 63(1):5–58, 2025 | 10.1257/jel.20241733 | deep learning pour économistes |
81 +| korinek2023generative | Korinek, JEL 61(4):1281–1317, 2023 | 10.1257/jel.20231736 | IA générative en recherche |
82 +| gilardi2023chatgpt | Gilardi, Alizadeh & Kubli, PNAS 120(30), 2023 | 10.1073/pnas.2305016120 | fiabilité de l'annotation LLM |
83 +| han2016asking | Han & Strange, JUE 93:115–130, 2016 | 10.1016/j.jue.2016.03.008 | rôle du prix demandé |
84 +| pace2020examining | Pace & Hayunga, JREFE 60(1–2):170–180, 2020 | 10.1007/s11146-019-09724-w | résidus hédoniques et arbres |
85 +| bourassa2010predicting | Bourassa, Cantoni & Hoesli, JRER 32(2):139–160, 2010 | 10.1080/10835547.2010.12091276 | prédiction et dépendance spatiale |
86 +| roberts2017cross | Roberts et al., Ecography 40(8):913–929, 2017 | 10.1111/ecog.02881 | validation croisée bloquée |
87 +| abadie2023when | Abadie, Athey, Imbens & Wooldridge, QJE 138(1):1–35, 2023 | 10.1093/qje/qjac038 | quand grouper les SE |
88 +| mackinnon2017wild | MacKinnon & Webb, JAE 32(2):233–254, 2017 | 10.1002/jae.2508 | grappes de tailles inégales |
89 +
90 +Référence envisagée puis non ajoutée : Ho (U. Washington, job-market paper, texte + images Zillow) — non publiée, non vérifiable
91 +par Crossref.
92 +
93 +## 4. Réanalyses exécutées
94 +
95 +| Script | Durée | Sortie | Contenu |
96 +|---|---|---|---|
97 +| `scripts/18_revision_language_leakage_spatial.py` | ≈ 5,5 min (Mac M-series, 9 CV GBM, 299 bootstrap) | `results/revision_2026-10-17.json`, `.log` | langue, fuite, OLS A/B + langue / RTA / Realtor seul, ablation in-sample par sous-échantillon, bootstrap en grappes, accord inter-sources, CV aléatoire vs spatiale (OLS, GBM ; L2–L4, L4 + langue), grille et CV emboîtée du blend |
98 +| `scripts/19_make_revision_tables.py` | < 1 s | `paper/tables/rob_language_source.tex`, `rob_spatial.tex`, `rob_ai_coefs.tex`, `agreement.tex` | mise en forme des tables à partir du JSON |
99 +
100 +Vérifications sans script versionné (lecture seule) : égalité prix demandé dans le texte / `priceNumber` ; comptage des
101 +municipalités regroupées ; part de Montréal ; composition de Sainte-Anne-des-Plaines (`maisons_hedonic_v4.csv`) ;
102 +reproduction exacte des prédictions GBM v5 (R² 0,7695) et récupération des prédictions NN v5 (R² 0,747) à partir de
103 +`v5_blend_oos.npy`.
104 +
105 +Aucun appel aux API OpenAI/Anthropic. Figures non régénérées (chiffres inchangés) ; `fig_ablation` reste valide (légende
106 +corrigée).
107 +
108 +## 5. Chiffres du texte modifiés
109 +
110 +| Chiffre | Avant | Après | Justification |
111 +|---|---|---|---|
112 +| Gain in-sample des deux couches | « nearly four points » | 3,8 pt (0,8 texte + 3,0 vision) | `ablationA_insample` (0,7169 / 0,7252 / 0,7552) |
113 +| Gain OLS du passage OLS → RF | +4,9 | +4,9 (inchangé, précisé « Panel A ») | — |
114 +| Dimension d'embedding | ≈ 29 | ≈ 30 | 1,6 × 195^0,56 |
115 +| Aménités par mots-clés | 17 | 15 | code |
116 +| Régresseurs vision | 12 | 21 (12 champs) | code |
117 +| Part d'extérieurs détectés | 96 % | 95,8 % (18 314 / 19 123) | JSON |
118 +
119 +Tous les autres chiffres de la v2.0 (R² 0,717 / 0,725 / 0,755 ; 0,779 / 14,0 % ; coefficients de la table de régression ;
120 +tables `models`, `ablation`, `robustness`, `summary_stats`, `ai_summary`) sont inchangés et ont été re-vérifiés contre
121 +`results/revision_extras.json` et `results/ablation_extras.json`.
122 +
123 +## 6. Éléments non modifiés faute de données / de temps
124 +
125 +- Pas de ré-extraction LLM sur des descriptions purgées du prix (nécessite l'API OpenAI ; ≈ 1 $) — recommandée.
126 +- Pas de validation humaine des extractions (aucun jeu étiqueté).
127 +- Pas de mesure test–retest de la couche vision (API Anthropic).
128 +- Pas de prix de transaction (registre foncier) ; pas de dimension temporelle.
129 +- Figures `fig_ablation`, `fig_importance`, `fig_calibration` non régénérées (script 14 ≈ 5 min, chiffres inchangés).
130 +- Les tables « auto-generated » `models.tex` et `ablation.tex` ont été modifiées à la main **dans leurs notes seulement**
131 + (et le générateur `16_make_tables_v5.py` mis en cohérence) ; les chiffres n'ont pas été touchés.
modified docs/METHODOLOGIE.md +37 −1
@@ -47,7 +47,7 @@ Les prix scrappés contiennent des erreurs de parsing (min 899 $, max 368 G$). R
47 47 | Chambres / sdb | 0–15 / 0–12 (sinon → manquant) | plafonds réalistes |
48 48 | **Prix/pied²** | garder **P0,5 – P99,5** (≈ 73–1 276 $/ft²) sur les biens à surface réelle | retire les erreurs résiduelles de parsing prix ou surface |
49 49
50 −→ **19 115 maisons** conservées (158 retirées).
50 +→ **19 115 maisons** conservées (158 retirées) dans la première version du pipeline (`04_hedonic_ols_base.py`). Dans la version finale (scripts 05–18), la reconstruction de la surface par les pièces précède le filtre prix/pi², ce qui donne **19 123 maisons** (150 retirées) ; c'est ce chiffre qui figure dans le papier.
51 51
52 52 **Principe** : on borne plutôt que de supprimer quand une seule variable est aberrante (on met la variable à *manquant* et on l'impute), pour ne pas perdre l'observation entière.
53 53
@@ -170,3 +170,39 @@ Progression observée : v2 (structurel) R²≈0,70 → v3 (+ pièces/photos) R²
170 170 | `specs.jsonl`, `facade_specs.jsonl` | sorties LLM brutes (clé uid) |
171 171 | `maisons_hedonic*.csv` | dataset final + prix prédit + `resid_pct` |
172 172 | `hedonic_*_summary.txt` | sorties statistiques complètes |
173 +
174 +
175 +---
176 +
177 +## 10. Révision scientifique du 2026-10-17 (version 2.1 du papier)
178 +
179 +Réanalyses locales (`scripts/18_revision_language_leakage_spatial.py`, ~5 min, aucun appel API ; résultats dans
180 +`results/revision_2026-10-17.json`, tables `paper/tables/rob_*.tex` et `agreement.tex`) :
181 +
182 +- **Fuite d'information par le texte.** Les 3 767 descriptions DuProprio de l'échantillon nettoyé contiennent toutes la
183 + fiche structurée de la plateforme rendue en texte, dont le prix demandé (« Asking Price $499,999 », égal au prix
184 + enregistré dans 3 761 cas) et, pour 46,6 % d'entre elles, l'évaluation municipale ; 2,5 % des descriptions Realtor.ca
185 + contiennent un montant en dollars, aucune le prix demandé. Le LLM a donc lu la variable dépendante pour 19,7 % de
186 + l'échantillon. Gain in-sample de la couche texte (modèle A) : +0,6 pt sur Realtor.ca contre +3,3 pt sur DuProprio ;
187 + couche vision (jamais exposée au texte) : +2,4 contre +4,1 pt. Les gains Realtor.ca sont retenus comme estimation
188 + crédible ; une ré-extraction sur des descriptions purgées du prix est recommandée (≈ 1 $).
189 +- **Langue.** Règle de stop-words (ratio 1,5, comme WP 2) : 44,6 % FR, 43,1 % EN, 12,3 % mixte (DuProprio = traduction
190 + anglaise + original français). Sur Realtor.ca, 41,9 % EN ; prix médian EN 749 000 $ vs FR 524 900 $. Indicatrice EN :
191 + +7,2 % conditionnel (modèle A), +11,8 % (modèle B). Les prix implicites IA sont inchangés à la 3e décimale ; les gains
192 + des deux couches existent dans chaque langue.
193 +- **Effets fixes RTA** (3 premiers caractères du code postal ; 419 RTA, 250 avec ≥ 20 annonces) : R² A 0,755 → 0,764 ;
194 + coefficients IA stables.
195 +- **Validation spatiale** (GroupKFold par RTA vs 5-fold aléatoire) : tous les niveaux baissent de 3 à 5 pt de R² OOS
196 + (GBM texte+vision 0,770 → 0,734), les incréments texte/vision sont préservés.
197 +- **Bootstrap en grappes** (299 réplications, 195 municipalités) : IC 95 % excluant zéro pour curb appeal, rénovation,
198 + luxe, garage, brique, pierre, vinyle, cadre rural, condition LLM, cuisine rénovée ; incluant zéro de peu pour
199 + aménagement paysager et bois franc.
200 +- **Accord inter-sources** (LLM vs mots-clés sur le même texte ; vision vs texte/champs structurés) : κ = 0,96 garage,
201 + 0,91 foyer, 0,81 piscine, 0,65 climatisation, 0,56 sous-sol fini, 0,50 bord de l'eau (le LLM sur-déclare : 22,5 % vs
202 + 9,2 %) ; garage visible vs garage texte κ = 0,35 ; étages visuels exacts 60 %, à ± ½ étage 89 % ; type « maison en
203 + rangée » visuel : précision 18 % (champ non fiable, utilisé seulement comme contrôle).
204 +- **Poids du blend** : w = 0,5 fixé a priori ; grille 0,3–0,7 → R² 0,771–0,780 ; poids choisi en CV emboîtée → 0,775.
205 +- **Corrections de forme** : la température n'était pas fixée pour la vision (défaut API), seuls les alias de modèles
206 + sont connus ; 15 (et non 17) aménités par mots-clés ; 12 champs vision → 21 régresseurs ; catégorie de référence des
207 + EF ville = municipalités regroupées (< 20 annonces) ; la couche vision est le plus grand des deux incréments IA mais
208 + le bloc pièces/listing apporte davantage (+9,8 pt OLS).
added docs/REVIEW-REPORT.md +165 −0
@@ -0,0 +1,165 @@
1 +<!-- Author: Simon-Pierre Boucher — simon-pierre.boucher@uqo.ca -->
2 +# REVIEW REPORT — editorial board, révision scientifique complète (2026-10-17)
3 +
4 +**Papier** : UQO Working Paper No. 14, *Hedonic House Pricing with Language-Model and Vision Features: Evidence from
5 +Quebec Real-Estate Listings* (S.-P. Boucher). Version examinée : 2.0 (juillet 2026, 33 p., 35 réf., pas sur SSRN).
6 +Version produite : 2.1 (octobre 2026, 46 p., 53 réf.). Journal d'audit : `docs/CHANGELOG-REVISION.md`.
7 +
8 +**Verdict global.** La question est bien posée et actuelle (des modèles d'IA généralistes, contraints par un schéma de
9 +sortie, peuvent-ils transformer texte et photos d'annonces en caractéristiques hédoniques interprétables, et combien
10 +cela ajoute-t-il ?), le pipeline est entièrement reproductible et les résultats-phares de la v2.0 se reproduisent à
11 +l'identique. La v2.0 présentait toutefois trois faiblesses de fond que la révision corrige à partir de réanalyses
12 +locales : (i) une **fuite d'information** non détectée — toutes les descriptions DuProprio contiennent le prix demandé
13 +que le LLM a lu ; (ii) une **affirmation centrale fausse** — la couche vision n'est pas « le plus grand gain de tout bloc
14 +de variables », le bloc pièces/listing fait mieux ; (iii) l'**absence de tout contrôle de langue, d'effets fixes fins et
15 +de validation spatiale**, alors que 43 % des annonces sont en anglais, que les codes postaux sont disponibles et que la
16 +validation croisée aléatoire flatte des modèles qui exploitent la proximité spatiale. S'y ajoutent des inexactitudes
17 +de description du protocole (température, nombre d'aménités, nombre de régresseurs vision, catégorie de référence des
18 +effets fixes). Après correction, les conclusions sur la **couche vision** sortent renforcées (robustes à la langue, aux
19 +EF RTA, au bootstrap en grappes, à la validation spatiale, et jamais exposée au prix) ; celles sur la **couche texte**
20 +sont ramenées à ce que la plateforme sans fuite (Realtor.ca) permet d'établir : un gain modeste (+0,6 pt in-sample).
21 +
22 +---
23 +
24 +## 1. Diagnostic par section
25 +
26 +### Résumé / Introduction
27 +- *Forces* : positionnement clair (mesure vs estimateur), contribution en trois points, prudence sur la causalité.
28 +- *Faiblesses* : « blind to the listing price » (faux pour DuProprio) ; « deterministic » (faux pour la vision) ; « largest
29 + out-of-sample gain of any feature block » (contredit par la table d'ablation) ; aucune mention de la langue ni de la
30 + validation spatiale ; « nearly four points » imprécis.
31 +- *Corrigé* : résumé et introduction réécrits ; paragraphe explicite sur les trois menaces (fuite, langue, dépendance
32 + spatiale) et sur ce que la révision établit ; chiffres ventilés texte / vision.
33 +
34 +### Revue de littérature
35 +- *Forces* : quatre volets pertinents (hédonique, spatial/prédiction, contenu non structuré, microstructure), 35 références
36 + réelles.
37 +- *Faiblesses* : aucune DOI ; Lindenthal & Johnson incomplète ; rien sur la validation croisée spatiale, les grappes
38 + inégales, la stabilité du vocabulaire (Nowak, Price & Smith), la qualité omise dans les indices (Nowak & Smith 2020),
39 + la fiabilité de l'annotation par LLM, le rôle du prix demandé (Han & Strange 2016) ; street-view (Naik 2017, Law 2019,
40 + Zhang & Dong 2018, Kang 2021) absent ; pas de lien avec le papier frère WP 2.
41 +- *Corrigé* : 18 références vérifiées ajoutées et intégrées à l'argument ; DOI pour toutes les entrées ; renvoi à WP 2 sur
42 + la langue sans dépendance.
43 +
44 +### Données
45 +- *Faiblesses* : le contenu réel des « descriptions » DuProprio (fiche structurée + prix demandé + évaluation municipale)
46 + n'était pas décrit ; langue absente ; codes postaux non exploités ; couverture des couches IA non chiffrée ; « Rooms
47 + (Realtor) » agrégé sur 19 123 obs. sans dire que DuProprio vaut 0.
48 +- *Corrigé* : nouvelle §3.4 « Content and Language of the Descriptions » (fuite quantifiée, langue par plateforme, écart de
49 + prix EN/FR brut 0,37 et intra-municipal 0,21 log-point) ; couverture (100 % texte, 100 % vision, 95,8 % extérieurs) ;
50 + annexe C (règle de langue).
51 +
52 +### Méthodologie
53 +- *Forces* : cadre de Rosen, forme semi-log justifiée, Halvorsen–Palmquist, design en deux parties, SE groupées, rétro-
54 + transformation de Duan, définitions formelles des estimateurs ML.
55 +- *Faiblesses* : température zéro revendiquée pour les deux couches (vision : défaut API) ; alias de modèles non épinglés ;
56 + « Seventeen amenity indicators » (15) ; « twelve attributes » confondant champs et régresseurs (21) ; codage des
57 + « inconnu » (condition texte → « bon », scores vision → 5) non documenté ; catégorie de référence des EF ville non
58 + précisée ; éq. (hedonic) dupliquant ln Area / ln Lot dans X ; absence d'équation / de description de la validation OOS
59 + spatiale ; grappes très inégales non discutées ; « deterministic ».
60 +- *Corrigé* : tous ces points ; paragraphes « From schema fields to regressors », « Out-of-sample validation » ; bootstrap
61 + en grappes ; EF RTA ; indicatrices de langue ; équation scindée et blocs explicités.
62 +
63 +### Résultats
64 +- *Forces* : coefficients correctement convertis en % (vérifiés un à un : e^b − 1) ; discussion sensée ; validation
65 + monotone des scores visuels.
66 +- *Faiblesses* : « nearly four points » ; âge non chiffré ; score de condition visuelle (non significatif en A) passé sous
67 + silence ; validation « indépendante » surinterprétée (monotonie ≠ qualité) ; Sainte-Anne-des-Plaines +143 % non
68 + expliqué (2 observations) ; aucune mesure d'erreur d'extraction.
69 +- *Corrigé* : chiffres précisés ; colinéarité des trois scores visuels discutée ; nouvelle table `agreement` (κ par champ,
70 + précision/rappel, étages, type de bâtiment) ; champs non fiables (bord de l'eau LLM, cadre et type visuels) relégués aux
71 + contrôles ; référence des EF explicitée.
72 +
73 +### Benchmark prédictif
74 +- *Forces* : folds identiques, ladder complet, définitions formelles, blend.
75 +- *Faiblesses* : « single largest increment from any one feature layer » faux ; poids du blend présenté comme « simple »
76 + alors que le script 10 cherchait le meilleur poids sur les prédictions test ; ordre des couches du bloc résiduel
77 + inversé par rapport au code ; gain NN de la vision non chiffré.
78 +- *Corrigé* : affirmation retirée ; grille de poids (0,771–0,780) et poids emboîté (0,775) rapportés ; w = 0,5 conservé ;
79 + architecture décrite conformément au code ; renvoi à la validation spatiale.
80 +
81 +### Robustesse et limites
82 +- *Forces* : SE groupées vs HC3 bien lues ; table par sous-échantillon ; limites honnêtes sur les prix demandés.
83 +- *Faiblesses* : rien sur la langue, la fuite, les EF fins, la validation spatiale, l'erreur d'extraction, la version des
84 + modèles ; « Five limitations ».
85 +- *Corrigé* : quatre nouvelles sous-sections (inférence + bootstrap ; langue ; fuite ; EF RTA et validation spatiale),
86 + quatre nouvelles tables ; sept limites.
87 +
88 +### Conclusion
89 +- Réécrite : gains inégaux entre couches, fuite, validation spatiale, extensions (ré-extraction purgée, jeu étiqueté,
90 + coordonnées, prix de transaction).
91 +
92 +---
93 +
94 +## 2. Méthodologie — verdict motivé (§3 du master prompt)
95 +
96 +| Point | Verdict |
97 +|---|---|
98 +| Spécification justifiée | Oui (semi-log, Rosen, Malpezzi) ; ordre d'entrée des blocs explicité pour l'attribution des gains. |
99 +| Hypothèses d'estimation explicites | Oui après révision (MCO, EF, imputation + indicateurs, codage des « inconnu », troncature à 4 000 caractères). |
100 +| Variables correctement définies | Oui après correction (15 aménités, 18 + 21 régresseurs IA, référence des EF, échelles ordinales). |
101 +| Effets fixes appropriés | Municipalité (195) adéquat au premier ordre ; EF RTA (250) ajoutés en robustesse : coefficients IA stables, R² A 0,755 → 0,764. |
102 +| Dépendance spatiale / temporelle | Spatiale : EF + SE groupées + validation par RTA ; temporelle : impossible (coupe instantanée), dit explicitement. |
103 +| Erreurs-types | Groupées municipalité (préférées), HC3, bootstrap en grappes (299) ; grappes inégales discutées (MacKinnon & Webb). |
104 +| Construction de l'échantillon | Transparente (annexe B) ; divergence 19 115 / 19 123 entre versions du pipeline expliquée dans METHODOLOGIE.md. |
105 +| Robustesse face aux vraies menaces | Désormais : fuite (par plateforme), langue (par groupe et en contrôle), localisation fine, validation spatiale, bootstrap. |
106 +| Interprétation des coefficients | Correcte (e^b − 1 vérifié) ; prix implicites d'attributs IA lus comme associations prédictives de « ce que le modèle voit ». |
107 +| Significativité statistique vs économique | Distinguées (magnitudes en %, IC bootstrap). |
108 +| Affirmations causales | Aucune ; limites explicites. |
109 +
110 +---
111 +
112 +## 3. Littérature
113 +
114 +- 35 références existantes **toutes vérifiées** (Crossref / arXiv) ; corrections : Lindenthal & Johnson 2021 (vol. 71(3), 353–384),
115 + entrées NeurIPS/ICLR/arXiv complétées ; DOI ajoutés partout (Kennedy 1981 sans DOI, vérifié via RePEc).
116 +- 18 références ajoutées, toutes vérifiées (liste et DOI dans le CHANGELOG §3), intégrées dans l'argument (qualité omise,
117 + vocabulaire instable, annotation LLM, prix demandé, validation bloquée, grappes inégales, street-view).
118 +- Aucune référence retirée. Total 53.
119 +
120 +## 4. Résultats — incohérences trouvées
121 +
122 +1. Vision = « plus grand gain de tout bloc » (résumé, intro, benchmark, ablation, conclusion, highlights, légende) — faux ;
123 + corrigé partout.
124 +2. 17 vs 15 aménités ; 12 attributs vs 21 régresseurs ; curb appeal 1–5 (README) vs 1–10 (papier) — corrigés.
125 +3. Température zéro « pour les deux couches » — faux pour la vision ; corrigé.
126 +4. « Reference city » — c'est la catégorie regroupée ; corrigé.
127 +5. Petites différences (± 0,001) entre tables issues de runs différents (ordre des colonnes dans HistGBM) — notées.
128 +6. Script 13 imputait les variables avant la CV GBM (0,7689) alors que le script 12 laissait les NaN (0,7695) ; la table
129 + `models` (0,770) suit le script 09/12 ; documenté, le script 18 suit la convention NaN.
130 +7. Les scripts 11–14 et 16 ne pouvaient plus s'exécuter (`v5_ml.py` introuvable après l'harmonisation) — réparé.
131 +
132 +## 5. Limites restantes
133 +
134 +- Prix demandés ; coupe instantanée ; pas de prix de transaction.
135 +- Fuite du prix dans le texte DuProprio : seule une ré-extraction purgée (API) tranchera entre « information structurée
136 + légitime » et « biais d'ancrage » ; en attendant, gains texte crédibles = Realtor.ca.
137 +- Pas de jeu étiqueté humain ; erreur d'extraction mesurée indirectement seulement.
138 +- Vision à température par défaut, alias de modèles ; variabilité test–retest non mesurée.
139 +- Prime conditionnelle de 7 % des annonces anglaises : proxy de localisation/clientèle, non interprétée causalement.
140 +- Modèle commun moins adéquat pour DuProprio (R² OOS 0,58 ; R² OLS intra-groupe négatif).
141 +- Effets fixes de municipalités à cellules rares (ex. Sainte-Anne-des-Plaines).
142 +
143 +## 6. Analyses recommandées non exécutées
144 +
145 +1. Ré-extraction LLM sur les descriptions DuProprio purgées du bloc structuré et du prix (≈ 3 767 appels, ≈ 1 $) et
146 + comparaison des coefficients / gains.
147 +2. Validation humaine : 300 annonces codées à la main (texte) et 300 façades (vision) → précision/rappel par champ.
148 +3. Test–retest de la vision : ré-extraction de 500 façades à température 0 et à température par défaut.
149 +4. Panel de photos intérieures (cuisine, salle de bain) sous le même schéma.
150 +5. Appariement au registre foncier (prix de transaction) ; ratio liste/vente par attribut IA.
151 +6. Variables spatiales continues (coordonnées DuProprio, centroïdes de RTA) et modèle spatial (SAR/SEM ou GWR).
152 +7. Wild cluster bootstrap (Webb weights) en complément du bootstrap par paires.
153 +8. Régénération des figures via `14_figures_v5.py` (inchangées en substance).
154 +
155 +## 7. Maturité pour publication
156 +
157 +- **Revue cible** : *Journal of Housing Economics* (déjà envisagée ; `13_revision_extras.py`) ou *Journal of Real Estate
158 + Finance and Economics* ; *Real Estate Economics* exigerait des prix de transaction. Alternative orientée méthodes :
159 + *Journal of Property Research* ou *Journal of Real Estate Research*.
160 +- **Risques de rejet** : (1) prix demandés plutôt que transactions (le plus probable) ; (2) fuite DuProprio — désormais
161 + documentée, mais un arbitre demandera la ré-extraction purgée ; (3) absence de validation humaine des extractions ;
162 + (4) dépendance aux versions de modèles propriétaires ; (5) longueur (46 p.) — une version revue devrait déplacer
163 + `agreement`, `rob_spatial` et les définitions formelles ML en annexe.
164 +- **État** : manuscrit interne cohérent, chiffres vérifiés, bibliographie vérifiée ; prêt pour SSRN après lecture de
165 + l'auteur ; pour soumission en revue, exécuter d'abord les recommandations 1 et 2.
modified docs/highlights_elsevier.tex +6 −5
@@ -1,6 +1,7 @@
1 1 % ============================================================================
2 2 % Highlights — separate file required at Elsevier submission
3 3 % (3-5 bullets, max 85 characters each, results-oriented)
4 +% Revised 2026-10-17 (version 2.1 of the working paper)
4 5 % ============================================================================
5 6 \documentclass[12pt]{article}
6 7 \usepackage[utf8]{inputenc}
@@ -8,10 +9,10 @@
8 9 \begin{document}
9 10 \section*{Highlights}
10 11 \begin{itemize}
11 − \item LLM and vision models turn listing text and photos into hedonic regressors
12 − \item Schema-constrained decoding makes AI outputs reproducible econometric variables
13 − \item Two AI feature layers add four points of $R^2$ to a rich hedonic model
14 − \item Façade-vision features give the largest out-of-sample gain of any block
15 − \item Curb appeal, materials, and luxury cues carry sensible implicit prices
12 + \item LLM and vision models turn listing text and photos into auditable hedonic regressors
13 + \item Two AI layers add 3.8 points of $R^2$ to a rich hedonic model; 3.0 come from the photo
14 + \item Façade-vision features lift every estimator, more than switching OLS to a forest
15 + \item Curb appeal, materials and luxury cues price sensibly under clustered and bootstrap SEs
16 + \item FSBO descriptions embed the asking price: text gains are credible only on the MLS
16 17 \end{itemize}
17 18 \end{document}
modified paper/appendix/appendix.tex +58 −30
@@ -12,52 +12,62 @@
12 12 \section{Machine-Generated Feature Definitions}
13 13 \label{app:features}
14 14
15 −Table~\ref{tab:app_features} lists the attributes extracted by the language and
16 −vision models under their respective strict JSON schemas. All fields use closed
17 −enumerations or booleans; absent information is coded as \texttt{false},
18 −\texttt{unknown}, or an empty list.
15 +Table~\ref{tab:app_features} lists the fields extracted by the language and
16 +vision models under their respective strict JSON schemas, with the value domains as
17 +specified in the extraction scripts (the enumerations are in French in the schema;
18 +they are translated here). All fields use closed enumerations, integers, or
19 +booleans; absent information is coded as \texttt{false}, \texttt{unknown}, or an
20 +empty list. The mapping from fields to regressors is described in
21 +Section~\ref{subsec:vision}.
19 22
20 23 \begin{table}[htbp]
21 24 \centering
22 25 \begin{threeparttable}
23 − \caption{Attributes Extracted by the AI Models}
26 + \caption{Fields Extracted by the AI Models}
24 27 \label{tab:app_features}
25 28 \small
26 − \begin{tabular}{@{}p{2.9cm}p{4.4cm}p{6.3cm}@{}}
29 + \begin{tabular}{@{}p{3.1cm}p{4.3cm}p{6.2cm}@{}}
27 30 \toprule
28 31 Layer & Field & Domain \\
29 32 \midrule
30 − \multicolumn{3}{@{}l}{\textit{Panel A: Text (LLM, \texttt{gpt-4o-mini})}} \\[3pt]
31 − Condition & \texttt{condition} & to renovate / original / good / renovated / like-new / new \\
32 − Turnkey & \texttt{turnkey} & boolean \\
33 − Renovated kitchen & \texttt{renovated\_kitchen} & boolean \\
34 − Renovated bathroom & \texttt{renovated\_bathroom} & boolean \\
35 − Flooring & \texttt{flooring} & hardwood / ceramic / laminate / carpet / vinyl / concrete \\
36 − Countertop & \texttt{countertop} & granite / quartz / marble / laminate / wood \\
37 − Heating & \texttt{heating} & electric / heat pump / wood / gas / oil / radiant / geothermal \\
38 − Water view & \texttt{view} & water / mountain / city / none \\
39 − Income property & \texttt{income\_property} & boolean \\
40 − Large lot; quiet area & \texttt{large\_lot}, \texttt{quiet\_area} & boolean \\[6pt]
41 − \multicolumn{3}{@{}l}{\textit{Panel B: Vision (\texttt{claude-haiku-4-5}, façade)}} \\[3pt]
42 − Building type (visual) & \texttt{building\_type\_visual} & detached / semi / row / condo / cottage / land \\
33 + \multicolumn{3}{@{}l}{\textit{Panel A: Text (LLM, \texttt{gpt-4o-mini}; 20 fields, 18 regressors)}} \\[3pt]
34 + Condition & \texttt{condition} & to renovate / original / good / renovated / like-new / new / unknown \\
35 + Turnkey; recently renovated & \texttt{turnkey}, \texttt{recently\_renovated} & boolean \\
36 + Renovated kitchen / bathroom & \texttt{renovated\_kitchen}, \texttt{renovated\_bathroom} & boolean \\
37 + Flooring (multi-valued) & \texttt{flooring} & hardwood / ceramic / floating / carpet / vinyl / concrete / other \\
38 + Countertop & \texttt{countertop} & granite / quartz / marble / laminate / wood / other / unknown \\
39 + Heating (multi-valued) & \texttt{heating} & electric / baseboards / heat pump / wood / gas / oil / radiant / geothermal / other \\
40 + View & \texttt{view} & water / mountain / city / none / unknown \\
41 + Amenities & \texttt{garage}, \texttt{pool}, \texttt{waterfront}, \texttt{finished\_basement}, \texttt{fireplace}, \texttt{central\_ac} & boolean \\
42 + New construction; income property & \texttt{new\_construction}, \texttt{income\_property} & boolean \\
43 + Large lot; quiet area & \texttt{large\_lot}, \texttt{quiet\_area} & boolean \\
44 + Highlights & \texttt{highlights} & 3--6 free-text keywords (not used in the regressions) \\[6pt]
45 + \multicolumn{3}{@{}l}{\textit{Panel B: Vision (\texttt{claude-haiku-4-5}, façade; 12 fields, 21 regressors)}} \\[3pt]
46 + Exterior shot & \texttt{is\_exterior} & boolean \\
47 + Building type (visual) & \texttt{building\_type\_visual} & detached / semi-detached / row / condo building / cottage / land / other \\
43 48 Condition score & \texttt{condition\_score} & integer 1--10 \\
44 49 Curb appeal & \texttt{curb\_appeal} & integer 1--10 \\
45 − Renovation level & \texttt{renovation\_level} & to renovate / dated / standard / renovated / high-end \\
46 − Exterior materials & \texttt{exterior\_materials} & brick / stone / vinyl / wood / aluminum / stucco / concrete \\
50 + Renovation level & \texttt{renovation\_level} & to renovate / dated / standard / renovated / high-end / unknown \\
51 + Exterior materials (multi-valued) & \texttt{exterior\_materials} & brick / stone / vinyl / wood / aluminum / stucco / roughcast / concrete / other \\
47 52 Landscaping & \texttt{landscaping} & none / minimal / maintained / manicured \\
48 − Setting & \texttt{setting} & dense urban / suburban / rural / waterfront / wooded \\
49 − Luxury indicator & \texttt{curb\_luxury} & boolean \\
53 + Garage visible & \texttt{has\_garage\_visible} & boolean \\
54 + Storeys (visual) & \texttt{storeys\_visual} & 1 / 1.5 / 2 / 3+ / unknown \\
55 + Setting & \texttt{setting} & dense urban / suburban / rural / waterfront / wooded / unknown \\
50 56 Photo quality & \texttt{photo\_quality} & integer 1--10 \\
57 + Luxury indicator & \texttt{curb\_luxury} & boolean \\
51 58 \bottomrule
52 59 \end{tabular}
53 60 \begin{tablenotes}[flushleft]
54 61 \footnotesize
55 62 \item \textit{Notes:} Both extractions use schema-constrained decoding with
56 − closed enumerations, required fields, and \texttt{additionalProperties:false};
57 − decoding temperature is zero. The text layer reads the free-text description;
58 − the vision layer analyzes one façade photograph per listing. Fields are mapped
59 − to numeric regressors (booleans to $0/1$, ordinal scales to integers, categories
60 − to indicators), each with an availability flag.
63 + closed enumerations, required fields, and \texttt{additionalProperties:false}.
64 + The text layer reads the free-text description (truncated at 4{,}000 characters)
65 + at temperature zero; the vision layer analyzes one façade photograph per listing
66 + at the API's default temperature. Model identifiers are the API aliases current in
67 + July 2026. Fields are mapped to numeric regressors (booleans to $0/1$, ordinal
68 + scales to integers, multi-valued fields and categories to indicators); the text
69 + condition scale is coded $0$--$5$ with ``unknown'' at~2, the visual renovation
70 + level $0$--$4$ with ``unknown'' at~2, and landscaping $0$--$3$.
61 71 \end{tablenotes}
62 72 \end{threeparttable}
63 73 \end{table}
@@ -77,16 +87,34 @@ enumerations or booleans; absent information is coded as \texttt{false},
77 87 Realtor.ca raw & 27{,}302 \\
78 88 DuProprio raw & 7{,}693 \\
79 89 Houses (single-family + townhouse) with price & 19{,}273 \\
80 − After outlier cleaning & 19{,}123 \\
90 + After outlier cleaning (price bounds, price-per-ft$^2$ trim) & 19{,}123 \\
91 + \quad of which Realtor.ca / DuProprio & 15{,}356 / 3{,}767 \\
81 92 \quad of which with measured living area (Model~A) & 9{,}446 \\
93 + \quad of which with a detected exterior photograph & 18{,}314 \\
82 94 \bottomrule
83 95 \end{tabular}
84 96 \begin{tablenotes}[flushleft]
85 97 \footnotesize
86 98 \item \textit{Notes:} See Section~\ref{sec:data}. ``Measured living area'' means
87 − reported area or area reconstructed from room-level dimensions.
99 + reported area or area reconstructed from room-level dimensions. Both AI layers
100 + cover all 19{,}123 cleaned listings.
88 101 \end{tablenotes}
89 102 \end{threeparttable}
90 103 \end{table}
91 104
105 +\section{Language Classification}
106 +\label{app:language}
107 +
108 +Each description is classified from the counts $e$ and $f$ of English and French
109 +stop words it contains (articles, prepositions, pronouns, and auxiliaries such as
110 +\textit{the, and, with, is} and \textit{le, la, les, et, avec, est}). A description is
111 +``English'' if $e\ge1.5f$, ``French'' if $f\ge1.5e$, and ``mixed'' otherwise or when
112 +neither count is positive. On Realtor.ca the mixed class is small (2.6\%) and
113 +consists mostly of very short descriptions; on DuProprio it is large (51.7\%)
114 +because the crawler returns the automated English translation followed by the
115 +original French comments. Table~\ref{tab:rob_language_source} reports the gains from
116 +the AI layers within each class, and Table~\ref{tab:rob_ai_coefs} the effect of
117 +adding the language indicators to the hedonic regression. The language variable is
118 +produced by \texttt{scripts/18\_revision\_language\_leakage\_spatial.py}.
119 +
92 120 \end{appendices}
modified paper/figures/nn_diagram.tex +1 −1
@@ -63,7 +63,7 @@
63 63 % ---- residual detail callout ----
64 64 \node[detail, right=10mm of resb] (dtl)
65 65 {\textbf{Res block:} $x \mapsto x + f(x)$\\
66 − $f=$ Lin$_{256}\!\circ$Drop$\circ$Lin$_{512}\!\circ$GELU$\circ$BN};
66 + $f=$ Drop$\circ$Lin$_{256}\!\circ$Drop$\circ$GELU\\ $\circ$Lin$_{512}\!\circ$BN};
67 67 \draw[->, dashed, black!45] (resb.east)--(dtl.west);
68 68
69 69 % ---- training / ensemble annotation ----
modified paper/main.pdf +0 −0

Binary file not shown.

modified paper/main.tex +32 −19
@@ -71,26 +71,33 @@
71 71 \newcommand{\WPnumber}{14}
72 72 \newcommand{\WPtitle}{Hedonic House Pricing with Language-Model and Vision Features}
73 73 \newcommand{\WPsubtitle}{Evidence from Quebec Real-Estate Listings}
74 −\newcommand{\WPdate}{July 2026}
75 −\newcommand{\WPversion}{2.0}
74 +\newcommand{\WPdate}{October 2026}
75 +\newcommand{\WPversion}{2.1}
76 76 \newcommand{\WPabstract}{%
77 77 Hedonic house-price models are bounded by the characteristics an analyst can
78 78 measure, yet much of a home's value is recorded only in the free-text description
79 79 and photographs of its listing. We treat general-purpose AI models as a
80 80 feature-engineering technology for hedonic analysis: a large language model
81 81 converts each description into structured property specifications, and a
82 − vision--language model converts the façade photograph into objective visual
83 − attributes, both under strict JSON schemas that render the outputs reproducible
84 − econometric variables. Using 19{,}123 Quebec house listings, the two AI layers
85 − raise the $R^2$ of an already rich log--linear hedonic specification from 0.717 to
86 − 0.755, and the vision layer delivers the largest out-of-sample gain of any feature
87 − block for every estimator, from OLS to gradient boosting and neural networks (best
88 − ensemble: out-of-sample $R^2$ of 0.779, median absolute error 14.0\%). The
89 − extracted attributes carry economically sensible implicit prices---2.3\% per point
90 − of curb appeal, 3--4\% for brick or stone façades---that survive
91 − municipality-clustered inference.
82 + vision--language model converts the façade photograph into visual attributes, both
83 + under strict JSON schemas that make the outputs auditable econometric variables.
84 + Using 19{,}123 Quebec house listings, the two AI layers raise the $R^2$ of an
85 + already rich log-linear hedonic specification with municipality fixed effects from
86 + 0.717 to 0.755, 3.0 of the 3.8 points coming from the façade photograph, and they
87 + raise the out-of-sample $R^2$ of every estimator from OLS to gradient boosting and
88 + neural networks (best ensemble: $R^2$ of 0.779, median absolute error 14.0\%). The
89 + vision layer adds more to the linear model than switching from OLS to a random
90 + forest does. The extracted attributes carry economically sensible implicit
91 + prices---2.3\% per point of curb appeal, 3--4\% for brick or stone façades---that
92 + survive municipality-clustered inference, a cluster bootstrap, postal-code fixed
93 + effects, and a control for the language of the description, which itself carries a
94 + 7\% conditional premium for English listings. We also document that
95 + for-sale-by-owner descriptions embed the asking price, so that the text layer's
96 + gain is credible only on the brokered platform, and that spatially grouped
97 + cross-validation lowers all accuracy levels by three to five points while
98 + preserving the AI increments.
92 99 }
93 −\newcommand{\WPkeywords}{hedonic pricing, house prices, large language models, computer vision, feature engineering, Quebec}
100 +\newcommand{\WPkeywords}{hedonic pricing, house prices, large language models, computer vision, text as data, automated valuation, Quebec}
94 101 \newcommand{\WPjel}{R31, C21, C55, R21}
95 102
96 103 % --- Author ---
@@ -133,14 +140,20 @@ appeared to influence the work reported in this paper.
133 140 \paragraph{Data availability.} The listing data were collected from publicly
134 141 accessible web platforms (Realtor.ca and DuProprio) on 3~July~2026. The full
135 142 data-construction code, the extraction schemas, and the derived analysis dataset
136 −(excluding copyrighted listing text and images) are available from the author upon
137 −reasonable request.
138 −
139 −\paragraph{Use of AI models.} Large language and vision--language models
140 −(\texttt{gpt-4o-mini} and \texttt{claude-haiku-4-5}) are the object of study of this
143 +(excluding copyrighted listing text and images), together with the raw JSON outputs
144 +of the two AI models, are available from the author upon reasonable request. The
145 +robustness analyses added in version 2.1 are produced by a single script
146 +(\path{scripts/18_revision_language_leakage_spatial.py}) from the archived
147 +extractions, without new API calls.
148 +
149 +\paragraph{Use of AI models.} The large language model \texttt{gpt-4o-mini} and the
150 +vision--language model \texttt{claude-haiku-4-5} are the object of study of this
141 151 paper and were used exclusively as documented in Section~\ref{sec:methodology} to
142 152 extract structured features from listing text and photographs. They were not used to
143 −generate the analysis or the conclusions of the paper.
153 +generate the analysis or the conclusions of the paper. An AI coding assistant was
154 +used in the October 2026 revision, under the author's direction, to check the
155 +manuscript for internal consistency and to run the additional robustness analyses;
156 +all results were verified by the author.
144 157
145 158 \newpage
146 159 \bibliography{references}
modified paper/references.bib +282 −36
@@ -5,7 +5,8 @@
5 5 volume={82},
6 6 number={1},
7 7 pages={34--55},
8 − year={1974}
8 + year={1974},
9 + doi={10.1086/260169}
9 10 }
10 11
11 12 @article{lancaster1966new,
@@ -15,7 +16,8 @@
15 16 volume={74},
16 17 number={2},
17 18 pages={132--157},
18 − year={1966}
19 + year={1966},
20 + doi={10.1086/259131}
19 21 }
20 22
21 23 @article{sirmans2005composition,
@@ -25,7 +27,8 @@
25 27 volume={13},
26 28 number={1},
27 29 pages={1--44},
28 − year={2005}
30 + year={2005},
31 + doi={10.1080/10835547.2005.12090154}
29 32 }
30 33
31 34 @incollection{malpezzi2003hedonic,
@@ -35,7 +38,8 @@
35 38 editor={O'Sullivan, Tony and Gibb, Kenneth},
36 39 pages={67--89},
37 40 publisher={Blackwell Science},
38 − year={2003}
41 + year={2003},
42 + doi={10.1002/9780470690680.ch5}
39 43 }
40 44
41 45 @article{duan1983smearing,
@@ -45,7 +49,8 @@
45 49 volume={78},
46 50 number={383},
47 51 pages={605--610},
48 − year={1983}
52 + year={1983},
53 + doi={10.1080/01621459.1983.10478017}
49 54 }
50 55
51 56 @article{white1980heteroskedasticity,
@@ -55,7 +60,8 @@
55 60 volume={48},
56 61 number={4},
57 62 pages={817--838},
58 − year={1980}
63 + year={1980},
64 + doi={10.2307/1912934}
59 65 }
60 66
61 67 @article{mackinnon1985some,
@@ -65,7 +71,8 @@
65 71 volume={29},
66 72 number={3},
67 73 pages={305--325},
68 − year={1985}
74 + year={1985},
75 + doi={10.1016/0304-4076(85)90158-7}
69 76 }
70 77
71 78 @article{can1992spatial,
@@ -75,7 +82,8 @@
75 82 volume={22},
76 83 number={3},
77 84 pages={453--474},
78 − year={1992}
85 + year={1992},
86 + doi={10.1016/0166-0462(92)90039-4}
79 87 }
80 88
81 89 @book{anselin1988spatial,
@@ -83,7 +91,8 @@
83 91 author={Anselin, Luc},
84 92 publisher={Kluwer Academic Publishers},
85 93 address={Dordrecht},
86 − year={1988}
94 + year={1988},
95 + doi={10.1007/978-94-015-7799-1}
87 96 }
88 97
89 98 @article{gentzkow2019text,
@@ -93,7 +102,8 @@
93 102 volume={57},
94 103 number={3},
95 104 pages={535--574},
96 − year={2019}
105 + year={2019},
106 + doi={10.1257/jel.20181020}
97 107 }
98 108
99 109 @article{poursaeed2018vision,
@@ -103,7 +113,8 @@
103 113 volume={29},
104 114 number={4},
105 115 pages={667--676},
106 − year={2018}
116 + year={2018},
117 + doi={10.1007/s00138-018-0922-2}
107 118 }
108 119
109 120 @inproceedings{ahmed2016house,
@@ -111,7 +122,8 @@
111 122 author={Ahmed, Eman H. and Moustafa, Mohamed},
112 123 booktitle={Proceedings of the 8th International Joint Conference on Computational Intelligence (NCTA)},
113 124 pages={62--68},
114 − year={2016}
125 + year={2016},
126 + doi={10.5220/0006040700620068}
115 127 }
116 128
117 129 @article{bin2004prediction,
@@ -121,7 +133,8 @@
121 133 volume={13},
122 134 number={1},
123 135 pages={68--84},
124 − year={2004}
136 + year={2004},
137 + doi={10.1016/j.jhe.2004.01.001}
125 138 }
126 139
127 140 @article{mullainathan2017machine,
@@ -131,7 +144,8 @@
131 144 volume={31},
132 145 number={2},
133 146 pages={87--106},
134 − year={2017}
147 + year={2017},
148 + doi={10.1257/jep.31.2.87}
135 149 }
136 150
137 151 @article{halvorsen1980interpretation,
@@ -151,7 +165,8 @@
151 165 volume={45},
152 166 number={1},
153 167 pages={5--32},
154 − year={2001}
168 + year={2001},
169 + doi={10.1023/A:1010933404324}
155 170 }
156 171
157 172 @article{friedman2001greedy,
@@ -161,35 +176,43 @@
161 176 volume={29},
162 177 number={5},
163 178 pages={1189--1232},
164 − year={2001}
179 + year={2001},
180 + doi={10.1214/aos/1013203451}
165 181 }
166 182
167 183 @article{guo2016entity,
168 184 title={Entity embeddings of categorical variables},
169 185 author={Guo, Cheng and Berkhahn, Felix},
170 − journal={arXiv preprint arXiv:1604.06737},
171 − year={2016}
186 + journal={arXiv preprint},
187 + volume={arXiv:1604.06737},
188 + year={2016},
189 + doi={10.48550/arXiv.1604.06737}
172 190 }
173 191
174 192 @inproceedings{loshchilov2019decoupled,
175 193 title={Decoupled weight decay regularization},
176 194 author={Loshchilov, Ilya and Hutter, Frank},
177 195 booktitle={International Conference on Learning Representations (ICLR)},
178 − year={2019}
196 + year={2019},
197 + note={arXiv:1711.05101},
198 + doi={10.48550/arXiv.1711.05101}
179 199 }
180 200
181 201 @inproceedings{gorishniy2021revisiting,
182 202 title={Revisiting deep learning models for tabular data},
183 203 author={Gorishniy, Yury and Rubachev, Ivan and Khrulkov, Valentin and Babenko, Artem},
184 − booktitle={Advances in Neural Information Processing Systems (NeurIPS)},
185 − year={2021}
204 + booktitle={Advances in Neural Information Processing Systems (NeurIPS) 34},
205 + year={2021},
206 + note={arXiv:2106.11959},
207 + doi={10.48550/arXiv.2106.11959}
186 208 }
187 209
188 210 @inproceedings{grinsztajn2022why,
189 211 title={Why do tree-based models still outperform deep learning on tabular data?},
190 212 author={Grinsztajn, L{\'e}o and Oyallon, Edouard and Varoquaux, Ga{\"e}l},
191 − booktitle={Advances in Neural Information Processing Systems (NeurIPS)},
192 − year={2022}
213 + booktitle={Advances in Neural Information Processing Systems (NeurIPS) 35},
214 + year={2022},
215 + doi={10.52202/068431-0037}
193 216 }
194 217
195 218 @inproceedings{he2016deep,
@@ -197,7 +220,8 @@
197 220 author={He, Kaiming and Zhang, Xiangyu and Ren, Shaoqing and Sun, Jian},
198 221 booktitle={IEEE Conference on Computer Vision and Pattern Recognition (CVPR)},
199 222 pages={770--778},
200 − year={2016}
223 + year={2016},
224 + doi={10.1109/CVPR.2016.90}
201 225 }
202 226
203 227 @article{nowak2017textual,
@@ -207,7 +231,8 @@
207 231 volume={32},
208 232 number={4},
209 233 pages={896--918},
210 − year={2017}
234 + year={2017},
235 + doi={10.1002/jae.2550}
211 236 }
212 237
213 238 @article{shen2021information,
@@ -216,7 +241,8 @@
216 241 journal={Journal of Urban Economics},
217 242 volume={121},
218 243 pages={103299},
219 − year={2021}
244 + year={2021},
245 + doi={10.1016/j.jue.2020.103299}
220 246 }
221 247
222 248 @article{johnson2020curb,
@@ -226,14 +252,19 @@
226 252 volume={60},
227 253 number={1-2},
228 254 pages={111--133},
229 − year={2020}
255 + year={2020},
256 + doi={10.1007/s11146-019-09713-z}
230 257 }
231 258
232 259 @article{lindenthal2021architecture,
233 260 title={Machine learning, architectural styles and property values},
234 261 author={Lindenthal, Thies and Johnson, Erik B.},
235 262 journal={Journal of Real Estate Finance and Economics},
236 − year={2021}
263 + volume={71},
264 + number={3},
265 + pages={353--384},
266 + year={2021},
267 + doi={10.1007/s11146-021-09845-1}
237 268 }
238 269
239 270 @techreport{glaeser2018vision,
@@ -242,7 +273,8 @@
242 273 institution={National Bureau of Economic Research},
243 274 type={Working Paper},
244 275 number={25174},
245 − year={2018}
276 + year={2018},
277 + doi={10.3386/w25174}
246 278 }
247 279
248 280 @article{kok2017bigdata,
@@ -252,7 +284,8 @@
252 284 volume={43},
253 285 number={6},
254 286 pages={202--211},
255 − year={2017}
287 + year={2017},
288 + doi={10.3905/jpm.2017.43.6.202}
256 289 }
257 290
258 291 @article{hendel2009mls,
@@ -262,7 +295,8 @@
262 295 volume={99},
263 296 number={5},
264 297 pages={1878--1898},
265 − year={2009}
298 + year={2009},
299 + doi={10.1257/aer.99.5.1878}
266 300 }
267 301
268 302 @article{horowitz1992list,
@@ -272,7 +306,8 @@
272 306 volume={7},
273 307 number={2},
274 308 pages={115--129},
275 − year={1992}
309 + year={1992},
310 + doi={10.1002/jae.3950070202}
276 311 }
277 312
278 313 @article{knight2002listing,
@@ -282,7 +317,8 @@
282 317 volume={30},
283 318 number={2},
284 319 pages={213--237},
285 − year={2002}
320 + year={2002},
321 + doi={10.1111/1540-6229.00038}
286 322 }
287 323
288 324 @incollection{han2015microstructure,
@@ -292,7 +328,8 @@
292 328 volume={5},
293 329 pages={813--886},
294 330 publisher={Elsevier},
295 − year={2015}
331 + year={2015},
332 + doi={10.1016/B978-0-444-59531-7.00013-2}
296 333 }
297 334
298 335 @article{hill2013hedonic,
@@ -302,7 +339,8 @@
302 339 volume={27},
303 340 number={5},
304 341 pages={879--914},
305 − year={2013}
342 + year={2013},
343 + doi={10.1111/j.1467-6419.2012.00731.x}
306 344 }
307 345
308 346 @article{cameron2015cluster,
@@ -312,5 +350,213 @@
312 350 volume={50},
313 351 number={2},
314 352 pages={317--372},
315 − year={2015}
353 + year={2015},
354 + doi={10.3368/jhr.50.2.317}
355 +}
356 +
357 +
358 +% ---------------------------------------------------------------------------
359 +% References added in the 2026-10-17 revision (all verified via Crossref / arXiv)
360 +% ---------------------------------------------------------------------------
361 +
362 +@article{kennedy1981estimation,
363 + title={Estimation with correctly interpreted dummy variables in semilogarithmic equations},
364 + author={Kennedy, Peter E.},
365 + journal={American Economic Review},
366 + volume={71},
367 + number={4},
368 + pages={801},
369 + year={1981}
370 +}
371 +
372 +@article{naik2017computer,
373 + title={Computer vision uncovers predictors of physical urban change},
374 + author={Naik, Nikhil and Kominers, Scott Duke and Raskar, Ramesh and Glaeser, Edward L. and Hidalgo, C{\'e}sar A.},
375 + journal={Proceedings of the National Academy of Sciences},
376 + volume={114},
377 + number={29},
378 + pages={7571--7576},
379 + year={2017},
380 + doi={10.1073/pnas.1619003114}
381 +}
382 +
383 +@article{law2019take,
384 + title={Take a look around: Using street view and satellite images to estimate house prices},
385 + author={Law, Stephen and Paige, Brooks and Russell, Chris},
386 + journal={ACM Transactions on Intelligent Systems and Technology},
387 + volume={10},
388 + number={5},
389 + pages={1--19},
390 + year={2019},
391 + doi={10.1145/3342240}
392 +}
393 +
394 +@article{zhang2018greenery,
395 + title={Impacts of street-visible greenery on housing prices: Evidence from a hedonic price model and a massive street view image dataset in {Beijing}},
396 + author={Zhang, Yonglin and Dong, Rencai},
397 + journal={ISPRS International Journal of Geo-Information},
398 + volume={7},
399 + number={3},
400 + pages={104},
401 + year={2018},
402 + doi={10.3390/ijgi7030104}
403 +}
404 +
405 +@article{kang2021understanding,
406 + title={Understanding house price appreciation using multi-source big geo-data and machine learning},
407 + author={Kang, Yuhao and Zhang, Fan and Peng, Wenzhe and Gao, Song and Rao, Jinmeng and Duarte, Fabio and Ratti, Carlo},
408 + journal={Land Use Policy},
409 + volume={111},
410 + pages={104919},
411 + year={2021},
412 + doi={10.1016/j.landusepol.2020.104919}
413 +}
414 +
415 +@inproceedings{bessinger2016quantifying,
416 + title={Quantifying curb appeal},
417 + author={Bessinger, Zachary and Jacobs, Nathan},
418 + booktitle={2016 IEEE International Conference on Image Processing (ICIP)},
419 + pages={4388--4392},
420 + year={2016},
421 + doi={10.1109/ICIP.2016.7533189}
422 +}
423 +
424 +@article{nowak2020quality,
425 + title={Quality-adjusted house price indexes},
426 + author={Nowak, Adam D. and Smith, Patrick S.},
427 + journal={American Economic Review: Insights},
428 + volume={2},
429 + number={3},
430 + pages={339--356},
431 + year={2020},
432 + doi={10.1257/aeri.20190337}
433 +}
434 +
435 +@article{nowak2021dictionaries,
436 + title={Real estate dictionaries across space and time},
437 + author={Nowak, Adam D. and Price, Bradley S. and Smith, Patrick S.},
438 + journal={Journal of Real Estate Finance and Economics},
439 + volume={62},
440 + number={1},
441 + pages={139--163},
442 + year={2021},
443 + doi={10.1007/s11146-019-09740-w}
444 +}
445 +
446 +@article{zhang2024describe,
447 + title={Describe the house and {I} will tell you the price: House price prediction with textual description data},
448 + author={Zhang, Hanxiang and Li, Yansong and Branco, Paula},
449 + journal={Natural Language Engineering},
450 + volume={30},
451 + number={4},
452 + pages={661--695},
453 + year={2024},
454 + doi={10.1017/S1351324923000360}
455 +}
456 +
457 +@article{dell2025deep,
458 + title={Deep learning for economists},
459 + author={Dell, Melissa},
460 + journal={Journal of Economic Literature},
461 + volume={63},
462 + number={1},
463 + pages={5--58},
464 + year={2025},
465 + doi={10.1257/jel.20241733}
466 +}
467 +
468 +@article{korinek2023generative,
469 + title={Generative {AI} for economic research: Use cases and implications for economists},
470 + author={Korinek, Anton},
471 + journal={Journal of Economic Literature},
472 + volume={61},
473 + number={4},
474 + pages={1281--1317},
475 + year={2023},
476 + doi={10.1257/jel.20231736}
477 +}
478 +
479 +@article{gilardi2023chatgpt,
480 + title={{ChatGPT} outperforms crowd workers for text-annotation tasks},
481 + author={Gilardi, Fabrizio and Alizadeh, Meysam and Kubli, Ma{\"e}l},
482 + journal={Proceedings of the National Academy of Sciences},
483 + volume={120},
484 + number={30},
485 + pages={e2305016120},
486 + year={2023},
487 + doi={10.1073/pnas.2305016120}
488 +}
489 +
490 +@article{han2016asking,
491 + title={What is the role of the asking price for a house?},
492 + author={Han, Lu and Strange, William C.},
493 + journal={Journal of Urban Economics},
494 + volume={93},
495 + pages={115--130},
496 + year={2016},
497 + doi={10.1016/j.jue.2016.03.008}
498 +}
499 +
500 +@article{pace2020examining,
501 + title={Examining the information content of residuals from hedonic and spatial models using trees and forests},
502 + author={Pace, R. Kelley and Hayunga, Darren},
503 + journal={Journal of Real Estate Finance and Economics},
504 + volume={60},
505 + number={1--2},
506 + pages={170--180},
507 + year={2020},
508 + doi={10.1007/s11146-019-09724-w}
509 +}
510 +
511 +@article{bourassa2010predicting,
512 + title={Predicting house prices with spatial dependence: A comparison of alternative methods},
513 + author={Bourassa, Steven C. and Cantoni, Eva and Hoesli, Martin},
514 + journal={Journal of Real Estate Research},
515 + volume={32},
516 + number={2},
517 + pages={139--160},
518 + year={2010},
519 + doi={10.1080/10835547.2010.12091276}
520 +}
521 +
522 +@article{roberts2017cross,
523 + title={Cross-validation strategies for data with temporal, spatial, hierarchical, or phylogenetic structure},
524 + author={Roberts, David R. and Bahn, Volker and Ciuti, Simone and Boyce, Mark S. and Elith, Jane and Guillera-Arroita, Gurutzeta and Hauenstein, Severin and Lahoz-Monfort, Jos{\'e} J. and Schr{\"o}der, Boris and Thuiller, Wilfried and Warton, David I. and Wintle, Brendan A. and Hartig, Florian and Dormann, Carsten F.},
525 + journal={Ecography},
526 + volume={40},
527 + number={8},
528 + pages={913--929},
529 + year={2017},
530 + doi={10.1111/ecog.02881}
531 +}
532 +
533 +@article{abadie2023when,
534 + title={When should you adjust standard errors for clustering?},
535 + author={Abadie, Alberto and Athey, Susan and Imbens, Guido W. and Wooldridge, Jeffrey M.},
536 + journal={Quarterly Journal of Economics},
537 + volume={138},
538 + number={1},
539 + pages={1--35},
540 + year={2023},
541 + doi={10.1093/qje/qjac038}
542 +}
543 +
544 +@article{mackinnon2017wild,
545 + title={Wild bootstrap inference for wildly different cluster sizes},
546 + author={MacKinnon, James G. and Webb, Matthew D.},
547 + journal={Journal of Applied Econometrics},
548 + volume={32},
549 + number={2},
550 + pages={233--254},
551 + year={2017},
552 + doi={10.1002/jae.2508}
553 +}
554 +
555 +@techreport{boucher2026decoding,
556 + title={Decoding real estate descriptions: Semantic embeddings and hedonic pricing of residential properties in {Quebec}},
557 + author={Boucher, Simon-Pierre},
558 + institution={Universit\'e du Qu\'ebec en Outaouais, D\'epartement des sciences administratives},
559 + type={UQO Working Paper},
560 + number={2},
561 + year={2026}
316 562 }
modified paper/sections/benchmark.tex +87 −62
@@ -8,7 +8,7 @@ coefficients are implicit prices. It is not, however, the most accurate predicto
8 8 price. To map the accuracy frontier---and to gauge how much curvature and interaction
9 9 the linear model leaves on the table---we benchmark a ladder of estimators of
10 10 increasing flexibility on the \emph{identical} feature set, under a common
11 −5-fold cross-validation protocol. All rows of Table~\ref{tab:models} share the same
11 +five-fold cross-validation protocol. All rows of Table~\ref{tab:models} share the same
12 12 folds and random seed, so differences reflect the estimator, not the split.
13 13
14 14 \subsection{The Estimator Ladder}
@@ -21,9 +21,9 @@ the interpretable baseline and, mechanically, the least flexible: it imposes con
21 21 marginal effects and additive separability.
22 22
23 23 \paragraph{(2) Random forest.} A bagged ensemble of regression trees
24 −\citep{mullainathan2017machine}. Trees capture non-linearities and low-order
25 −interactions automatically and handle missing values natively, but averaging
26 −deep decorrelated trees tends to over-smooth sharp price thresholds.
24 +\citep{breiman2001random,mullainathan2017machine}. Trees capture non-linearities and
25 +low-order interactions automatically and handle missing values natively, but
26 +averaging deep decorrelated trees tends to over-smooth sharp price thresholds.
27 27
28 28 \paragraph{(3) Gradient boosting.} A histogram-based gradient-boosted tree ensemble
29 29 (600 boosting iterations, learning rate 0.05, up to 63 leaves, $L_2$ regularization).
@@ -33,14 +33,17 @@ values directly. It is the strongest single model in our comparison.
33 33
34 34 \paragraph{(4) Neural networks.} A plain multilayer perceptron and a robust
35 35 residual network with entity embeddings, described in
36 −Section~\ref{subsec:nn}. Neural networks excel when high-cardinality categoricals
37 −(here, 195 municipalities) can be represented as dense learned vectors rather than
38 −sparse indicators.
36 +Section~\ref{subsec:nn}. Neural networks are attractive when high-cardinality
37 +categoricals (here, 195 municipalities) can be represented as dense learned vectors
38 +rather than sparse indicators.
39 39
40 −\paragraph{(5) Blend.} A simple 50/50 average of the robust neural network and the
41 −gradient-boosting predictions. Because the two models make partly decorrelated
40 +\paragraph{(5) Blend.} An equally weighted average of the robust neural network and
41 +the gradient-boosting predictions. Because the two models make partly decorrelated
42 42 errors---the network smooths through embeddings while the boosted trees cut sharp
43 −thresholds---their average cancels idiosyncratic noise, a standard stacking result.
43 +thresholds---their average cancels idiosyncratic noise, a standard model-averaging
44 +result. The weight of one half was fixed \emph{a priori}; Section~\ref{subsec:bench_results}
45 +reports how the result varies with the weight and under a nested choice of the
46 +weight.
44 47
45 48 \subsection{Formal Definitions}
46 49 \label{subsec:formal}
@@ -61,7 +64,9 @@ trees, each grown on a bootstrap resample with a random feature subset at every
61 64 \label{eq:rf}
62 65 \end{equation}
63 66 %
64 −where $\Theta_b$ denotes the randomness (resample and split subsets) of tree~$b$.
67 +where $\Theta_b$ denotes the randomness (resample and split subsets) of tree~$b$;
68 +we use $B=400$ trees, half of the features at each split, and a minimum leaf size
69 +of three.
65 70
66 71 \paragraph{Gradient boosting \citep{friedman2001greedy}.} A forward stagewise additive
67 72 expansion built by fitting each new tree to the negative gradient of the loss:
@@ -78,8 +83,8 @@ expansion built by fitting each new tree to the negative gradient of the loss:
78 83 \label{eq:gbm}
79 84 \end{equation}
80 85 %
81 −with learning rate $\nu$ and $L_2$-regularized histogram trees $f_m$; missing values
82 −are routed to a learned default direction at each split.
86 +with learning rate $\nu$, squared-error loss $L$, and $L_2$-regularized histogram
87 +trees $f_m$; missing values are routed to a learned default direction at each split.
83 88
84 89 \paragraph{Neural network (robust).} High-cardinality categoricals are mapped to
85 90 learned embeddings \citep{guo2016entity}, concatenated with the batch-normalized
@@ -119,9 +124,11 @@ Parameters minimize the Huber (smooth-$L_1$) loss, robust to price outliers,
119 124 \label{eq:huber}
120 125 \end{equation}
121 126 %
122 −optimized with AdamW (decoupled weight decay) \citep{loshchilov2019decoupled} under a
123 −cosine-annealed learning rate $\eta_t=\tfrac12\eta_0\big(1+\cos(\pi t/T)\big)$,
124 −gradient-norm clipping, and early stopping. Three seeds are averaged per fold.
127 +on the standardized target, optimized with AdamW (decoupled weight decay)
128 +\citep{loshchilov2019decoupled} under a cosine-annealed learning rate
129 +$\eta_t=\tfrac12\eta_0\big(1+\cos(\pi t/T)\big)$, gradient-norm clipping, and early
130 +stopping on a 10\% validation split of each training fold. Three seeds are averaged
131 +per fold.
125 132
126 133 \paragraph{Blend.} A convex combination of the two strongest models in log space,
127 134 %
@@ -141,9 +148,14 @@ accuracy,
141 148 R^2_{\text{OOS}} = 1-\frac{\sum_i\big(y_i-\hat y_{-k(i)}\big)^2}
142 149 {\sum_i\big(y_i-\bar y\big)^2},
143 150 \qquad
144 − \text{MdAPE}=\operatorname*{median}_i\Big|e^{\,\hat y_{-k(i)}-y_i}-1\Big|.
151 + \text{MdAPE}=\operatorname*{median}_i\Big|e^{\,\hat y_{-k(i)}-y_i}-1\Big|,
145 152 \label{eq:cvmetrics}
146 153 \end{equation}
154 +%
155 +together with the share of listings predicted within $\pm15\%$ and $\pm25\%$ of the
156 +asking price. The MdAPE is computed on the ratio of predicted to actual price and is
157 +therefore unaffected by the retransformation constant of
158 +Equation~\eqref{eq:smearing}.
147 159
148 160 \subsection{Results}
149 161 \label{subsec:bench_results}
@@ -151,37 +163,46 @@ accuracy,
151 163 \input{tables/models}
152 164
153 165 Table~\ref{tab:models} reports the ladder in two panels. In Panel~A (structural,
154 −amenity, and text features) out-of-sample $R^2$ rises monotonically with flexibility:
155 −from 0.628 for OLS, to 0.677 for the random forest, 0.711 for the plain network, and a
156 −near-tie at the top between the robust network (0.741) and gradient boosting (0.743);
157 −their blend attains $R^2_{\text{OOS}}=0.760$. Panel~B adds the twelve façade vision
158 −attributes. Vision lifts \emph{every} estimator---OLS gains 5.8 points
159 −(0.628 $\to$ 0.686), the random forest 2.7 (0.677 $\to$ 0.704), and gradient boosting
160 −2.7 (0.743 $\to$ 0.770), the single largest increment from any one feature layer at
161 −the frontier---and the \textbf{text\,+\,vision blend attains
162 −$R^2_{\text{OOS}}=0.779$ with a median absolute error of 14.0\%}, the best model in
163 −the study.
166 +amenity, room, listing, and text features) out-of-sample $R^2$ rises monotonically
167 +with flexibility: from 0.628 for OLS, to 0.677 for the random forest, 0.711 for the
168 +plain network, and a near-tie at the top between the robust network (0.741) and
169 +gradient boosting (0.743); their blend attains $R^2_{\text{OOS}}=0.760$. Panel~B adds
170 +the façade vision layer. Vision lifts every estimator---OLS gains 5.8 points
171 +(0.628 $\to$ 0.686), the random forest 2.7 (0.677 $\to$ 0.704), the robust network
172 +0.6 (0.741 $\to$ 0.747), and gradient boosting 2.7 (0.743 $\to$ 0.770)---and the
173 +text\,$+$\,vision blend attains $R^2_{\text{OOS}}=0.779$ with a median absolute
174 +percentage error of 14.0\%, the best model in the study.
164 175
165 176 Three lessons follow. First, on the full feature set roughly nine points of
166 177 out-of-sample $R^2$ separate the interpretable linear model (0.686) from the
167 178 predictive frontier (0.779)---the price of interpretability, and a measure of the
168 179 curvature and interactions the hedonic form omits. Notably, the vision layer buys the
169 −linear model more accuracy (+5.8 points) than switching from OLS to a random forest
170 −does (+4.9), so better measurement rivals better estimation. Second, on this tabular
171 −problem gradient boosting and a carefully regularized neural network are
172 −statistically comparable, consistent with the broader evidence that tree ensembles
173 −remain strong baselines on tabular data \citep{grinsztajn2022why}; the gains come
174 −from \emph{combining} them. Third, the façade-vision layer is not redundant with the
175 −text and structural fields: it moves the frontier more than any other block we add,
176 −confirming that image-derived quality is genuinely orthogonal information.
180 +linear model more accuracy ($+5.8$ points) than switching from OLS to a random forest
181 +does ($+4.9$ points in Panel~A), so better measurement rivals better estimation.
182 +Second, on this tabular problem gradient boosting and a carefully regularized neural
183 +network are statistically comparable, consistent with the broader evidence that tree
184 +ensembles remain strong baselines on tabular data \citep{grinsztajn2022why}; the
185 +gains come from \emph{combining} them. Third, the façade-vision layer is not redundant
186 +with the text and structural fields: it is the more informative of the two AI layers
187 +for every estimator.
188 +
189 +The equal-weight blend is not the best achievable combination, but it is close.
190 +Varying the weight $w$ on gradient boosting in Equation~\eqref{eq:blend} over
191 +$\{0.3,0.4,0.5,0.6,0.7\}$ gives out-of-sample $R^2$ of 0.771, 0.776, 0.779, 0.780,
192 +and 0.780 for the text\,$+$\,vision models, so $w=0.5$ is within 0.002 of the best
193 +weight on the grid; choosing $w$ in a nested fashion---on the four training folds,
194 +then applied to the fifth---returns $R^2_{\text{OOS}}=0.775$, slightly below the
195 +fixed equal weight, because the fold-specific optimal weights are noisy. For the
196 +text-only models the equal weight is exactly the best on the grid (0.760). We
197 +therefore keep the fixed equal weight and do not tune it on the test predictions.
177 198
178 199 \begin{figure}[htbp]
179 200 \centering
180 201 \includegraphics[width=.72\textwidth]{figures/fig_ablation.pdf}
181 − \caption{Out-of-sample $R^2$ as feature layers are added (nested, left to right).
182 − Each layer improves both the linear and boosted models; the façade-vision layer
183 − delivers the largest single jump---about $+5.8$ points for OLS and $+2.7$ for
184 − gradient boosting.}
202 + \caption{Out-of-sample $R^2$ as feature layers are added (nested, left to right),
203 + random five-fold cross-validation. Each layer improves both the linear and boosted
204 + models; the façade-vision layer adds about $+5.8$ points for OLS and $+2.7$ for
205 + gradient boosting, the text layer $+0.9$ and $+1.4$.}
185 206 \label{fig:ablation}
186 207 \end{figure}
187 208
@@ -189,28 +210,31 @@ confirming that image-derived quality is genuinely orthogonal information.
189 210 \label{subsec:ablation}
190 211
191 212 Table~\ref{tab:ablation} and Figure~\ref{fig:ablation} decompose the accuracy gains
192 −layer by layer. Reading down the table, room detail and listing signals add the first
193 −large increment, the LLM text layer a smaller one, and the vision layer the single
194 −largest jump for every estimator---raising OLS from 0.628 to 0.686 and gradient
195 −boosting from 0.743 to 0.770 out of sample. The pattern is monotone: no layer degrades
196 −fit, and the two AI layers together account for a substantial share of the distance
197 −between the structural baseline and the frontier.
213 +layer by layer. Reading down the table, the room-detail and listing-signal block adds
214 +the largest increment of all (9.8 points for OLS, 3.7 for gradient boosting), which
215 +reflects both the information in the Realtor.ca room block and the fact that this
216 +block carries the platform's identity; the LLM text layer adds 0.9 and 1.4 points;
217 +and the vision layer adds 5.8 and 2.7 points, the largest increment of the two AI
218 +layers and the second largest overall. The pattern is monotone: no layer degrades
219 +fit. Under spatially grouped folds (Table~\ref{tab:rob_spatial},
220 +Section~\ref{subsec:robust_spatial}) all levels fall by three to five points, but the
221 +increments from the text and vision layers are essentially unchanged.
198 222
199 223 \input{tables/ablation}
200 224
201 225 Figure~\ref{fig:importance} aggregates the gradient-boosting permutation importances
202 226 into feature blocks. Structural attributes and location fixed effects dominate, as
203 −expected, but the \textbf{vision block alone accounts for 13\% of total importance}---
204 −more than the rule-based amenities and the LLM text block combined---reinforcing that
205 −the façade photograph is a first-order, not marginal, source of price information.
206 −Figure~\ref{fig:calibration} shows the blend is well calibrated across the price range,
207 −with an approximately symmetric, tight error distribution centered near zero.
227 +expected, but the vision block accounts for 13\% of total importance---more than the
228 +rule-based amenities and the LLM text block combined---so the façade photograph is a
229 +first-order, not marginal, source of price information in the boosted model.
230 +Figure~\ref{fig:calibration} shows that the blend is well calibrated across the price
231 +range, with an approximately symmetric error distribution centred near zero.
208 232
209 233 \begin{figure}[htbp]
210 234 \centering
211 235 \includegraphics[width=.72\textwidth]{figures/fig_importance.pdf}
212 236 \caption{Share of gradient-boosting permutation importance by feature block
213 − (text\,+\,vision model). The vision block accounts for 13\% of total importance,
237 + (text\,$+$\,vision model). The vision block accounts for 13\% of total importance,
214 238 exceeding the rule-based amenity and LLM-text blocks.}
215 239 \label{fig:importance}
216 240 \end{figure}
@@ -218,7 +242,7 @@ with an approximately symmetric, tight error distribution centered near zero.
218 242 \begin{figure}[htbp]
219 243 \centering
220 244 \includegraphics[width=\textwidth]{figures/fig_calibration.pdf}
221 − \caption{Out-of-sample calibration of the text\,+\,vision blend. Left: predicted
245 + \caption{Out-of-sample calibration of the text\,$+$\,vision blend. Left: predicted
222 246 versus actual price (log--log; dashed line is the 45$^\circ$ identity). Right:
223 247 distribution of percentage prediction errors, with median absolute error of 14.0\%.}
224 248 \label{fig:calibration}
@@ -230,22 +254,23 @@ with an approximately symmetric, tight error distribution centered near zero.
230 254 Figure~\ref{fig:nn} details the robust network. High-cardinality categoricals enter
231 255 as learned \textbf{entity embeddings}: each municipality, property type, and source is
232 256 mapped to a dense vector (dimension $\approx 1.6\,C^{0.56}$ in cardinality $C$, so
233 −about 29 dimensions for the 195 cities). The embeddings are concatenated with the
234 −batch-normalized numeric block and projected to a 256-dimensional trunk, which passes
235 −through three \textbf{residual blocks}
236 −(BatchNorm $\to$ GELU $\to$ Linear$_{512}$ $\to$ Dropout $\to$ Linear$_{256}$, with a
237 −skip connection), followed by a normalization head that outputs $\ln(\text{Price})$.
257 +about 30 dimensions for the 195 municipalities). The embeddings are concatenated with
258 +the batch-normalized numeric block and projected to a 256-dimensional trunk, which
259 +passes through three \textbf{residual blocks} (BatchNorm $\to$ Linear$_{512}$ $\to$
260 +GELU $\to$ Dropout $\to$ Linear$_{256}$ $\to$ Dropout, with a skip connection),
261 +followed by a normalization head that outputs $\ln(\text{Price})$.
238 262
239 263 Robustness comes from the training recipe rather than raw capacity:
240 264 a \textbf{Huber (smooth-$L_1$) loss} that damps the influence of price outliers;
241 −\textbf{AdamW} with decoupled weight decay and \textbf{cosine-annealed} learning rate;
265 +\textbf{AdamW} with decoupled weight decay and a \textbf{cosine-annealed} learning rate;
242 266 \textbf{gradient-norm clipping} at 5; \textbf{dropout} and batch normalization
243 −throughout; \textbf{learned missingness indicators} appended to the numeric block so
267 +throughout; \textbf{missingness indicators} appended to the numeric block so
244 268 the network can exploit the non-random pattern of missing fields; and
245 269 \textbf{early stopping} on a held-out 10\% of each training fold. Finally, three
246 270 networks with different random seeds are trained per fold and \textbf{averaged}, which
247 −reduces prediction variance. The result (0.741) is competitive with gradient boosting
248 −and, once blended with it, produces the best model in the study.
271 +reduces prediction variance. The result (0.741 with text, 0.747 with text and vision)
272 +is competitive with gradient boosting and, once blended with it, produces the best
273 +model in the study.
249 274
250 275 \begin{figure}[htbp]
251 276 \centering
@@ -255,6 +280,6 @@ and, once blended with it, produces the best model in the study.
255 280 augmented with missingness indicators. The concatenated representation is projected
256 281 to a 256-dimensional trunk, refined by three residual blocks, and mapped to
257 282 $\ln(\text{Price})$. Three seeds are ensembled per fold; the final model blends this
258 − network 50/50 with gradient boosting.}
283 + network equally with gradient boosting.}
259 284 \label{fig:nn}
260 285 \end{figure}
modified paper/sections/conclusion.tex +36 −29
@@ -6,36 +6,43 @@
6 6 We have estimated a hedonic price model for Quebec houses that augments the standard
7 7 set of structural, locational, and amenity attributes with two machine-generated
8 8 feature layers: structured specifications extracted from free-text descriptions by a
9 −large language model, and objective visual attributes extracted from façade
10 −photographs by a vision--language model. Schema-constrained decoding renders these
11 −outputs as ordinary, reproducible econometric variables, obtained at a marginal cost
12 −of a fraction of a cent per listing. The central message is methodological:
13 −general-purpose AI models, disciplined by strict output schemas, function as a
14 −low-cost feature-engineering technology that measurably expands the characteristics
15 −set available to hedonic analysis.
9 +large language model, and visual attributes extracted from façade photographs by a
10 +vision--language model. Schema-constrained decoding renders these outputs as
11 +ordinary, auditable econometric variables, obtained at a marginal cost of a fraction
12 +of a cent per listing. The central message is methodological: general-purpose AI
13 +models, disciplined by strict output schemas, function as a low-cost
14 +feature-engineering technology that measurably expands the characteristics set
15 +available to hedonic analysis---and the discipline must extend to the input, since
16 +the text handed to the model may contain the very price one is trying to explain.
16 17
17 −The gains are economically meaningful. The two AI layers add nearly four points of
18 −in-sample $R^2$ to an already rich log--linear specification (0.717 to 0.755 with
19 −living area observed), and they raise the out-of-sample accuracy of every estimator
20 −in our ladder, from OLS to gradient boosting and an embedding-based residual neural
21 −network; the best blend attains an out-of-sample $R^2$ of 0.779 with a median
22 −absolute prediction error of 14.0\%. The façade-vision layer alone contributes the
23 −largest increment of any feature block (2.7 points for gradient boosting, 5.8 for
24 −OLS), confirming that image-derived quality is genuinely orthogonal to text and
25 −structured fields. Crucially for hedonic practice, the extracted attributes carry
26 −interpretable implicit prices---curb appeal, renovation level, luxury cues, and
27 −exterior materials all price sensibly and survive municipality-clustered
28 −inference---so the technology augments the classical regression rather than
29 −replacing it with a black box.
18 +The gains are economically meaningful but unequal across the two layers. Together
19 +they add 3.8 points of in-sample $R^2$ to an already rich log-linear specification
20 +(0.717 to 0.755 with living area observed), of which 3.0 come from the façade
21 +photograph and 0.8 from the text; they raise the out-of-sample accuracy of every
22 +estimator in our ladder, from OLS to gradient boosting and an embedding-based
23 +residual neural network; and the best blend attains an out-of-sample $R^2$ of 0.779
24 +with a median absolute percentage error of 14.0\% under random folds, 0.734 for
25 +gradient boosting when entire postal areas are held out. The vision layer adds more
26 +to the linear model than switching from OLS to a random forest does. The extracted
27 +attributes carry interpretable implicit prices---curb appeal, renovation level,
28 +luxury cues, and exterior materials all price sensibly---that survive
29 +municipality-clustered inference, a cluster bootstrap, finer postal-code fixed
30 +effects, and a control for the language of the description. The text layer's
31 +contribution, by contrast, is modest once keyword amenities are already in the model,
32 +and part of its measured gain on the for-sale-by-owner platform reflects the asking
33 +price embedded in those descriptions rather than information about the house.
30 34
31 35 Three extensions are natural. First, the same schema-constrained approach scales
32 36 from a single façade image to a small panel of interior photographs (kitchen,
33 −bathroom, living space), which our design already accommodates. Second, the
34 −DuProprio coordinates permit continuous spatial features---distance to the urban
35 −core, to water, or to amenities---that would refine the geography beyond discrete
36 −city effects. Third, and most important for external validity, linking listings to
37 −registry transaction prices would establish how much of the AI-measured quality
38 −premium survives bargaining. For appraisal, automated valuation, and property-tax
39 −assessment---applications that consume hedonic models at scale---the practical
40 −implication is immediate: the unstructured majority of a listing's information
41 −content is no longer out of reach.
37 +bathroom, living space), which our design already accommodates; a repeat extraction
38 +of a subsample at a fixed temperature, and a human-labelled validation set, would
39 +also quantify the extraction error that we could only measure indirectly. Second,
40 +the DuProprio coordinates and the postal codes of all listings permit continuous
41 +spatial features---distance to the urban core, to water, or to amenities---that would
42 +refine the geography beyond discrete area effects. Third, and most important for
43 +external validity, linking listings to registry transaction prices would establish
44 +how much of the AI-measured quality premium survives bargaining. For appraisal,
45 +automated valuation, and property-tax assessment---applications that consume hedonic
46 +models at scale---the practical implication is that the unstructured majority of a
47 +listing's information content is no longer out of reach, provided that it is read
48 +with the same care as any other regressor.
modified paper/sections/data.tex +64 −19
@@ -7,25 +7,26 @@
7 7 \label{subsec:data_sources}
8 8
9 9 We assemble a cross-section of residential listings from two complementary Quebec
10 −platforms, both retrieved on 3~July~2026 via web-scraping actors. The first,
10 +platforms, both retrieved on 3~July~2026 with web-scraping actors. The first,
11 11 \textbf{Realtor.ca}, is the national Multiple Listing Service (MLS) portal and
12 12 provides broker-listed inventory (27{,}302 raw records). The second,
13 −\textbf{DuProprio}, is the dominant for-sale-by-owner platform in Quebec (7{,}693
14 −raw records). Pooling the two sources broadens coverage across price segments and
15 −sale channels---brokered and FSBO listings differ systematically in seller
16 −composition and marketing \citep{hendel2009mls}---and a source indicator absorbs any
17 −systematic level difference between them. The price we observe is the \emph{asking}
18 −price; Section~\ref{subsec:limits} discusses the interpretation of hedonic
19 −coefficients estimated on list rather than transaction prices
20 −\citep{horowitz1992list,knight2002listing}.
13 +\textbf{DuProprio}, is the dominant for-sale-by-owner (FSBO) platform in Quebec
14 +(7{,}693 raw records). Pooling the two sources broadens coverage across price
15 +segments and sale channels---brokered and FSBO listings differ systematically in
16 +seller composition and marketing \citep{hendel2009mls}---and a source indicator
17 +absorbs any systematic level difference between them. The price we observe is the
18 +\emph{asking} price; Section~\ref{subsec:limits} discusses the interpretation of
19 +hedonic coefficients estimated on list rather than transaction prices
20 +\citep{horowitz1992list,knight2002listing,han2016asking}.
21 21
22 −Each record is a structured JSON object containing a price, an address, a
23 −property-attribute block, a free-text description, a list of features, and an array
24 −of image URLs. The two sources differ in their auxiliary fields: Realtor.ca records
25 −include room-by-room dimensions but no geographic coordinates, whereas DuProprio
26 −records include coordinates and captioned images but no room block. We construct a
27 −unique key by concatenating the source and the listing identifier. Because the data
28 −form a single-date cross-section, no time dimension is modeled.
22 +Each record is a structured JSON object containing a price, an address with a
23 +postal code, a property-attribute block, a free-text description, a list of
24 +features, and an array of image URLs. The two sources differ in their auxiliary
25 +fields: Realtor.ca records include room-by-room dimensions but no geographic
26 +coordinates, whereas DuProprio records include coordinates and captioned images but
27 +no room block. We construct a unique key by concatenating the source and the listing
28 +identifier. Because the data form a single-date cross-section, no time dimension is
29 +modelled and no forward-in-time holdout is possible.
29 30
30 31 \subsection{Sample Construction}
31 32 \label{subsec:sample}
@@ -52,7 +53,47 @@ deleting the observation, preserving sample size. Finally, we trim the
52 53 price-per-square-foot distribution at its 0.5th and 99.5th percentiles
53 54 (approximately \$73--\$1{,}276/ft$^2$) among listings with a measured area, removing
54 55 residual price- or area-parsing errors. The cleaned sample contains
55 −\textbf{19{,}123 houses}, of which 80.3\% originate from Realtor.ca.
56 +\textbf{19{,}123 houses}, of which 80.3\% originate from Realtor.ca
57 +(Table~\ref{tab:app_flow} in the Appendix summarizes the flow).
58 +
59 +\subsection{Content and Language of the Descriptions}
60 +\label{subsec:text_content}
61 +
62 +Because the language model reads the description verbatim, what the description
63 +contains matters for the interpretation of the text layer. The two platforms differ
64 +sharply. A Realtor.ca description is a free-text paragraph written by the listing
65 +broker (mean length about 540 characters); only 2.5\% of these descriptions contain any
66 +dollar amount, and fewer than 0.3\% mention the municipal assessment. A DuProprio
67 +``description,'' as delivered by the crawler, concatenates the owner's comments with
68 +the platform's structured property sheet rendered as text: room counts, living and
69 +lot area, year of construction, exterior cladding, heating, inclusions, and---in
70 +every one of the 3{,}767 DuProprio listings in the cleaned sample---the asking price
71 +itself (``Asking Price \$499,999''), which equals the recorded list price in 3{,}761
72 +cases; 46.6\% of DuProprio descriptions also mention the municipal assessment. The
73 +language model was therefore shown the dependent variable for 19.7\% of the sample.
74 +The extraction schema has no price field, and the prompt instructs the model to
75 +report only what the text states about the property, so leakage can operate only
76 +indirectly (a model that sees a high price may, for instance, lean towards
77 +``like-new'' when the condition is ambiguous). We treat this as a first-order
78 +threat to the text layer and address it in Sections~\ref{subsec:robust_leakage}
79 +and~\ref{subsec:limits}: all text-layer gains are reported separately for
80 +Realtor.ca, where the text contains no price, and for DuProprio. The vision model
81 +receives only the image URL and never sees any text or price.
82 +
83 +Quebec is a bilingual listing market. Classifying each description from counts of
84 +French and English stop words (a ratio rule of 1.5, as in \citealp{boucher2026decoding}),
85 +44.6\% of descriptions are French, 43.1\% English, and 12.3\% ``mixed''---mostly
86 +DuProprio descriptions that juxtapose an automated English translation and the
87 +French original. Within Realtor.ca, 57.0\% of descriptions are French and 41.9\%
88 +English, and the two groups are priced very differently: the median asking price of
89 +English-language Realtor.ca listings is \$749{,}000 against \$524{,}900 for French
90 +ones, a raw gap of 0.37 log points that falls to 0.21 within municipalities. English
91 +descriptions concentrate in the Montreal West Island and other affluent anglophone
92 +municipalities, so language proxies for location and for the clientele targeted by
93 +the broker. Because the LLM processes both languages with the same schema, language
94 +could also affect the extraction itself. We therefore add language indicators to the
95 +hedonic specification and re-estimate the gains from both AI layers within each
96 +language group (Section~\ref{subsec:robust_language}).
56 97
57 98 \subsection{Summary Statistics}
58 99 \label{subsec:summary_stats}
@@ -62,13 +103,17 @@ The median list price is \$599{,}900. The distribution of living area is right-s
62 103 motivating the logarithmic transformation adopted in Section~\ref{sec:methodology}.
63 104 Living area is reported for only a minority of Realtor.ca listings; where it is
64 105 missing we reconstruct it from room-level dimensions (Section~\ref{subsec:structural}),
65 −and where neither is available we rely on the area-free specification.
106 +and where neither is available we rely on the area-free specification. The room
107 +count is available only for Realtor.ca listings (the DuProprio room block is absent
108 +and coded as zero rooms, with the source indicator absorbing the level).
66 109
67 110 \input{tables/summary_stats}
68 111
69 112 Table~\ref{tab:ai_summary} previews the machine-generated attributes described in
70 113 Section~\ref{sec:methodology}: roughly a quarter of listings are described as recently
71 114 renovated, the mean visual condition and curb-appeal scores are 7.4 and 7.2 on a
72 −10-point scale, and 43\% of façades display a visual luxury cue.
115 +10-point scale, and 43\% of façades display a visual luxury cue. The text layer
116 +covers all 19{,}123 listings and the vision layer all 19{,}123 photographs, of which
117 +95.8\% were recognized by the model as exterior shots.
73 118
74 119 \input{tables/ai_summary}
modified paper/sections/introduction.tex +86 −59
@@ -11,77 +11,104 @@ performance of hedonic models is bounded by the richness of the characteristics
11 11 analyst can measure. A large share of what determines a home's value---its state of
12 12 repair, the quality of its finishes, its curb appeal, the character of its
13 13 street---is recorded not in structured fields but in the free-text description and
14 −the photographs that accompany each listing. These modalities have historically been
14 +the photographs that accompany each listing. These modalities have long been
15 15 discarded because they were costly to encode at scale, leaving hedonic estimates
16 −exposed to omitted-quality bias and hedonic predictions short of their potential
16 +exposed to omitted-quality bias and hedonic predictions short of their attainable
17 17 accuracy.
18 18
19 19 This paper treats general-purpose artificial-intelligence models as a
20 −\emph{feature-engineering technology} for hedonic analysis, and quantifies the
20 +\emph{feature-engineering technology} for hedonic analysis and quantifies the
21 21 explanatory power that technology adds. We assemble a cross-section of 19{,}123
22 −single-family houses and townhouses listed for sale in Quebec, pooling the
23 −brokerage-listed MLS inventory from Realtor.ca with the for-sale-by-owner inventory
24 −from DuProprio. On top of standard structural, locational, and amenity attributes,
25 −we build two machine-generated feature layers. First, a large language model (LLM)
26 −reads each free-text description and returns a fixed set of property
27 −specifications---state of repair, renovation of the kitchen and bathroom, flooring
28 −and countertop materials, heating system, view, and income potential---under a
29 −strict JSON schema that guarantees valid, reproducible output. Second, a
22 +single-family houses and townhouses listed for sale in Quebec on 3~July~2026,
23 +pooling the brokerage-listed MLS inventory from Realtor.ca with the
24 +for-sale-by-owner inventory from DuProprio. On top of standard structural,
25 +locational, and amenity attributes, we build two machine-generated feature layers.
26 +First, a large language model (LLM) reads each free-text description and returns a
27 +fixed set of property specifications---state of repair, renovation of the kitchen
28 +and bathroom, flooring and countertop materials, heating system, view, and income
29 +potential---under a strict JSON schema with closed enumerations. Second, a
30 30 vision--language model inspects the façade photograph of each property and returns
31 −objective visual attributes: overall condition, curb appeal, renovation level,
32 −exterior materials, landscaping, setting, and a luxury indicator. Because both
33 −extractions are schema-constrained, deterministic, and blind to the listing price,
34 −their outputs behave as ordinary tabulated regressors rather than as noisy text.
31 +visual attributes: overall condition, curb appeal, renovation level, exterior
32 +materials, landscaping, setting, and a luxury indicator. Because both extractions
33 +are schema-constrained, their outputs enter the hedonic regression as ordinary
34 +tabulated regressors with interpretable coefficients rather than as latent scores.
35 35
36 36 Our contribution is threefold. First, we show that AI-based feature engineering
37 −delivers economically meaningful gains in explanatory power over an already rich
38 −hedonic specification: the two machine-generated layers raise in-sample $R^2$ from
39 −0.717 to 0.755 (nearly four points) in our preferred specification, and raise the
40 −out-of-sample $R^2$ of every estimator we consider, with the façade-vision layer
41 −alone contributing the largest single increment of any feature block (2.7 points for
42 −gradient boosting, 5.8 points for OLS). Second, we show that the extracted
37 +delivers gains in explanatory power over an already rich hedonic specification. In
38 +our preferred specification---a log-linear model with 195 municipality fixed
39 +effects, property-type fixed effects, and fifteen keyword amenities, room-level and
40 +listing-quality controls---the two machine-generated layers raise in-sample $R^2$
41 +from 0.717 to 0.755, and they raise the out-of-sample $R^2$ of every estimator we
42 +consider, from ordinary least squares to gradient boosting and an embedding-based
43 +neural network. Of the two layers, the façade-vision layer is by far the more
44 +informative: it adds 3.0 points of in-sample $R^2$ against 0.8 for the text layer,
45 +and 5.8 (2.7) points of out-of-sample $R^2$ for OLS (gradient boosting), more than
46 +the gain from replacing OLS by a random forest. Second, we show that the extracted
43 47 attributes carry interpretable implicit prices---a one-point increase in visual curb
44 48 appeal is associated with a 2.3\% higher asking price, a visible luxury cue with
45 −5.2\%, brick or stone façades with premia of 3--4\% relative to vinyl siding, and a
46 −renovated kitchen (read from the text) with 2.6\%---so the approach augments rather
47 −than replaces the classical hedonic regression. Third, we document a complete,
48 −reproducible pipeline from raw scraped listings to an analysis-ready dataset,
49 −including rules-based outlier cleaning, unit harmonization that reconstructs living
50 −area from room-level dimensions, and the schema-constrained extraction protocol,
51 −with a total AI cost of about \$60 for the full sample---roughly a third of a cent
52 −per listing.
49 +5.2\%, brick or stone façades with premia of 3--4\% relative to the omitted
50 +materials, and a renovated kitchen (read from the text) with 2.6\%---and that these
51 +associations survive municipality-clustered inference, a cluster bootstrap, finer
52 +postal-code (RTA) fixed effects, and a control for the language of the description.
53 +Third, we document a complete and reproducible pipeline from raw scraped listings to
54 +an analysis-ready dataset, including rules-based outlier cleaning, unit harmonization
55 +that reconstructs living area from room-level dimensions, and the
56 +schema-constrained extraction protocol, with a total AI cost of about \$60 for the
57 +full sample---roughly a third of a cent per listing.
53 58
54 −Our main findings can be summarized as follows. A log--linear specification with 195
55 −municipality fixed effects, property-type fixed effects, and
56 −municipality-clustered standard errors explains 75.5\% of the variation in log
57 −asking prices when living area is observed, and 72.5\% on the full sample when it is
58 −not. The estimated implicit prices are economically sensible: the living-area
59 −elasticity is 0.36, each additional bathroom is associated with a 7.6\% premium, and
60 −the age profile traces the familiar depreciation-then-heritage pattern. The
61 −municipality effects span a wide range---from roughly $+231$\% in Westmount to about
62 −$-30$\% in remote municipalities---and order the province's markets as expected.
63 −Benchmarking a ladder of estimators on the identical feature set, an ensemble blend
64 −of a robust embedding-based neural network and gradient boosting attains an
65 −out-of-sample $R^2$ of 0.779 with a median absolute prediction error of 14.0\%. We
66 −interpret all estimates as \emph{predictive} associations rather than causal
67 −effects, since listing content is chosen by sellers and correlates with unobserved
68 −determinants of value, and we discuss the asking-price nature of the dependent
69 −variable at length in Section~\ref{sec:robustness}.
59 +The revision reported here (version 2.1) also confronts three threats that a
60 +skeptical reader should raise against any ``AI features'' exercise, and we
61 +believe the paper is more useful for addressing them explicitly. The first is
62 +\emph{information leakage}. Every DuProprio description in our data embeds a
63 +structured block that includes the asking price itself (``Asking Price \$599,000''),
64 +so the language model read the dependent variable for 19.7\% of the sample. The
65 +schema contains no price field, but the text-layer gain is five times larger on
66 +DuProprio than on Realtor.ca, whose descriptions never contain the asking price,
67 +and we therefore report all gains separately by platform and treat the Realtor.ca
68 +estimates as the clean benchmark. The vision model never sees the price. The second
69 +is \emph{language}. Forty-three percent of the descriptions are in English, and
70 +English-language listings are priced about 7\% higher than French ones conditional
71 +on all other attributes and on location; the AI implicit prices are, however,
72 +essentially unchanged when language is controlled for, and the gains from both
73 +layers are present within the French and within the English subsamples. The third
74 +is \emph{spatial dependence in validation}. Random cross-validation folds let a
75 +model learn from a neighbour of each held-out house; when folds are formed by
76 +forward sortation area instead, all estimators lose three to five points of
77 +out-of-sample $R^2$, but the increments from the text and vision layers are
78 +preserved.
70 79
71 −Beyond the specific Quebec application, the exercise speaks to a general measurement
72 −problem in housing economics. Text- and image-derived quality measures have until
73 −now required bespoke supervised models trained on labeled data
74 −\citep{nowak2017textual,shen2021information,johnson2020curb}. General-purpose
75 −foundation models with schema-constrained decoding lower the fixed cost of that
76 −measurement step to nearly zero, produce attributes that are human-readable rather
77 −than latent, and can be audited field by field. Our results suggest that this
78 −technology moves the effective characteristics frontier of hedonic analysis by
79 −enough to matter for appraisal, automated valuation, and assessment applications.
80 +Our main findings can be summarized as follows. The preferred specification explains
81 +75.5\% of the variation in log asking prices when living area is observed and 72.5\%
82 +on the full sample when it is not. The living-area elasticity is 0.36, each
83 +additional bathroom is associated with a 7.6\% premium, and the age profile traces
84 +the familiar depreciation-then-heritage pattern. The municipality effects range from
85 +about $+231$\% in Westmount to about $-30$\% in remote municipalities relative to the
86 +pooled group of small municipalities, and order the province's markets as expected.
87 +On the identical feature set, an equally weighted blend of a robust embedding-based
88 +neural network and gradient boosting attains an out-of-sample $R^2$ of 0.779 with a
89 +median absolute percentage error (MdAPE) of 14.0\% under random folds. We interpret
90 +all coefficients as \emph{predictive associations} rather than causal effects: listing
91 +content is chosen by sellers and their agents and correlates with unobserved
92 +determinants of value, and the dependent variable is an asking price, two issues we
93 +discuss at length in Section~\ref{sec:robustness}.
94 +
95 +Beyond the Quebec application, the exercise speaks to a measurement problem in
96 +housing economics. Text- and image-derived quality measures have until now required
97 +bespoke supervised models trained on labelled data
98 +\citep{nowak2017textual,shen2021information,johnson2020curb,lindenthal2021architecture}.
99 +General-purpose foundation models with schema-constrained decoding lower the fixed
100 +cost of that measurement step to nearly zero, produce attributes that are
101 +human-readable rather than latent, and can be audited field by field. Our results
102 +suggest that this technology shifts the effective characteristics frontier of hedonic
103 +analysis by enough to matter for appraisal, automated valuation, and assessment
104 +applications---provided that the text fed to the model is screened for the outcome
105 +it is meant to predict.
80 106
81 107 The remainder of the paper is organized as follows. Section~\ref{sec:literature}
82 −reviews the related literature. Section~\ref{sec:data} describes the data sources
83 −and cleaning. Section~\ref{sec:methodology} presents the feature-engineering
84 −pipeline and the econometric specification. Section~\ref{sec:results} reports the
85 −main results. Section~\ref{sec:benchmark} maps the predictive frontier across
86 −estimators. Section~\ref{sec:robustness} discusses robustness and limitations.
108 +reviews the related literature. Section~\ref{sec:data} describes the data, the
109 +cleaning rules, and the content of the descriptions. Section~\ref{sec:methodology}
110 +presents the feature-engineering pipeline and the econometric specification.
111 +Section~\ref{sec:results} reports the main results. Section~\ref{sec:benchmark}
112 +maps the predictive frontier across estimators. Section~\ref{sec:robustness}
113 +reports the robustness analyses and discusses limitations.
87 114 Section~\ref{sec:conclusion} concludes.
modified paper/sections/literature.tex +77 −44
@@ -14,53 +14,84 @@ survey the characteristics most commonly included in hedonic house-price models
14 14 document substantial heterogeneity in specification, \citet{malpezzi2003hedonic}
15 15 provides a critical review of functional-form and identification choices, and
16 16 \citet{hill2013hedonic} surveys the use of hedonic methods for house-price
17 −measurement. A recurring theme in this literature is omitted-variable bias from
18 −unmeasured quality: the state of repair, the standard of finishes, and the visual
19 −quality of a dwelling are priced by the market but rarely recorded in the structured
20 −fields available to the econometrician. Our contribution speaks directly to this
21 −measurement problem. Our log--linear specification, with logged continuous attributes
22 −and a dependent variable in logs, follows the mainstream of this literature.
17 +measurement. A recurring theme is omitted-variable bias from unmeasured quality:
18 +the state of repair, the standard of finishes, and the visual quality of a dwelling
19 +are priced by the market but rarely recorded in the structured fields available to
20 +the econometrician. \citet{nowak2020quality} show that this omission is not
21 +innocuous even for aggregate statistics, since quality-adjusted repeat-sales indexes
22 +built with information from listing remarks differ materially from conventional
23 +ones. Our contribution speaks directly to this measurement problem. Our log-linear
24 +specification, with logged continuous attributes and a dependent variable in logs,
25 +follows the mainstream of this literature, and we read indicator coefficients with
26 +the \citet{halvorsen1980interpretation} transformation; the small-sample refinement
27 +of \citet{kennedy1981estimation} is immaterial at our precision.
23 28
24 −The second strand concerns \textbf{spatial structure, functional form, and
25 −prediction}. \citet{can1992spatial} and \citet{anselin1988spatial} emphasize that
26 −unobserved locational amenities induce spatial dependence that, if ignored, biases
27 −coefficient estimates; we absorb the first-order component of this problem through a
28 −rich set of municipality fixed effects and cluster inference at the same level.
29 −On the prediction side, \citet{bin2004prediction} compares parametric and
30 −semi-parametric hedonic predictors, \citet{mullainathan2017machine} frame
31 −machine-learning methods as estimators of the conditional expectation
32 −$\mathbb{E}[P\mid\mathbf{z}]$ well suited to prediction tasks, and
33 −\citet{kok2017bigdata} document the rise of automated valuation models in industry.
34 −This literature generally finds that flexible learners beat linear hedonics on
35 −accuracy at some cost in interpretability; our estimator ladder
36 −(Section~\ref{sec:benchmark}) reproduces that pattern on a common feature set, and
37 −our focus is on how much of the gap is closed not by a better \emph{estimator} but by
38 −better \emph{measurement}.
29 +The second strand concerns \textbf{spatial structure, inference, and prediction}.
30 +\citet{can1992spatial} and \citet{anselin1988spatial} emphasize that unobserved
31 +locational amenities induce spatial dependence that, if ignored, biases coefficient
32 +estimates and understates uncertainty; we absorb the first-order component of this
33 +problem through municipality fixed effects, re-estimate with finer forward
34 +sortation area (RTA) effects, and cluster inference at the level of the fixed
35 +effects \citep{cameron2015cluster,abadie2023when}, complementing the analytical
36 +standard errors with a cluster bootstrap because our clusters are of very unequal
37 +size \citep{mackinnon2017wild}. The same dependence bears on validation.
38 +\citet{bourassa2010predicting} show that spatial structure that is not exploited by
39 +the model surfaces in prediction errors, \citet{pace2020examining} find that tree
40 +ensembles recover information left in the residuals of hedonic and spatial models,
41 +and \citet{roberts2017cross} document that random cross-validation is optimistic
42 +whenever observations are spatially clustered, recommending blocked (spatially
43 +grouped) folds instead. We report both. On the prediction side, \citet{bin2004prediction}
44 +compares parametric and semi-parametric hedonic predictors, \citet{mullainathan2017machine}
45 +frame machine-learning methods as estimators of the conditional expectation
46 +$\mathbb{E}[P\mid\mathbf{z}]$ suited to prediction tasks, and \citet{kok2017bigdata}
47 +document the rise of automated valuation models (AVMs) in industry. This literature
48 +generally finds that flexible learners beat linear hedonics on accuracy at some cost
49 +in interpretability; our estimator ladder (Section~\ref{sec:benchmark}) reproduces
50 +that pattern on a common feature set, and our focus is on how much of the gap is
51 +closed not by a better \emph{estimator} but by better \emph{measurement}.
39 52
40 53 The third strand is the use of \textbf{unstructured listing content}---text and
41 54 images---in real-estate valuation. \citet{gentzkow2019text} survey text-as-data
42 −methods in economics. In real estate specifically, \citet{nowak2017textual} show that
43 −words in MLS remarks predict transaction prices and reduce hedonic pricing errors,
44 −and \citet{shen2021information} extract quality signals from property descriptions
45 −with supervised machine learning, finding that the text carries information about
46 −unobserved quality that is capitalized into prices. On the image side,
47 −\citet{ahmed2016house} combine visual and textual features for price estimation;
48 −\citet{poursaeed2018vision} show that learned measures of interior and exterior
49 −quality carry independent predictive power; \citet{glaeser2018vision} use street-level
50 −imagery of Boston houses to measure exterior quality and study its interaction with
51 −incentives; \citet{johnson2020curb} construct a curb-appeal index from Google Street
52 −View images and estimate its capitalization into sale prices; and
53 −\citet{lindenthal2021architecture} classify architectural styles from photographs and
54 −document style premia. Relative to this work, our approach differs in the
55 +methods in economics, and \citet{dell2025deep} and \citet{korinek2023generative}
56 +survey, respectively, deep learning and generative models as research tools for
57 +economists. In real estate specifically, \citet{nowak2017textual} show that words in
58 +MLS remarks predict transaction prices and reduce hedonic pricing errors;
59 +\citet{nowak2021dictionaries} show that the price-relevant vocabulary is not stable
60 +across markets or over time, a warning against transferring word lists; and
61 +\citet{shen2021information} extract quality signals from property descriptions with
62 +supervised machine learning, finding that the text carries information about
63 +unobserved quality that is capitalized into prices. \citet{zhang2024describe}
64 +benchmark text encoders for price prediction and find that textual and
65 +non-textual features are complementary, and a companion working paper on Quebec
66 +listings \citep{boucher2026decoding} documents that semantic embeddings of
67 +descriptions carry out-of-sample price information and that the language of the
68 +description is a first-order confounder in a bilingual market---a finding we
69 +build on here. On the image side, \citet{ahmed2016house} combine visual and
70 +textual features for price estimation; \citet{bessinger2016quantifying} and
71 +\citet{poursaeed2018vision} show that learned measures of exterior and interior
72 +quality carry independent predictive power; \citet{naik2017computer} and
73 +\citet{glaeser2018vision} use street-level imagery to measure the physical
74 +appearance of neighbourhoods and houses and study how appearance relates to
75 +incentives and change; \citet{law2019take} and \citet{kang2021understanding}
76 +combine street-view and satellite imagery with structured attributes to predict
77 +prices and appreciation; \citet{zhang2018greenery} price street-visible greenery
78 +from street-view images; \citet{johnson2020curb} construct a curb-appeal index from
79 +Google Street View images and estimate its capitalization into sale prices; and
80 +\citet{lindenthal2021architecture} classify architectural styles from photographs
81 +and document style premia. Relative to this work, our approach differs in the
55 82 \emph{technology of measurement}: rather than training bespoke supervised models on
56 −labeled data---which requires labels, task-specific engineering, and yields opaque
57 −scores---we use general-purpose large language and vision--language models with
58 −schema-constrained decoding to produce a fixed set of interpretable, human-readable
59 −attributes (e.g., ``renovated kitchen,'' ``brick façade,'' a 1--10 curb-appeal score)
60 −at a marginal cost of a fraction of a cent per listing, and we embed those attributes
61 −in a transparent hedonic regression whose coefficients can be read as implicit
62 −prices. The models never observe the price, so the extracted attributes cannot
63 −mechanically encode the outcome.
83 +labelled data---which requires labels and task-specific engineering, and yields
84 +opaque scores---we use general-purpose large language and vision--language models
85 +with schema-constrained decoding to produce a fixed set of interpretable,
86 +human-readable attributes (``renovated kitchen,'' ``brick façade,'' a 1--10
87 +curb-appeal score) at a marginal cost of a fraction of a cent per listing, and we
88 +embed those attributes in a transparent hedonic regression whose coefficients can be
89 +read as implicit prices. The reliability of such zero-shot annotation is itself an
90 +active research question; \citet{gilardi2023chatgpt} find that general-purpose LLMs
91 +match or exceed crowd workers on standard text-annotation tasks, but the extraction
92 +error of any particular field must be assessed, which we do by confronting the
93 +machine-generated attributes with independent sources
94 +(Section~\ref{subsec:validation}).
64 95
65 96 The fourth strand is the \textbf{microstructure of listing markets}, which frames two
66 97 institutional features of our data. Our sample pools a brokered MLS platform
@@ -70,5 +101,7 @@ composition, marketing, and outcomes, motivating our platform indicator and
70 101 platform-specific robustness checks. And because we observe asking rather than
71 102 transaction prices, the interpretation of our dependent variable rests on the
72 103 list-price literature: the asking price is a strategic choice that anchors search and
73 −bargaining \citep{horowitz1992list,knight2002listing,han2015microstructure}. We
74 −return to this limitation in Section~\ref{sec:robustness}.
104 +bargaining \citep{horowitz1992list,knight2002listing,han2015microstructure}, and
105 +\citet{han2016asking} show that it acts simultaneously as a commitment device and
106 +as a signal in a directed-search environment. We return to this limitation in
107 +Section~\ref{subsec:limits}.
modified paper/sections/methodology.tex +136 −65
@@ -14,12 +14,12 @@ listings into an analysis-ready design matrix (Sections~\ref{subsec:structural}-
14 14 Following \citet{rosen1974hedonic}, a house is a bundle of $K$ characteristics
15 15 $\mathbf{z}=(z_1,\dots,z_K)$, and the market clears through a hedonic price function
16 16 $P(\mathbf{z})$ that equates the marginal bid of buyers to the marginal offer of
17 −sellers. A buyer with income $y$ and preferences $U(\mathbf{x},\mathbf{z})$ over a
18 −numéraire good $\mathbf{x}$ and housing attributes chooses $\mathbf{z}$ to solve
17 +sellers. A buyer with income $y$ and preferences $U(x,\mathbf{z})$ over a
18 +numéraire good $x$ and housing attributes chooses $\mathbf{z}$ to solve
19 19 %
20 20 \begin{equation}
21 − \max_{\mathbf{x},\,\mathbf{z}}\; U(\mathbf{x},\mathbf{z})
22 − \quad\text{s.t.}\quad y = \mathbf{x} + P(\mathbf{z}),
21 + \max_{x,\,\mathbf{z}}\; U(x,\mathbf{z})
22 + \quad\text{s.t.}\quad y = x + P(\mathbf{z}),
23 23 \label{eq:consumer}
24 24 \end{equation}
25 25 %
@@ -40,7 +40,12 @@ $P(\mathbf{z})$---and hence its gradient---from observed prices and characterist
40 40 The linear model of Section~\ref{subsec:econometrics} imposes a parametric form on
41 41 $P(\cdot)$ and reads the implicit prices off its coefficients; the machine-learning
42 42 estimators of Section~\ref{sec:benchmark} approximate $P(\cdot)$ nonparametrically,
43 −trading the closed-form gradient for predictive flexibility.
43 +trading the closed-form gradient for predictive flexibility. Two caveats apply
44 +throughout. The observed price is an asking price, so $P(\cdot)$ is the hedonic
45 +function of sellers' offers rather than of transactions; and the machine-generated
46 +attributes are measured with error and chosen by the seller, so their coefficients
47 +are predictive associations rather than structural marginal prices
48 +(Section~\ref{subsec:limits}).
44 49
45 50 \subsection{Structural and Locational Attributes}
46 51 \label{subsec:structural}
@@ -60,24 +65,33 @@ median, and each imputed field is accompanied by a ``\texttt{has\_}'' indicator
60 65 that the regression can distinguish observed from imputed values.
61 66
62 67 Location is captured by \textbf{municipality fixed effects}. City names are
63 −normalized (Realtor.ca appends neighborhood qualifiers in parentheses, which we
68 +normalized (Realtor.ca appends neighbourhood qualifiers in parentheses, which we
64 69 strip), and municipalities with fewer than 20 listings are pooled into a residual
65 −category, leaving 195 city effects. Property-type fixed effects and a source
66 −indicator complete the locational and platform controls.
70 +category, leaving 195 city effects; the pooled group of small municipalities is the
71 +omitted (reference) category, so every reported municipality effect is a premium or
72 +discount relative to a listing in a small municipality. In robustness checks we
73 +replace the municipality effects by \textbf{forward sortation area (RTA) effects},
74 +the first three characters of the postal code, which are available for all but one
75 +listing: 419 RTAs appear in the sample, of which 250 have at least 20 listings and
76 +receive their own effect. Property-type fixed effects and a source indicator complete
77 +the locational and platform controls.
67 78
68 79 \subsection{Rule-Based Amenities and Room Detail}
69 80 \label{subsec:amenities}
70 81
71 −Seventeen amenity indicators are detected by bilingual (French/English) keyword
82 +Fifteen amenity indicators are detected by bilingual (French/English) keyword
72 83 matching over the concatenated feature list, description, and title: swimming pool,
73 84 waterfront, garage, fireplace, central air conditioning or heat pump, finished
74 −basement, cul-de-sac, proximity to a golf course, ski hill, park, school, highway,
75 −and public transit, among others. From the Realtor.ca room block we additionally
76 −construct room-detail indicators---presence of a primary bedroom, a basement, an
77 −office, a family room, a workshop, a wine cellar, a sunroom, a mezzanine, a library,
78 −and a laundry room---together with the room and storey counts. Two listing-quality
79 −signals, the number of photographs (capped at~60) and the presence of a virtual
80 −tour, proxy for the marketing tier of the listing.
85 +basement, proximity to water, cul-de-sac, proximity to a golf course, ski hill,
86 +park, school, highway, and public transit, and the absence of rear neighbours. From
87 +the Realtor.ca room block we additionally construct room-detail indicators---presence
88 +of a primary bedroom, a basement, an office, a family room, a workshop, a wine
89 +cellar, a sunroom, a mezzanine, a library, and a laundry room---together with the
90 +room and storey counts. Two listing-quality signals, the number of photographs
91 +(capped at~60) and the presence of a virtual tour, proxy for the marketing tier of
92 +the listing. Finally, following the evidence of Section~\ref{subsec:text_content},
93 +two language indicators (English, mixed; French omitted) and the description length
94 +are available as controls.
81 95
82 96 \subsection{Language-Model Specifications from Text}
83 97 \label{subsec:llm}
@@ -87,43 +101,59 @@ extraction: whether the home is turnkey or a fixer-upper, whether the kitchen or
87 101 bathroom has been renovated, the flooring and countertop materials, the heating
88 102 system, the presence of a water view, and income potential. We extract these as a
89 103 fixed set of structured specifications using a large language model
90 −(\texttt{gpt-4o-mini}) with \emph{schema-constrained decoding}: the request specifies
91 −a strict JSON schema with closed enumerations and required fields, so the API
92 −validates the output at generation time and the model returns exactly the expected
93 −fields in the expected value domains. Decoding temperature is set to zero for
94 −determinism, and the system instruction constrains the model to report only what the
95 −text supports, coding absent information as \texttt{false}/\texttt{unknown}/empty.
96 −Each call consumes roughly 600 input and 110 output tokens; the extraction is run in
97 −parallel, is resumable through an on-disk cache keyed by the listing identifier, and
98 −retries transient errors with exponential backoff, for a total cost of about \$3
99 −across the sample.
104 +(\texttt{gpt-4o-mini}, OpenAI, called through the Chat Completions API in July 2026)
105 +with \emph{schema-constrained decoding}: the request specifies a strict JSON schema
106 +with closed enumerations and required fields, so the API validates the output at
107 +generation time and the model returns exactly the expected fields in the expected
108 +value domains (Table~\ref{tab:app_features}). Decoding temperature is set to zero,
109 +and the system instruction, written in French, constrains the model to rely only on
110 +the text and to code absent information as \texttt{false}, \texttt{unknown}, or an
111 +empty list. Each call consumes roughly 600 input and 110 output tokens; the
112 +extraction is run in parallel, is resumable through an on-disk cache keyed by the
113 +listing identifier, and retries transient errors with exponential backoff, for a
114 +total cost of about \$3 across the sample. Descriptions are truncated at 4{,}000
115 +characters, which affects only the longest DuProprio property sheets.
100 116
101 117 \subsection{Vision Attributes from the Façade Photograph}
102 118 \label{subsec:vision}
103 119
104 120 To capture the visual quality that neither structured fields nor text convey, we
105 121 analyze one photograph per listing---the façade---with a vision--language model
106 −(\texttt{claude-haiku-4-5}). For Realtor.ca the first image is the exterior shot; for
107 −DuProprio, whose images are captioned, we select the image whose caption is the
108 −address rather than a room. The model returns twelve objective attributes under a
109 −strict JSON schema: an overall condition score (1--10), a curb-appeal score (1--10),
110 −a renovation level, exterior materials, landscaping quality, visible garage, storeys,
111 −setting (urban/suburban/rural/waterfront/wooded), a luxury indicator, and photo
112 −quality. The system instruction directs the model to ground each score in concrete,
113 −reproducible criteria (surface condition, materials, landscaping) rather than
114 −subjective taste, and to flag non-exterior images. Images are passed by URL without
115 −local download; each call consumes roughly 2{,}470 input and 100 output tokens, for a
116 −total cost of about \$57 across the sample. As with the text layer, the pass is
117 −parallelized, resumable, and retried.
118 −
119 −\paragraph{Why schema-constrained decoding matters.}
122 +(\texttt{claude-haiku-4-5}, Anthropic, Messages API, July 2026). For Realtor.ca the
123 +first image is the exterior shot; for DuProprio, whose images are captioned, we
124 +select the first image whose caption is not a room name. The model returns twelve
125 +fields under a strict JSON schema: an exterior-shot flag, a visual building type, an
126 +overall condition score (1--10), a curb-appeal score (1--10), a renovation level,
127 +the list of exterior materials, landscaping quality, a visible-garage flag, the
128 +visual storey count, the setting (dense urban, suburban, rural, waterfront, wooded),
129 +a photo-quality score, and a luxury indicator. The system instruction directs the
130 +model to ground each score in concrete criteria (surface condition, materials,
131 +landscaping) rather than taste, and to flag non-exterior images. Images are passed
132 +by URL without local download; each call consumes roughly 2{,}470 input and 100
133 +output tokens, for a total cost of about \$57 across the sample. As with the text
134 +layer, the pass is parallelized, resumable, and retried. Unlike the text layer, the
135 +vision calls did not set a sampling temperature and therefore ran at the API default,
136 +so repeated calls could return slightly different scores for borderline images; we
137 +did not re-run the extraction to measure this test--retest variability (see
138 +Section~\ref{subsec:limits}).
139 +
140 +\paragraph{From schema fields to regressors.}
120 141 Imposing a typed schema with enumerations, required fields, and
121 −\texttt{additionalProperties:false} moves validation into the API layer. The models
122 −are forced to emit exactly the expected fields within the expected value domains, so
123 −their outputs behave as ordinary tabulated variables---reproducible and free of
124 −free-text post-processing. The outputs are then mapped to numeric regressors:
125 −booleans to $0/1$, the ordinal condition scale to $0$--$5$, materials to indicator
126 −variables, and so on, each with an availability flag.
142 +\texttt{additionalProperties:false} moves validation into the API layer, so the
143 +outputs are tabulated variables that require no free-text post-processing. They are
144 +then mapped to numeric regressors: booleans to $0/1$; the six-level text condition
145 +scale to an ordinal score $0$--$5$ (\textit{to renovate} $=0$, \ldots, \textit{new}
146 +$=5$, with \textit{unknown} placed at the ``good'' midpoint of~2); the five-level
147 +visual renovation scale to $0$--$4$ and landscaping to $0$--$3$; multi-valued
148 +fields (flooring, heating, exterior materials) and the setting to one indicator per
149 +category. The twelve vision fields thus generate 21 regressors (two scores, the
150 +photo-quality score, two ordinal scales, three indicators, five setting indicators,
151 +and eight material indicators), and the text schema 18 regressors. A listing whose
152 +photograph is not an exterior shot keeps the model's best-effort scores and is
153 +flagged by the exterior indicator. Model identifiers are API aliases that resolve to
154 +the provider's current snapshot; we record the alias and the extraction date, and
155 +the raw JSON outputs are archived so that every regressor can be regenerated from
156 +them without new API calls.
127 157
128 158 \subsection{Econometric Specification}
129 159 \label{subsec:econometrics}
@@ -149,26 +179,37 @@ disjoint blocks,
149 179 and estimate, by ordinary least squares,
150 180 %
151 181 \begin{equation}
152 − \ln P_i \;=\; \alpha
182 + \begin{aligned}
183 + \ln P_i \;=\;& \alpha
153 184 \;+\; \beta\,\ln\text{Area}_i
154 185 \;+\; \gamma\,\ln\text{Lot}_i
155 − \;+\; \mathbf{X}_i^{\!\top}\bm{\delta}
186 + \;+\; \mathbf{s}_i^{\!\top}\bm{\delta}_s + \mathbf{a}_i^{\!\top}\bm{\delta}_a
187 + + \mathbf{r}_i^{\!\top}\bm{\delta}_r + \mathbf{m}_i^{\!\top}\bm{\delta}_m \\
188 + &+ \underbrace{\mathbf{g}_i^{\!\top}\bm{\delta}_g}_{\text{LLM text}}
189 + + \underbrace{\mathbf{v}_i^{\!\top}\bm{\delta}_v}_{\text{vision}}
156 190 \;+\; \underbrace{\mu_{c(i)}}_{\text{city FE}}
157 191 \;+\; \underbrace{\tau_{t(i)}}_{\text{type FE}}
158 192 \;+\; \varepsilon_i ,
193 + \end{aligned}
159 194 \label{eq:hedonic}
160 195 \end{equation}
161 196 %
162 197 where $c(i)$ and $t(i)$ index the municipality and property type of listing~$i$, and
163 −$\mu_c$, $\tau_t$ are the corresponding fixed effects that sweep out
164 −location- and type-specific price levels. In matrix form
165 −$\mathbf{y}=\mathbf{Z}\bm{\theta}+\bm{\varepsilon}$ with
198 +$\mu_c$, $\tau_t$ are the corresponding fixed effects that sweep out location- and
199 +type-specific price levels. The source indicator is part of $\mathbf{m}_i$. In
200 +matrix form $\mathbf{y}=\mathbf{Z}\bm{\theta}+\bm{\varepsilon}$ with
166 201 $\widehat{\bm{\theta}}=(\mathbf{Z}^{\!\top}\mathbf{Z})^{-1}\mathbf{Z}^{\!\top}\mathbf{y}$.
202 +The contribution of a feature layer is measured by the change in $R^2$ when its
203 +block is added to the specification that contains all preceding blocks; the order
204 +is size and structural, amenities, rooms and listing signals, text, vision, so the
205 +text layer is credited only with what the keyword amenities do not already capture,
206 +and the vision layer only with what neither the text nor the structured fields
207 +capture.
167 208
168 209 \paragraph{Interpretation of the coefficients.}
169 210 Because the size variables enter in logs, $\beta$ and $\gamma$ are \emph{elasticities}:
170 211 $\partial \ln P/\partial \ln\text{Area}=\beta$. For an indicator regressor $d$ with
171 −coefficient $b$, the exact percentage price effect is not $b$ but
212 +coefficient $b$, the percentage price effect is not $b$ but
172 213 %
173 214 \begin{equation}
174 215 \%\Delta P \;=\; \big(e^{\,b}-1\big)\times 100,
@@ -176,10 +217,14 @@ coefficient $b$, the exact percentage price effect is not $b$ but
176 217 \end{equation}
177 218 %
178 219 the \citet{halvorsen1980interpretation} correction we apply throughout (for small
179 −$b$, $e^{b}-1\approx b$). Under Equation~\eqref{eq:hedonic} the implicit price of a
180 −continuous attribute $z_k$ from \eqref{eq:implicit} is $p_k=\delta_k\,P/z_k$, so the
181 −semi-log form lets the \emph{dollar} marginal price scale with the price level while
182 −keeping the \emph{percentage} effect constant.
220 +$b$, $e^{b}-1\approx b$; the \citealp{kennedy1981estimation} adjustment
221 +$e^{\,b-\frac12\widehat{\mathrm{Var}}(b)}-1$ moves none of our reported effects by
222 +more than 0.1 percentage point). For an ordinal score such as curb appeal, $b$ is the
223 +log-price difference associated with one step of the scale, holding the other
224 +attributes fixed. Under Equation~\eqref{eq:hedonic} the implicit price of a
225 +continuous attribute $z_k$ from \eqref{eq:implicit} is $p_k=\delta_k\,P/z_k$, so
226 +the semi-log form lets the \emph{dollar} marginal price scale with the price level
227 +while keeping the \emph{percentage} effect constant.
183 228
184 229 \paragraph{Two-part coverage design.}
185 230 Because living area is reported for only about 38\% of Realtor.ca listings, we
@@ -194,15 +239,16 @@ estimate two nested versions of \eqref{eq:hedonic} and combine their predictions
194 239 \label{eq:twopart}
195 240 \end{equation}
196 241 %
197 −Model~A includes $\ln\text{Area}$ and is fit on the measured-area subsample; Model~B
198 −drops it and is fit on the full cleaned sample, so every listing can be priced.
242 +Model~A includes $\ln\text{Area}$ and is fit on the 9{,}446 listings with a measured
243 +or reconstructed area; Model~B drops it and is fit on the full cleaned sample, so
244 +every listing can be priced.
199 245
200 246 \paragraph{Inference.}
201 247 House-price residuals are strongly heteroskedastic, and listings within a
202 248 municipality share unobserved local shocks that induce within-cluster error
203 249 correlation. Our preferred standard errors are therefore clustered at the
204 250 municipality level (195 clusters), the same level as the fixed effects
205 −\citep{cameron2015cluster}:
251 +\citep{cameron2015cluster,abadie2023when}:
206 252 %
207 253 \begin{equation}
208 254 \widehat{\mathrm{Var}}_{\text{CL}}(\widehat{\bm{\theta}})
@@ -215,12 +261,18 @@ municipality level (195 clusters), the same level as the fixed effects
215 261 \end{equation}
216 262 %
217 263 where $\mathbf{Z}_c$ and $\hat{\bm{\varepsilon}}_c$ stack the regressors and
218 −residuals of cluster $c$. As a complement we also compute
219 −heteroskedasticity-consistent (HC3) standard errors
220 −\citep{white1980heteroskedasticity,mackinnon1985some}, which inflate the
221 −contribution of high-leverage points; Section~\ref{subsec:robust} shows that the two
222 −choices agree on the significance of the property-level AI attributes and differ
223 −only for keyword amenities with a strong spatial component.
264 +residuals of cluster $c$. Cluster sizes are very unequal---Montreal alone contributes
265 +about one listing in twenty, and the pooled residual category of small municipalities,
266 +which is treated as a single cluster, holds about one listing in four (5{,}006 listings from 999 municipalities)---a configuration in which the asymptotic cluster-robust
267 +variance can be unreliable \citep{mackinnon2017wild}. We therefore also report a
268 +pairs cluster bootstrap that resamples municipalities with replacement
269 +($B=299$) and takes the 2.5th and 97.5th percentiles of the resampled coefficients
270 +as a confidence interval. As a complement we compute heteroskedasticity-consistent
271 +(HC3) standard errors \citep{white1980heteroskedasticity,mackinnon1985some};
272 +Section~\ref{subsec:robust} shows that the three approaches agree on the
273 +significance of the property-level AI attributes and differ mainly for keyword
274 +amenities with a strong spatial component. When RTA fixed effects replace
275 +municipality effects, standard errors are clustered by RTA.
224 276
225 277 \paragraph{Retransformation.}
226 278 Predicting the \emph{level} of price requires undoing the log. Because
@@ -239,3 +291,22 @@ generally Duan's nonparametric smearing estimate \citep{duan1983smearing}:
239 291 \underbrace{\frac1n\sum_{j=1}^{n} e^{\hat\varepsilon_j}}_{\text{smearing }\hat\phi}.
240 292 \label{eq:smearing}
241 293 \end{equation}
294 +%
295 +The out-of-sample accuracy metrics of Section~\ref{sec:benchmark} are computed on
296 +$\ln P$ (for $R^2$) and on the ratio of predicted to actual price (for the median
297 +absolute percentage error), neither of which depends on the retransformation
298 +constant.
299 +
300 +\paragraph{Out-of-sample validation.}
301 +All predictive comparisons use five-fold cross-validation with a common seed, so that
302 +every estimator and every feature set is evaluated on identical held-out folds. Under
303 +\emph{random} folds a held-out house typically has close neighbours in the training
304 +folds, which flatters any model that can exploit spatial proximity
305 +\citep{roberts2017cross}. We therefore repeat the exercise with \emph{spatially
306 +grouped} folds, assigning entire RTAs to folds so that no held-out listing shares
307 +its RTA with a training listing; because many small municipalities consist of a
308 +single RTA, their municipality indicator is then unobserved in training and the
309 +model prices them at the reference level. The spatial design is a deliberately harsh
310 +test of how the estimators and feature layers behave for a new area; the random
311 +design corresponds to the operational use of an AVM that prices a new listing in a
312 +market it already covers.
modified paper/sections/results.tex +89 −42
@@ -12,69 +12,83 @@ and vision-derived attributes). Model~A, which includes living area and is estim
12 12 on the 9{,}446 listings with a measured area, explains 75.5\% of the variation in log
13 13 prices ($R^2=0.755$, adjusted $0.748$). Model~B, the area-free specification estimated
14 14 on all 19{,}123 listings, explains 72.5\% ($R^2=0.725$, adjusted $0.721$). Adding the
15 −machine-generated feature layers raises Model~A's $R^2$ from 0.717 (structural,
16 −amenity, room, and listing attributes only) to 0.725 with the text layer and 0.755
17 −with the vision layer---a cumulative gain of nearly four points from the two
18 −AI-generated feature sets, over and above an already rich hedonic specification.
15 +machine-generated feature layers in sequence raises Model~A's $R^2$ from 0.717
16 +(structural, amenity, room, and listing attributes only) to 0.725 with the text layer
17 +and to 0.755 with the vision layer: 3.8 points in total, of which 0.8 come from the
18 +text and 3.0 from the façade photograph. The text increment is small because the
19 +fifteen keyword amenities already capture the most frequently mentioned features
20 +(garage, pool, fireplace, air conditioning), so the LLM is credited only with what
21 +keyword matching misses; Section~\ref{subsec:robust_leakage} shows that on
22 +Realtor.ca, where the text contains no price, the increments are 0.65 and 2.4
23 +points.
19 24
20 25 The estimated implicit prices are economically sensible and, with few exceptions,
21 26 statistically significant with standard errors clustered by municipality. In
22 27 Model~A, the living-area elasticity is 0.36 (a 10\% larger home commands roughly a
23 −3.6\% higher price) and the lot-area elasticity is small but precisely estimated.
24 −Each additional \textbf{bathroom} is associated with a 7.6\% premium and each parking
25 −space with about 1\%. \textbf{Central air conditioning} carries a 5.5\% premium and
26 −\textbf{public-transit} proximity 9.4\%, although the transit coefficient---like
27 −other keyword amenities with a strong spatial component---is estimated much less
28 −precisely once errors are allowed to correlate within municipalities.
29 −\textbf{Waterfront} access is associated with a 6.0--6.4\% premium, significant at
30 −conventional levels in the full-sample model. Age enters with a small negative linear
31 −term and a positive quadratic, tracing the familiar depreciation-then-heritage
32 −profile.
28 +3.6\% higher price) and the lot-area elasticity is small (0.027) but precisely
29 +estimated. Each additional \textbf{bathroom} is associated with a 7.6\% premium and
30 +each parking space with about 1\%. \textbf{Central air conditioning} carries a 5.5\%
31 +premium and \textbf{public-transit} proximity 9.4\%, although the transit
32 +coefficient---like other keyword amenities with a strong spatial component---is
33 +estimated much less precisely once errors are allowed to correlate within
34 +municipalities. \textbf{Waterfront} access is associated with a 6.0--6.4\% premium,
35 +significant at conventional levels in the full-sample model. Age enters with a
36 +negative linear term ($-0.4$\% per year) and a small positive quadratic
37 +($2.6\times10^{-5}$), so the price--age profile bottoms out at about 70 years and
38 +rises for older, heritage-type houses.
33 39
34 40 \input{tables/regression}
35 41
36 42 \subsection{Text- and Vision-Derived Attributes}
37 43 \label{subsec:ai_coeffs}
38 44
39 −The two AI feature layers contribute distinct, largely orthogonal signal.
45 +The two AI feature layers contribute distinct signal.
40 46
41 47 \paragraph{Text (LLM).} Beyond the keyword amenities, the structured specifications
42 48 carry independent information about finish and condition: a \textbf{renovated
43 49 kitchen} is associated with a 2.6\% premium, \textbf{hardwood flooring} with 2.4\%,
44 50 and the ordinal \textbf{condition} scale is positive and significant in both models
45 −(1.6\% per step). Listings the model reads as \textbf{income properties} sell about
46 −1.9\% below otherwise-comparable houses. The \textbf{new-construction} indicator,
47 −by contrast, is indistinguishable from zero once age, the visual renovation level,
48 −and the condition scales are controlled for---the premium a naive specification
49 −would attribute to the label ``new'' is fully absorbed by the measured quality
50 −attributes.
51 +(1.6\% per step). Listings the model reads as \textbf{income properties} are listed
52 +about 1.9\% below otherwise comparable houses. The \textbf{new-construction}
53 +indicator, by contrast, is indistinguishable from zero once age, the visual
54 +renovation level, and the condition scales are controlled for: the premium a naive
55 +specification would attribute to the label ``new'' is absorbed by the measured
56 +quality attributes.
51 57
52 58 \paragraph{Vision (façade).} The image-derived attributes are jointly significant and
53 −add the largest single increment to fit. Holding all structural, amenity, and text
59 +add the larger increment to fit. Holding all structural, amenity, and text
54 60 attributes fixed, a one-point increase in \textbf{curb appeal} (1--10) is associated
55 61 with a 2.3\% higher price, and a one-step increase in the visual \textbf{renovation
56 62 level} with 2.4\%. A visible \textbf{luxury} cue adds 5.2\%, a \textbf{visible garage}
57 63 4.7\%, and each step of \textbf{landscaping} quality 1.5\%. Exterior materials price
58 64 as expected: \textbf{brick} ($+3.4$\%) and \textbf{stone} ($+3.7$\%) command premia,
59 −while \textbf{vinyl} siding carries a 4.4\% discount. The visual \textbf{rural}
60 −setting is associated with an 11.5\% discount relative to the omitted category. That
61 −these visual measures remain significant after conditioning on the full text and
62 −structural set---and with inference clustered at the municipality level---confirms
63 −that the façade photograph carries price-relevant information available in no other
64 −field.
65 +while \textbf{vinyl} siding carries a 4.4\% discount; because a façade can combine
66 +several materials, each indicator is measured relative to façades without that
67 +material. The visual \textbf{rural} setting is associated with an 11.5\% discount
68 +relative to the other settings even within municipalities, which indicates that the
69 +model picks up within-municipality variation in density that the fixed effects
70 +cannot. The visual condition score, by contrast, is not significant in Model~A once
71 +curb appeal and the renovation level are included; the three scores are highly
72 +collinear (the model rates condition, curb appeal and renovation level from the same
73 +cues), and their individual coefficients should be read as a joint
74 +``visual-quality'' block rather than as three separate prices. That these visual
75 +measures remain significant after conditioning on the full text and structural set,
76 +with inference clustered at the municipality level, indicates that the façade
77 +photograph carries price-relevant information available in no other field.
65 78
66 79 \subsection{Validation of Machine-Generated Features}
67 80 \label{subsec:validation}
68 81
69 −Before relying on the AI-generated attributes, we check that they order prices in the
70 −expected direction. Figure~\ref{fig:validation} plots the median list price against the
71 −vision model's curb-appeal and condition scores among listings with a detected
72 −exterior shot. Both relationships are strongly monotone: median price rises steadily
73 −from the lowest to the highest visual scores, with no reliance on the regression to
74 −impose the pattern. Because these scores are produced by a model that never sees the
75 −price, the monotonicity is independent evidence that the vision layer extracts a real,
76 −price-relevant quality signal rather than noise. Table~\ref{tab:ai_summary} reports the
77 −prevalence of the main text- and vision-derived attributes.
82 +Before relying on the AI-generated attributes we assess them in two ways. First, we
83 +check that the vision scores order prices in the expected direction.
84 +Figure~\ref{fig:validation} plots the median list price against the vision model's
85 +curb-appeal and condition scores among listings with a detected exterior shot. Both
86 +relationships are monotone: median price rises from the lowest to the highest visual
87 +scores, with no reliance on the regression to impose the pattern. Because these
88 +scores are produced by a model that never sees the price, the monotonicity shows that
89 +the vision layer extracts a price-correlated quality signal rather than noise; it
90 +does not, of course, establish that the signal is \emph{quality} rather than, say,
91 +the marketing budget of the seller (Section~\ref{subsec:limits}).
78 92
79 93 \begin{figure}[htbp]
80 94 \centering
@@ -85,21 +99,54 @@ prevalence of the main text- and vision-derived attributes.
85 99 \label{fig:validation}
86 100 \end{figure}
87 101
102 +Second, because we do not have human labels, we measure extraction error indirectly
103 +by confronting the machine-generated attributes with independent sources
104 +(Table~\ref{tab:agreement}). Where the LLM and the bilingual keyword rule read the
105 +same text for the same binary feature, agreement is very high for concrete,
106 +unambiguous amenities---garage (agreement 98\%, Cohen's $\kappa=0.96$), fireplace
107 +($\kappa=0.91$), swimming pool ($\kappa=0.81$)---and lower for features whose
108 +definition is fuzzy or whose keywords are ambiguous: central air conditioning
109 +($\kappa=0.65$; the keyword rule also fires on ``air exchanger''), finished basement
110 +($\kappa=0.56$; the keyword rule fires on any mention of ``basement''), and
111 +waterfront ($\kappa=0.50$). For waterfront the LLM flags 22.5\% of listings against
112 +9.2\% for the rule, with a recall of 98\% but a precision of only 40\% relative to the
113 +rule, which suggests that the model treats water \emph{access} or \emph{views} as
114 +waterfront; the regression includes both variables, and the keyword waterfront is the
115 +one whose coefficient we report. Across modalities, the vision model's
116 +visible-garage flag agrees with the text-based garage indicator for 68\% of exterior
117 +shots ($\kappa=0.35$)---a garage need not be visible from the front---and its storey
118 +count matches the structured storey field exactly for 60\% of listings and within
119 +half a storey for 89\%. Two fields are clearly unreliable: the visual waterfront
120 +setting ($\kappa=0.39$ against the keyword) and the visual building type, which
121 +labels 12.7\% of façades as row houses when only 3.0\% of listings are townhouses
122 +(precision 18\%) and 14.5\% as ``chalets.'' We accordingly use the visual building
123 +type and setting only as controls and base no conclusion on them, and we read the
124 +reported coefficients of the vision block as prices of \emph{what the model sees}
125 +rather than of verified physical attributes.
126 +
127 +\input{tables/agreement}
128 +
88 129 \subsection{Location Fixed Effects}
89 130 \label{subsec:location}
90 131
91 132 The 195 municipality fixed effects absorb the dominant locational component of price.
92 −Relative to the reference city, the largest positive premia are estimated for
93 −\textbf{Westmount} ($\approx+231$\%), \textbf{Hampstead} ($+173$\%), and
94 −\textbf{Mont-Royal} ($+169$\%), followed by outlying municipalities at the low end
133 +Relative to the pooled group of small municipalities, the largest positive premia are
134 +estimated for \textbf{Westmount} ($\approx+231$\%), \textbf{Hampstead} ($+173$\%),
135 +and \textbf{Mont-Royal} ($+169$\%), followed by remote municipalities at the low end
95 136 carrying discounts of 23--30\%. Figure~\ref{fig:premia} plots the extreme effects. The
96 137 magnitude and ordering align with well-known patterns in the Quebec housing market and
97 −lend face validity to the specification.
138 +lend face validity to the specification; one apparent anomaly,
139 +Sainte-Anne-des-Plaines ($+143$\%), is an artefact of the two-part design: only two
140 +of that municipality's 33 listings report a living area and enter Model~A, one of
141 +them a \$5.5~million estate on a 100-acre lot, so the effect is identified from two
142 +observations. Municipality effects estimated on a single-date cross-section with
143 +sparse cells should not be read as permanent location premia.
98 144
99 145 \begin{figure}[htbp]
100 146 \centering
101 147 \includegraphics[width=.74\textwidth]{figures/fig_premia.pdf}
102 148 \caption{Estimated municipality fixed effects (Model~A), the twelve largest and eight
103 − smallest, expressed as percentage premia relative to the reference city.}
149 + smallest, expressed as percentage premia relative to the pooled group of
150 + municipalities with fewer than 20 listings.}
104 151 \label{fig:premia}
105 152 \end{figure}
modified paper/sections/robustness.tex +178 −36
@@ -3,10 +3,10 @@
3 3 \label{sec:robustness}
4 4 % ============================================================================
5 5
6 −\subsection{Robustness}
6 +\subsection{Inference and Subsample Stability}
7 7 \label{subsec:robust}
8 8
9 −Several features of the design guard against spurious results. The dual
9 +Several features of the design guard against spurious results. The two-part
10 10 specification (Section~\ref{subsec:econometrics}) ensures that estimates are not
11 11 driven by the non-random availability of living area: coefficients on attributes
12 12 common to both models retain their sign and rough magnitude whether or not area is
@@ -29,41 +29,155 @@ indicators), whereas keyword amenities with a strong spatial component lose
29 29 precision (public-transit proximity falls to the 5\% boundary in Model~A and is
30 30 insignificant in Model~B; central air conditioning is insignificant in Model~B; the
31 31 curb-appeal score, precisely estimated in Model~A, loses significance in the broader
32 −Model~B).
33 −Heteroskedasticity-robust (HC3) standard errors
32 +Model~B). Heteroskedasticity-robust (HC3) standard errors
34 33 \citep{white1980heteroskedasticity,mackinnon1985some} yield the same qualitative
35 −pattern with uniformly smaller intervals. This asymmetry is reassuring for our main
36 −claim: the AI-generated attributes capture within-market, property-level quality
37 −variation rather than repackaged location effects.
34 +pattern with uniformly smaller intervals. The last column of
35 +Table~\ref{tab:rob_ai_coefs} reports 95\% intervals from a pairs cluster bootstrap
36 +that resamples the 195 municipalities with replacement; they are wider than the
37 +analytical ones, as expected with clusters of very unequal size
38 +\citep{mackinnon2017wild}, and they exclude zero for curb appeal, the renovation
39 +level, the luxury cue, the visible garage, brick, stone, vinyl, the rural setting,
40 +the LLM condition scale, and the renovated kitchen; the intervals for landscaping and
41 +hardwood flooring include zero by a small margin. This asymmetry between
42 +property-level and location-level regressors supports our main claim: the
43 +AI-generated attributes capture within-market, property-level quality variation
44 +rather than repackaged location effects.
45 +
46 +\input{tables/rob_ai_coefs}
38 47
39 48 Table~\ref{tab:robustness} probes the stability of the predictive blend across
40 49 subsamples. Three patterns stand out. First, accuracy is comparable whether living
41 −area is observed (median error 13.5\%) or imputed (14.4\%), confirming that the
42 −area-free model prices the incomplete listings almost as well as the rich model.
43 −Second, the model fits the broker-listed Realtor.ca inventory better
44 −($R^2_{\text{OOS}}=0.80$) than the smaller, noisier for-sale-by-owner DuProprio
45 −segment (0.58), though median errors are identical at 14.0\%; the gap is consistent
46 −with the compositional and marketing differences between brokered and FSBO
47 −platforms documented by \citet{hendel2009mls}. Third, the median error is stable
48 −across price terciles (11--16\%), lowest in the mid-market and modestly higher in
49 −the tails, where idiosyncratic and luxury attributes are harder to capture. (Within
50 −a single tercile $R^2$ is uninformative because the price range is restricted; the
51 −median error is the comparable metric.)
50 +area is observed (median error 13.5\%) or imputed (14.4\%), so the area-free model
51 +prices the incomplete listings almost as well as the rich model. Second, the model
52 +fits the broker-listed Realtor.ca inventory much better ($R^2_{\text{OOS}}=0.80$)
53 +than the smaller for-sale-by-owner DuProprio segment (0.58), though median errors are
54 +identical at 14.0\%; the gap is consistent with the compositional and marketing
55 +differences between brokered and FSBO platforms documented by \citet{hendel2009mls},
56 +and with the absence of the room block for DuProprio listings. Third, the median
57 +error is stable across price terciles (11--16\%), lowest in the mid-market and
58 +modestly higher in the tails, where idiosyncratic and luxury attributes are harder
59 +to capture. (Within a single tercile $R^2$ is uninformative because the price range
60 +is restricted; the median error is the comparable metric.)
52 61
53 62 \input{tables/robustness}
54 63
64 +\subsection{Language of the Description}
65 +\label{subsec:robust_language}
66 +
67 +Section~\ref{subsec:text_content} showed that English-language listings are
68 +substantially more expensive than French ones. Column~(2) of
69 +Table~\ref{tab:rob_ai_coefs} adds the two language indicators and the description
70 +length to Model~A. The English indicator is large and precisely estimated: an
71 +English description is associated with a 7.2\% higher asking price conditional on
72 +every structural, amenity, text, and vision attribute and on the municipality
73 +(11.8\% in Model~B), and the mixed-language indicator with 4.6\%. We read this as a
74 +location and clientele effect within municipalities---English listings cluster in the
75 +most expensive parts of large municipalities such as Montreal, and brokers write in
76 +the language of the clientele they target---rather than as a price of the language
77 +itself. What matters for our purpose is that the AI-derived implicit prices are
78 +unchanged to the third decimal when language is controlled for: curb appeal moves
79 +from 0.023 to 0.022, the luxury cue stays at 0.051, brick at 0.032--0.033, vinyl
80 +at $-0.044$, and the LLM condition and kitchen coefficients at 0.017 and 0.025. The
81 +language indicators also leave the explanatory gain of the AI layers intact
82 +(Panel~A of Table~\ref{tab:rob_language_source}: 0.719 $\to$ 0.727 $\to$ 0.757 with
83 +language controls, against 0.717 $\to$ 0.725 $\to$ 0.755 without).
84 +
85 +The gains are present within each language group. Re-estimating the nested ablation
86 +of Model~A separately on French and English descriptions
87 +(Table~\ref{tab:rob_language_source}, Panel~A), the text layer adds 1.5 points of
88 +$R^2$ for French listings and 0.6 for English ones, and the vision layer 3.4 and
89 +2.2 points; out of sample, gradient boosting gains 1.5 points from the text in both
90 +groups and 3.1 and 2.6 from vision (Panel~C). The smaller text gain
91 +for English listings is consistent with a specification that already fits English
92 +listings better (base $R^2$ of 0.763 against 0.704), so that less is left for the
93 +text to explain, rather than with a weaker extraction in English. Adding the
94 +language indicators to the gradient-boosting feature set raises its out-of-sample
95 +$R^2$ from 0.770 to 0.774, a modest gain that confirms language is informative but
96 +largely redundant with location and the other attributes.
97 +
98 +\input{tables/rob_language_source}
99 +
100 +\subsection{Information Leakage Through the Text}
101 +\label{subsec:robust_leakage}
102 +
103 +Because every DuProprio description contains the asking price, the clean test of
104 +the text layer is the Realtor.ca subsample. The contrast in Panel~A of
105 +Table~\ref{tab:rob_language_source} is stark. On Realtor.ca listings the text layer
106 +adds 0.6 points of in-sample $R^2$ to Model~A and the vision layer 2.4 points; on
107 +DuProprio listings the increments are 3.3 and 4.1 points. Out of sample
108 +(Panel~C), gradient boosting gains 1.2 points from the text on Realtor.ca and 3.1
109 +on DuProprio. Three remarks temper the comparison. First, the DuProprio base is much
110 +weaker (in-sample $R^2$ of 0.564 against 0.779) because the room block is missing,
111 +so every additional block has more left to explain there; the vision layer, which
112 +never sees any text, also gains more on DuProprio (4.1 against 2.4 points), and the
113 +ratio of the DuProprio to the Realtor.ca gain is about 1.7 for vision against 5 for
114 +text. Second, the DuProprio ``description'' also embeds structured fields (cladding,
115 +heating, inclusions) that the LLM can read verbatim and that are genuinely
116 +informative, so part of the excess text gain is legitimate. Third, the asking price
117 +itself cannot be reproduced by any field of the schema; what the price can do is tilt
118 +the model's reading of ambiguous fields (condition, turnkey, luxury-type highlights)
119 +towards the expensive or the cheap end. We cannot separate these channels without
120 +re-running the extraction on descriptions purged of the price, which we recommend as
121 +the first step of any replication and which the archived raw text makes possible
122 +at a cost of about \$1. Until then, we regard the Realtor.ca increments---0.6 points
123 +of in-sample $R^2$ and 0.6 (OLS) to 1.2 (gradient boosting) points out of sample for
124 +the text layer---as the credible estimate of what the LLM specifications add beyond
125 +keyword amenities, and we note that none of the paper's conclusions about the
126 +vision layer is affected, since the vision model is exposed neither to the text nor
127 +to the price. Column~(4) of Table~\ref{tab:rob_ai_coefs} re-estimates Model~A on
128 +Realtor.ca listings alone with RTA fixed effects and language controls: curb appeal
129 +(0.021), the renovation level (0.041), the luxury cue (0.042), the visible garage
130 +(0.041), stone ($0.030$), vinyl ($-0.051$), landscaping (0.027), and the renovated
131 +kitchen (0.030) retain their magnitude and significance, whereas brick, the rural
132 +setting, the LLM condition scale, and hardwood flooring keep their sign but lose
133 +significance in this smaller sample of 5{,}781 listings.
134 +
135 +\subsection{Finer Location Controls and Spatial Validation}
136 +\label{subsec:robust_spatial}
137 +
138 +Municipalities are coarse spatial units: Montreal, Quebec City, Laval, and Gatineau
139 +each contain neighbourhoods whose price levels differ by a factor of two or more.
140 +Column~(3) of Table~\ref{tab:rob_ai_coefs} replaces the 195 municipality effects by
141 +250 RTA effects. In-sample fit improves (Model~A $R^2$ from 0.755 to 0.764,
142 +Model~B from 0.725 to 0.726), and the AI coefficients are stable: curb appeal 0.016,
143 +renovation level 0.029, luxury 0.045, garage 0.037, brick 0.031, stone 0.037, vinyl
144 +$-0.047$, rural $-0.122$, LLM condition 0.015, renovated kitchen 0.019, hardwood
145 +0.016, all significant at the 5\% level with standard errors clustered by RTA. The
146 +in-sample increments from the two AI layers under RTA effects are 0.7 and 3.2
147 +points (Table~\ref{tab:rob_language_source}, last row of Panel~A). Finer location
148 +controls therefore do not absorb the AI-measured quality, which is what one expects
149 +if the layers measure property-level rather than neighbourhood-level attributes.
150 +
151 +Random cross-validation folds let every held-out listing borrow strength from
152 +neighbours in the training folds. Table~\ref{tab:rob_spatial} repeats the nested
153 +ablation with folds formed by RTA, so that an entire postal area is predicted from a
154 +model that has never seen it. All levels fall, as they must: the full
155 +text\,$+$\,vision OLS drops from 0.686 to 0.638 and gradient boosting from 0.770
156 +to 0.734, with median absolute errors rising by about 1.5 percentage points. The
157 +increments from the AI layers, however, are preserved almost exactly---the text layer
158 +adds 0.7 (OLS) and 1.3 (gradient boosting) points under spatial folds against 0.9
159 +and 1.4 under random folds, and the vision layer 7.0 and 3.1 points against 5.8 and
160 +2.7. If anything, the vision layer is \emph{more} valuable when location is poorly
161 +measured, which is consistent with the façade carrying information about the
162 +neighbourhood (density, setting, housing vintage) as well as about the house. We
163 +regard the random-fold numbers as the relevant ones for an AVM operating within its
164 +coverage area, and the spatial-fold numbers as the relevant ones for extrapolation to
165 +new areas.
166 +
167 +\input{tables/rob_spatial}
168 +
55 169 \subsection{Limitations}
56 170 \label{subsec:limits}
57 171
58 −Five limitations qualify the interpretation of our estimates.
172 +Seven limitations qualify the interpretation of our estimates.
59 173
60 174 \emph{Asking prices.} The dependent variable is the asking price of properties
61 175 listed for sale, not the transaction price. The list price is a strategic choice
62 176 that anchors buyer search and bargaining
63 −\citep{horowitz1992list,knight2002listing,han2015microstructure}: it is set with
64 −sale in mind and is highly correlated with the eventual transaction price, but
65 −list-to-sale ratios vary systematically with market conditions and overpricing is
66 −more common in thin segments. Two implications follow. First, our implicit prices
177 +\citep{horowitz1992list,knight2002listing,han2015microstructure,han2016asking}: it is
178 +set with sale in mind and is highly correlated with the eventual transaction price,
179 +but list-to-sale ratios vary systematically with market conditions and overpricing
180 +is more common in thin segments. Two implications follow. First, our implicit prices
67 181 measure how sellers and their agents \emph{price} attributes into the ask, which
68 182 converges to buyers' valuation only insofar as the market disciplines the ask; the
69 183 economically sensible magnitudes and the monotone validation of
@@ -72,28 +186,56 @@ attribute that shifts the list-to-sale ratio itself (e.g., attributes of hard-to
72 186 properties) will carry a coefficient that mixes valuation and listing strategy.
73 187 Replicating the analysis on registry transaction prices is the natural next step.
74 188
189 +\emph{Price in the text.} For the 19.7\% of listings from DuProprio, the language
190 +model read the asking price as part of the description. The text-layer gains on
191 +that platform are therefore an upper bound, and the Realtor.ca gains the credible
192 +estimate (Section~\ref{subsec:robust_leakage}). The vision layer is unaffected.
193 +
75 194 \emph{Non-causality and content endogeneity.} Descriptions and photographs are
76 195 marketing instruments chosen by sellers and agents. Richer descriptions, more
77 196 photographs, and higher visual quality correlate with higher-end listing agents and
78 197 more valuable properties \citep{nowak2017textual}. The coefficients are therefore
79 198 \emph{predictive} associations, not causal effects; they should not be read as the
80 199 price change from physically adding a given attribute. We control for two observable
81 −dimensions of marketing effort (photo count and virtual tours), but cannot rule out
82 −that residual marketing quality loads on the AI-derived attributes.
200 +dimensions of marketing effort (photo count and virtual tours) and for the photo
201 +quality score, but cannot rule out that residual marketing quality loads on the
202 +AI-derived attributes, and the three visual quality scores are collinear enough that
203 +only their joint contribution is well identified.
204 +
205 +\emph{Machine-generated features and their reproducibility.} LLM and vision outputs
206 +contain errors. Strict schemas and closed enumerations bound the \emph{form} of the
207 +output but not its accuracy, and the agreement analysis of
208 +Section~\ref{subsec:validation} shows that accuracy varies widely across fields:
209 +high for concrete amenities, low for waterfront, the visual setting, and the visual
210 +building type. We have no human-labelled validation set; a few hundred manually
211 +coded listings would allow field-level precision and recall against ground truth
212 +and are the obvious next step. Classical measurement error in the extracted scores
213 +biases their coefficients towards zero, which makes our implicit prices
214 +conservative, but errors correlated with price (through the leakage channel above,
215 +or because the model rates expensive-looking houses more generously on every
216 +dimension) need not. The text extraction ran at temperature zero, the vision
217 +extraction at the API default temperature, and both model identifiers are provider
218 +aliases rather than pinned snapshots; the raw outputs are archived, so the
219 +regressors are exactly reproducible from them, but a fresh extraction with a later
220 +model version could return different scores, and we have not measured test--retest
221 +variability. The vision layer judges each property from a single façade photograph
222 +and ignores interiors.
83 223
84 −\emph{Machine-generated features.} LLM and vision outputs can contain errors.
85 −Zero-temperature decoding, strict schemas, and closed enumerations bound this risk
86 −and make it reproducible, and classical measurement error in the extracted scores
87 −would bias their coefficients toward zero---making our estimates conservative---but
88 −we do not perform systematic human validation, and the visual layer judges each
89 −property from a single façade photograph, ignoring interiors.
224 +\emph{Language.} The LLM processes French and English descriptions with the same
225 +French-language instruction and schema. We find no evidence that the extraction is
226 +weaker in either language and the implicit prices are robust to language controls,
227 +but the English indicator itself carries a large conditional premium that we can
228 +interpret only as a proxy for unobserved location and clientele.
90 229
91 230 \emph{Selection.} Listed properties are not a random sample of the housing stock,
92 231 and listings with missing area or room detail are not missing at random (they tend
93 −to be less complete listings). The ``\texttt{has\_}'' indicators and the two-part
94 −design mitigate, but do not eliminate, the second concern.
232 +to be less complete listings, and all DuProprio listings lack the room block). The
233 +``\texttt{has\_}'' indicators and the two-part design mitigate, but do not eliminate,
234 +the second concern, and the markedly worse fit for DuProprio shows that the pooled
235 +model is not equally adequate on both platforms.
95 236
96 237 \emph{Cross-section.} The data are a single-date snapshot, so the model captures no
97 −market dynamics and the location premia are fixed as of 3~July~2026. Municipalities
98 −with fewer than 20 listings are pooled, reducing spatial resolution where data are
99 −sparse.
238 +market dynamics, no forward-in-time holdout is possible, and the location premia are
239 +fixed as of 3~July~2026. Municipalities with fewer than 20 listings are pooled,
240 +reducing spatial resolution where data are sparse, and some municipality effects in
241 +Model~A rest on very few observations (Section~\ref{subsec:location}).
modified paper/tables/ablation.tex +3 −1
@@ -21,7 +21,9 @@
21 21 with a common seed. ``\# feats'' counts numeric/indicator features before the city,
22 22 type, and source fixed effects (added in every specification). The blend averages
23 23 the robust neural network and gradient boosting and is reported where both were
24 − trained. GBM handles missing living area natively; OLS uses median imputation.
24 + trained. GBM handles missing living area natively; OLS uses within-type median imputation.
25 + The gain from a layer is the difference between consecutive rows; the room and listing
26 + block includes the Realtor.ca room detail and therefore also carries platform identity.
25 27 \end{tablenotes}
26 28 \end{threeparttable}
27 29 \end{table}
added paper/tables/agreement.tex +37 −0
@@ -0,0 +1,37 @@
1 +% Auto-generated by scripts/18_revision_language_leakage_spatial.py (2026-10-17)
2 +\begin{table}[htbp]
3 + \centering
4 + \begin{threeparttable}
5 + \caption{Agreement Between Machine-Generated Attributes and Independent Sources}
6 + \label{tab:agreement}
7 + \footnotesize
8 + \setlength{\tabcolsep}{3.5pt}
9 + \begin{tabular}{@{}lccccccc@{}}
10 + \toprule
11 + Attribute & $N$ & Share (AI) & Share (ref.) & Agreement & $\kappa$ & Precision & Recall \\
12 + \midrule
13 + \multicolumn{8}{@{}l}{\textit{Panel A: LLM text layer vs.\ bilingual keyword rule on the same text}} \\[2pt]
14 + Garage & 19{,}123 & 40.8\% & 41.0\% & 98.2\% & 0.96 & 98.0\% & 97.6\% \\
15 + Swimming pool & 19{,}123 & 17.9\% & 23.9\% & 93.5\% & 0.81 & 98.8\% & 73.9\% \\
16 + Waterfront & 19{,}123 & 22.5\% & 9.2\% & 86.2\% & 0.50 & 39.8\% & 97.7\% \\
17 + Fireplace & 19{,}123 & 21.9\% & 23.6\% & 96.7\% & 0.91 & 96.3\% & 89.4\% \\
18 + Central A/C or heat pump & 19{,}123 & 11.5\% & 18.9\% & 90.8\% & 0.65 & 92.5\% & 56.0\% \\
19 + Finished basement & 19{,}123 & 35.5\% & 31.9\% & 80.5\% & 0.56 & 67.5\% & 75.0\% \\
20 + \addlinespace
21 + \multicolumn{8}{@{}l}{\textit{Panel B: Façade vision layer vs.\ text or structured fields (exterior shots only)}} \\[2pt]
22 + Garage visible vs.\ garage (LLM) & 18{,}314 & 47.5\% & 41.0\% & 67.6\% & 0.35 & 59.0\% & 68.4\% \\
23 + Garage visible vs.\ garage (keyword) & 18{,}314 & 47.5\% & 41.1\% & 67.4\% & 0.34 & 59.0\% & 68.1\% \\
24 + Waterfront setting vs.\ waterfront (keyword) & 18{,}314 & 6.5\% & 9.1\% & 91.1\% & 0.39 & 51.7\% & 37.2\% \\
25 + Row house vs.\ townhouse (listing type) & 18{,}314 & 12.7\% & 3.0\% & 89.0\% & 0.26 & 18.5\% & 79.0\% \\
26 + \bottomrule
27 + \end{tabular}
28 + \begin{tablenotes}[flushleft]
29 + \footnotesize
30 + \item \textit{Notes:} ``Share (AI)'' is the prevalence of the attribute according to the AI layer, ``Share (ref.)''
31 + according to the reference source; ``Agreement'' is the share of listings on which the two coincide, $\kappa$ is
32 + Cohen's kappa, and precision and recall treat the reference as ground truth (it is not: keyword rules miss
33 + negations and synonyms, and a garage need not be visible on the façade photograph). Visual storey count agrees
34 + exactly with the structured storey field for 60.5\% of 18{,}065 listings and within half a storey for 88.7\%.
35 + \end{tablenotes}
36 + \end{threeparttable}
37 +\end{table}
modified paper/tables/models.tex +5 −3
@@ -29,11 +29,13 @@
29 29 common seed (identical folds across rows). $R^2_{\text{OOS}}$ is on
30 30 $\ln(\text{Price})$; ``Median AE'' is the median absolute percentage error in
31 31 levels (Equation~\ref{eq:cvmetrics}). Panel~A uses structural, amenity, room-detail,
32 − listing-signal, and LLM text-derived features; Panel~B adds the twelve façade
33 − vision attributes. City, type, and source enter as one-hot indicators for the
32 + listing-signal, and LLM text-derived features; Panel~B adds the façade
33 + vision layer (21 regressors derived from the twelve schema fields). City, type, and source enter as one-hot indicators for the
34 34 linear and tree models and as learned embeddings for the neural networks. The tree
35 35 and neural models handle missing living area natively; OLS uses within-type median
36 − imputation. The best model is the text\,+\,vision blend.
36 + imputation. The blend weights the two models equally (weight fixed a priori). The best
37 + model is the text\,+\,vision blend. Entries may differ by $\pm0.001$ from
38 + Tables~\ref{tab:ablation} and~\ref{tab:rob_spatial}, which come from separate runs.
37 39 \end{tablenotes}
38 40 \end{threeparttable}
39 41 \end{table}
added paper/tables/rob_ai_coefs.tex +62 −0
@@ -0,0 +1,62 @@
1 +% Auto-generated by scripts/18_revision_language_leakage_spatial.py (2026-10-17)
2 +\begin{table}[htbp]
3 + \centering
4 + \begin{threeparttable}
5 + \caption{Robustness of the AI-Derived Implicit Prices (Model~A)}
6 + \label{tab:rob_ai_coefs}
7 + \footnotesize
8 + \setlength{\tabcolsep}{4pt}
9 + \begin{tabular}{@{}lccccc@{}}
10 + \toprule
11 + & (1) & (2) & (3) & (4) & (5) \\
12 + & Baseline & $+$Language & RTA FE & Realtor.ca only & Cluster bootstrap \\
13 + & & & $+$Language & RTA FE $+$Lang. & 95\% CI \\
14 + \midrule
15 + Curb appeal (1--10) & 0.023$^{***}$ & 0.022$^{**}$ & 0.016$^{***}$ & 0.021$^{**}$ & [$0.001$, $0.035$] \\
16 + & (0.009) & (0.009) & (0.006) & (0.010) & \\[2pt]
17 + Renovation level (ord.) & 0.024$^{**}$ & 0.024$^{***}$ & 0.029$^{***}$ & 0.041$^{**}$ & [$0.010$, $0.046$] \\
18 + & (0.010) & (0.009) & (0.009) & (0.018) & \\[2pt]
19 + Luxury (visual) & 0.051$^{***}$ & 0.051$^{***}$ & 0.045$^{***}$ & 0.042$^{***}$ & [$0.019$, $0.066$] \\
20 + & (0.012) & (0.012) & (0.010) & (0.015) & \\[2pt]
21 + Garage visible & 0.046$^{***}$ & 0.046$^{***}$ & 0.037$^{***}$ & 0.041$^{***}$ & [$0.029$, $0.055$] \\
22 + & (0.006) & (0.006) & (0.006) & (0.009) & \\[2pt]
23 + Brick façade & 0.033$^{***}$ & 0.032$^{***}$ & 0.031$^{***}$ & 0.024 & [$0.001$, $0.044$] \\
24 + & (0.009) & (0.009) & (0.010) & (0.017) & \\[2pt]
25 + Stone façade & 0.036$^{***}$ & 0.037$^{***}$ & 0.037$^{***}$ & 0.030$^{***}$ & [$0.022$, $0.049$] \\
26 + & (0.008) & (0.008) & (0.009) & (0.010) & \\[2pt]
27 + Vinyl façade & $-$0.045$^{***}$ & $-$0.044$^{***}$ & $-$0.047$^{***}$ & $-$0.051$^{**}$ & [$-0.067$, $-0.007$] \\
28 + & (0.015) & (0.015) & (0.018) & (0.021) & \\[2pt]
29 + Rural setting & $-$0.122$^{***}$ & $-$0.120$^{***}$ & $-$0.122$^{***}$ & $-$0.119 & [$-0.176$, $-0.029$] \\
30 + & (0.032) & (0.033) & (0.042) & (0.087) & \\[2pt]
31 + Landscaping (ord.) & 0.015$^{**}$ & 0.015$^{**}$ & 0.013$^{**}$ & 0.027$^{***}$ & [$-0.000$, $0.025$] \\
32 + & (0.006) & (0.006) & (0.006) & (0.007) & \\[2pt]
33 + Condition (LLM, ord.) & 0.016$^{***}$ & 0.017$^{***}$ & 0.015$^{***}$ & 0.012 & [$0.002$, $0.024$] \\
34 + & (0.006) & (0.006) & (0.005) & (0.009) & \\[2pt]
35 + Reno.\ kitchen (LLM) & 0.026$^{***}$ & 0.025$^{***}$ & 0.019$^{**}$ & 0.030$^{**}$ & [$0.010$, $0.039$] \\
36 + & (0.007) & (0.007) & (0.009) & (0.012) & \\[2pt]
37 + Hardwood floor (LLM) & 0.024$^{**}$ & 0.026$^{***}$ & 0.016$^{**}$ & $-$0.001 & [$-0.003$, $0.036$] \\
38 + & (0.009) & (0.009) & (0.007) & (0.013) & \\[2pt]
39 + English description & & 0.070$^{***}$ & 0.090$^{***}$ & 0.082$^{**}$ & \\
40 + & & (0.011) & (0.034) & (0.042) & \\[2pt]
41 + Mixed-language description & & 0.045$^{***}$ & 0.067$^{**}$ & 0.181$^{***}$ & \\
42 + & & (0.011) & (0.030) & (0.042) & \\[2pt]
43 + \midrule
44 + Fixed effects & Municipality & Municipality & RTA & RTA & Municipality \\
45 + Observations & 9{,}446 & 9{,}446 & 9{,}446 & 5{,}781 & 9{,}446 \\
46 + $R^2$ & 0.755 & 0.757 & 0.764 & 0.802 & \\
47 + \bottomrule
48 + \end{tabular}
49 + \begin{tablenotes}[flushleft]
50 + \footnotesize
51 + \item \textit{Notes:} OLS estimates of Equation~\eqref{eq:hedonic} on the measured-area subsample with the full
52 + control set (coefficients of the controls not shown). Column~(1) is the baseline of Table~\ref{tab:regression}.
53 + Column~(2) adds indicators for English and mixed-language descriptions and the description length (thousands of
54 + characters). Column~(3) replaces the 195 municipality fixed effects by 250 forward-sortation-area (RTA) fixed
55 + effects (RTAs with at least 20 listings; the remainder pooled). Column~(4) restricts the sample to Realtor.ca
56 + listings, whose descriptions never contain the asking price. Standard errors in parentheses are clustered at the
57 + level of the fixed effects. Column~(5) reports the 2.5th and 97.5th percentiles of a pairs cluster bootstrap
58 + (299 replications, resampling the 195 municipalities with replacement) for the baseline
59 + specification. $^{***}$, $^{**}$, $^{*}$: significance at 1\%, 5\%, 10\%.
60 + \end{tablenotes}
61 + \end{threeparttable}
62 +\end{table}
added paper/tables/rob_language_source.tex +52 −0
@@ -0,0 +1,52 @@
1 +% Auto-generated by scripts/18_revision_language_leakage_spatial.py (2026-10-17)
2 +\begin{table}[htbp]
3 + \centering
4 + \begin{threeparttable}
5 + \caption{Gains from the AI Feature Layers by Language, Platform, and Presence of a Price in the Text}
6 + \label{tab:rob_language_source}
7 + \small
8 + \begin{tabular}{@{}lcccc@{}}
9 + \toprule
10 + Subsample & $N$ & $R^2$ base & $\Delta R^2$ text (pp) & $\Delta R^2$ vision (pp) \\
11 + \midrule
12 + \multicolumn{5}{@{}l}{\textit{Panel A: In-sample $R^2$, Model~A (measured area), municipality FE}} \\[2pt]
13 + All listings & 9{,}446 & 0.717 & +0.8 & +3.0 \\
14 + \quad French descriptions & 2{,}619 & 0.704 & +1.5 & +3.4 \\
15 + \quad English descriptions & 4{,}815 & 0.763 & +0.6 & +2.2 \\
16 + \quad Mixed / bilingual & 2{,}012 & 0.579 & +3.2 & +4.8 \\
17 + \quad Realtor.ca (no asking price in text) & 5{,}781 & 0.779 & +0.6 & +2.4 \\
18 + \quad DuProprio (asking price in text) & 3{,}665 & 0.564 & +3.3 & +4.1 \\
19 + \quad Realtor.ca, no dollar amount in text & 5{,}640 & 0.780 & +0.6 & +2.4 \\
20 + All listings, RTA fixed effects & 9{,}446 & 0.722 & +0.7 & +3.2 \\
21 + \addlinespace
22 + \multicolumn{5}{@{}l}{\textit{Panel B: Out-of-sample $R^2$ (5-fold, random), OLS, full sample}} \\[2pt]
23 + All listings & 19{,}123 & 0.619 & +0.8 & +5.8 \\
24 + \quad French & 8{,}529 & 0.622 & +1.2 & +4.8 \\
25 + \quad English & 8{,}250 & 0.562 & +0.7 & +7.9 \\
26 + \quad Realtor.ca & 15{,}356 & 0.701 & +0.6 & +3.9 \\
27 + \quad DuProprio & 3{,}767 & $-0.083$ & +2.8 & +22.8 \\
28 + \addlinespace
29 + \multicolumn{5}{@{}l}{\textit{Panel C: Out-of-sample $R^2$ (5-fold, random), gradient boosting, full sample}} \\[2pt]
30 + All listings & 19{,}123 & 0.729 & +1.4 & +2.7 \\
31 + \quad French & 8{,}529 & 0.680 & +1.5 & +3.1 \\
32 + \quad English & 8{,}250 & 0.737 & +1.5 & +2.6 \\
33 + \quad Realtor.ca & 15{,}356 & 0.748 & +1.2 & +2.6 \\
34 + \quad DuProprio & 3{,}767 & 0.564 & +3.1 & +2.8 \\
35 + \bottomrule
36 + \end{tabular}
37 + \begin{tablenotes}[flushleft]
38 + \footnotesize
39 + \item \textit{Notes:} Panel~A re-estimates the nested in-sample ablation of Model~A (structural, amenity, room, and
40 + listing controls with municipality and type fixed effects; then $+$ LLM text layer; then $+$ façade vision layer) on
41 + each subsample; ``$\Delta R^2$'' is the gain, in percentage points, from adding the layer. Language is classified
42 + from French and English stop-word counts (ratio rule of 1.5; ``mixed'' listings are mostly DuProprio descriptions that
43 + concatenate an English translation and the French original). Every DuProprio description contains the asking price
44 + (``Asking Price \$\dots''); 2.5\% of Realtor.ca
45 + descriptions contain some dollar amount (usually a municipal assessment, a renovation budget, or a tax; the 141
46 + Model~A listings concerned are too few for a separate regression). Panels~B and
47 + C evaluate the full-sample cross-validated predictions (Table~\ref{tab:ablation}, identical folds) within each
48 + subsample; the base is the structural $+$ amenity $+$ rooms $+$ listing specification. Within-subsample $R^2$
49 + values can be negative (DuProprio, OLS) when the pooled model is miscalibrated for the subsample.
50 + \end{tablenotes}
51 + \end{threeparttable}
52 +\end{table}
added paper/tables/rob_spatial.tex +29 −0
@@ -0,0 +1,29 @@
1 +% Auto-generated by scripts/18_revision_language_leakage_spatial.py (2026-10-17)
2 +\begin{table}[htbp]
3 + \centering
4 + \begin{threeparttable}
5 + \caption{Random versus Spatial (RTA-Grouped) Cross-Validation}
6 + \label{tab:rob_spatial}
7 + \small
8 + \begin{tabular}{@{}lcccc@{\hskip 0.8cm}cccc@{}}
9 + \toprule
10 + & \multicolumn{4}{c}{Random 5-fold} & \multicolumn{4}{c}{Spatial 5-fold (RTA groups)} \\
11 + \cmidrule(lr){2-5}\cmidrule(lr){6-9}
12 + Estimator & Base & $+$Text & $+$Vision & MdAPE & Base & $+$Text & $+$Vision & MdAPE \\
13 + \midrule
14 + OLS (linear hedonic) & 0.619 & 0.628 & 0.686 & 16.9\% & 0.561 & 0.568 & 0.638 & 18.5\% \\
15 + Gradient boosting & 0.729 & 0.743 & 0.770 & 14.5\% & 0.690 & 0.703 & 0.734 & 15.9\% \\
16 + \bottomrule
17 + \end{tabular}
18 + \begin{tablenotes}[flushleft]
19 + \footnotesize
20 + \item \textit{Notes:} Out-of-sample $R^2$ on $\ln(\text{Price})$ and median absolute percentage error (MdAPE, for the
21 + full text\,$+$\,vision specification). ``Base'' is the structural $+$ amenity $+$ rooms $+$ listing specification; the
22 + next two columns add the LLM text layer and the façade vision layer. In the spatial design the 420 forward
23 + sortation areas (RTA, first three characters of the postal code) are assigned to five folds by \texttt{GroupKFold}, so
24 + no listing is predicted from a model that has seen a listing of the same RTA; municipality indicators of RTAs held
25 + out in their entirety are unobserved in training and the model falls back on the reference category. Random-fold
26 + entries may differ by $\pm0.001$ from Table~\ref{tab:ablation}, which comes from a separate run. $N=$19{,}123.
27 + \end{tablenotes}
28 + \end{threeparttable}
29 +\end{table}
added results/revision_2026-10-17.json +1643 −0
@@ -0,0 +1,1643 @@
1 +{
2 + "text_leakage": {
3 + "n": 19123,
4 + "asking_price_in_text": 3767,
5 + "asking_price_equals_list_price": 3761,
6 + "asking_by_source": {
7 + "duproprio": 3767,
8 + "realtor": 0
9 + },
10 + "dollar_amount_in_text": 4150,
11 + "dollar_by_source": {
12 + "duproprio": 1.0,
13 + "realtor": 0.0249
14 + },
15 + "assessment_mention": 1796,
16 + "assessment_by_source": {
17 + "duproprio": 0.4659,
18 + "realtor": 0.0027
19 + },
20 + "realtor_dollar_share": 0.02494139098723626
21 + },
22 + "language": {
23 + "by_source": [
24 + {
25 + "source": "duproprio",
26 + "lang": "en",
27 + "n": 1813,
28 + "median_price": 625000.0,
29 + "mean_lnp": 13.33569162585933,
30 + "desc_len": 3.2069260893546607
31 + },
32 + {
33 + "source": "duproprio",
34 + "lang": "fr",
35 + "n": 5,
36 + "median_price": 679000.0,
37 + "mean_lnp": 13.417860336206633,
38 + "desc_len": 4.3304
39 + },
40 + {
41 + "source": "duproprio",
42 + "lang": "mixed",
43 + "n": 1949,
44 + "median_price": 595000.0,
45 + "mean_lnp": 13.28764506220431,
46 + "desc_len": 4.836105695228322
47 + },
48 + {
49 + "source": "realtor",
50 + "lang": "en",
51 + "n": 6437,
52 + "median_price": 749000.0,
53 + "mean_lnp": 13.603230702848846,
54 + "desc_len": 0.5437955569364611
55 + },
56 + {
57 + "source": "realtor",
58 + "lang": "fr",
59 + "n": 8524,
60 + "median_price": 524900.0,
61 + "mean_lnp": 13.171599912716486,
62 + "desc_len": 0.5476710464570624
63 + },
64 + {
65 + "source": "realtor",
66 + "lang": "mixed",
67 + "n": 395,
68 + "median_price": 579000.0,
69 + "mean_lnp": 13.252576586101583,
70 + "desc_len": 0.048329113924050635
71 + }
72 + ],
73 + "share_en_overall": 0.4314176645923757,
74 + "share_en_realtor": 0.4191846835113311,
75 + "share_by_lang": {
76 + "fr": 0.446,
77 + "en": 0.4314,
78 + "mixed": 0.1226
79 + },
80 + "raw_gap_en_cityFE": 0.2143026513746558,
81 + "raw_gap_en_nocontrols": 0.37269268462471317
82 + },
83 + "coverage": {
84 + "specs": 19123,
85 + "facade": 19123,
86 + "is_exterior": 18314,
87 + "n": 19123
88 + },
89 + "fsa": {
90 + "n_fsa_raw": 419,
91 + "n_fsa_fe": 250,
92 + "n_city_fe": 195
93 + },
94 + "ols": {
95 + "A": {
96 + "r2": 0.7552103110293897,
97 + "n": 9446,
98 + "coef": {
99 + "v_curb": {
100 + "b": 0.023017903057222053,
101 + "se": 0.00865392944591846,
102 + "p": 0.007818219565022228
103 + },
104 + "v_reno": {
105 + "b": 0.023593099837373357,
106 + "se": 0.009529503385428685,
107 + "p": 0.013293976823549589
108 + },
109 + "v_luxury": {
110 + "b": 0.051004755970147975,
111 + "se": 0.01193275620932702,
112 + "p": 1.9169718790851146e-05
113 + },
114 + "v_garage_vis": {
115 + "b": 0.04626239493911488,
116 + "se": 0.005814520452429011,
117 + "p": 1.7718006522120285e-15
118 + },
119 + "v_mat_brique": {
120 + "b": 0.033343794442161366,
121 + "se": 0.00914381589542974,
122 + "p": 0.0002657381339896736
123 + },
124 + "v_mat_pierre": {
125 + "b": 0.036151980964813646,
126 + "se": 0.008011891510533968,
127 + "p": 6.413130561176702e-06
128 + },
129 + "v_mat_vinyle": {
130 + "b": -0.04493374989991618,
131 + "se": 0.015100349535057013,
132 + "p": 0.0029234340682950024
133 + },
134 + "v_set_rural": {
135 + "b": -0.12230705352270166,
136 + "se": 0.0321849710016236,
137 + "p": 0.00014462056903144396
138 + },
139 + "v_land": {
140 + "b": 0.015079214851206849,
141 + "se": 0.0062394992136159825,
142 + "p": 0.01566042254639302
143 + },
144 + "g_cond": {
145 + "b": 0.016168128372665236,
146 + "se": 0.0058314343073788385,
147 + "p": 0.005561354758418819
148 + },
149 + "g_renovated_kitchen": {
150 + "b": 0.02608983576575613,
151 + "se": 0.006899975364888895,
152 + "p": 0.00015610612944890796
153 + },
154 + "g_hardwood": {
155 + "b": 0.02365468450221908,
156 + "se": 0.00932507643698107,
157 + "p": 0.011191092284736665
158 + },
159 + "g_income_property": {
160 + "b": -0.019168272989677937,
161 + "se": 0.009252803603397662,
162 + "p": 0.03830109511353034
163 + }
164 + }
165 + },
166 + "A_lang": {
167 + "r2": 0.7567348777632339,
168 + "n": 9446,
169 + "coef": {
170 + "v_curb": {
171 + "b": 0.0220282046381,
172 + "se": 0.008674581574151156,
173 + "p": 0.011104380250457709
174 + },
175 + "v_reno": {
176 + "b": 0.02399647790026045,
177 + "se": 0.009084098521880597,
178 + "p": 0.008251750232457736
179 + },
180 + "v_luxury": {
181 + "b": 0.05137576904637699,
182 + "se": 0.012091590235267013,
183 + "p": 2.1483774749896047e-05
184 + },
185 + "v_garage_vis": {
186 + "b": 0.046255811432241944,
187 + "se": 0.005799162639961236,
188 + "p": 1.5079561537175714e-15
189 + },
190 + "v_mat_brique": {
191 + "b": 0.03226528382664501,
192 + "se": 0.008884270781382892,
193 + "p": 0.0002815262245350303
194 + },
195 + "v_mat_pierre": {
196 + "b": 0.0369047947749403,
197 + "se": 0.008449900660313994,
198 + "p": 1.2568676032209342e-05
199 + },
200 + "v_mat_vinyle": {
201 + "b": -0.043735965072819655,
202 + "se": 0.015191301814047412,
203 + "p": 0.003989210850339842
204 + },
205 + "v_set_rural": {
206 + "b": -0.12002666538429863,
207 + "se": 0.03301891648600372,
208 + "p": 0.0002778858194579655
209 + },
210 + "v_land": {
211 + "b": 0.015267192969310894,
212 + "se": 0.006239374073719944,
213 + "p": 0.014408641917175714
214 + },
215 + "g_cond": {
216 + "b": 0.01676521632569178,
217 + "se": 0.0057271291357884,
218 + "p": 0.0034188218673600174
219 + },
220 + "g_renovated_kitchen": {
221 + "b": 0.024655303780972332,
222 + "se": 0.0069227344505953785,
223 + "p": 0.00036874514498707776
224 + },
225 + "g_hardwood": {
226 + "b": 0.02617810938024412,
227 + "se": 0.008972271748477777,
228 + "p": 0.0035265971560858537
229 + },
230 + "g_income_property": {
231 + "b": -0.020691265306062245,
232 + "se": 0.008971390971810616,
233 + "p": 0.021090475166125118
234 + },
235 + "lang_en": {
236 + "b": 0.06965266990700705,
237 + "se": 0.010757367405508223,
238 + "p": 9.488692166328437e-11
239 + },
240 + "lang_mixed": {
241 + "b": 0.04502633023718461,
242 + "se": 0.011249579206348023,
243 + "p": 6.267927171514527e-05
244 + }
245 + }
246 + },
247 + "B": {
248 + "r2": 0.7250019979121326,
249 + "n": 19123,
250 + "coef": {
251 + "v_curb": {
252 + "b": 0.01595794997397919,
253 + "se": 0.013160921694536032,
254 + "p": 0.22531138001340867
255 + },
256 + "v_reno": {
257 + "b": 0.019980004053515125,
258 + "se": 0.008625974658909864,
259 + "p": 0.020544051694801335
260 + },
261 + "v_luxury": {
262 + "b": 0.06166029483028444,
263 + "se": 0.00869771753262625,
264 + "p": 1.3484065717025971e-12
265 + },
266 + "v_garage_vis": {
267 + "b": 0.04651267305323856,
268 + "se": 0.006720574480616525,
269 + "p": 4.486657807636773e-12
270 + },
271 + "v_mat_brique": {
272 + "b": 0.04403606617653332,
273 + "se": 0.011001756372450839,
274 + "p": 6.263967385428824e-05
275 + },
276 + "v_mat_pierre": {
277 + "b": 0.04658529674360948,
278 + "se": 0.004986368566239516,
279 + "p": 9.405939222760319e-21
280 + },
281 + "v_mat_vinyle": {
282 + "b": -0.0618081668156812,
283 + "se": 0.012339743471281481,
284 + "p": 5.475063125324791e-07
285 + },
286 + "v_set_rural": {
287 + "b": -0.07600759882542366,
288 + "se": 0.028985068114089782,
289 + "p": 0.008733802916506193
290 + },
291 + "v_land": {
292 + "b": 0.025364605559561076,
293 + "se": 0.004475099360767159,
294 + "p": 1.4452250155049804e-08
295 + },
296 + "g_cond": {
297 + "b": 0.016317524818281637,
298 + "se": 0.005198405151324424,
299 + "p": 0.0016955538138231493
300 + },
301 + "g_renovated_kitchen": {
302 + "b": 0.02694519585553197,
303 + "se": 0.009635624404475364,
304 + "p": 0.005167314672211995
305 + },
306 + "g_hardwood": {
307 + "b": 0.029508607751222216,
308 + "se": 0.010501088270171847,
309 + "p": 0.004953344261642659
310 + },
311 + "g_income_property": {
312 + "b": -0.012880740908551014,
313 + "se": 0.007646640747975008,
314 + "p": 0.09208572167015686
315 + }
316 + }
317 + },
318 + "B_lang": {
319 + "r2": 0.7294706327446184,
320 + "n": 19123,
321 + "coef": {
322 + "v_curb": {
323 + "b": 0.014140409984109793,
324 + "se": 0.012613533533211005,
325 + "p": 0.26226630268571227
326 + },
327 + "v_reno": {
328 + "b": 0.021452819122661136,
329 + "se": 0.00809144238940101,
330 + "p": 0.008018323350468072
331 + },
332 + "v_luxury": {
333 + "b": 0.061499675154300534,
334 + "se": 0.008400292138243528,
335 + "p": 2.458818030231777e-13
336 + },
337 + "v_garage_vis": {
338 + "b": 0.047583395448239,
339 + "se": 0.006708938616990217,
340 + "p": 1.3167540880058334e-12
341 + },
342 + "v_mat_brique": {
343 + "b": 0.04270872969142887,
344 + "se": 0.010761547590495083,
345 + "p": 7.228338107064312e-05
346 + },
347 + "v_mat_pierre": {
348 + "b": 0.047530408479713834,
349 + "se": 0.005227275353296171,
350 + "p": 9.654802277297552e-20
351 + },
352 + "v_mat_vinyle": {
353 + "b": -0.059391516825136194,
354 + "se": 0.011539624897825134,
355 + "p": 2.6504399622334076e-07
356 + },
357 + "v_set_rural": {
358 + "b": -0.07285861233369789,
359 + "se": 0.028764862151562362,
360 + "p": 0.011312217666697268
361 + },
362 + "v_land": {
363 + "b": 0.024365920822358444,
364 + "se": 0.00434483530483999,
365 + "p": 2.046550527173117e-08
366 + },
367 + "g_cond": {
368 + "b": 0.01670923929962092,
369 + "se": 0.005120433745030281,
370 + "p": 0.0011014353593805713
371 + },
372 + "g_renovated_kitchen": {
373 + "b": 0.02463755558525975,
374 + "se": 0.008759011578689791,
375 + "p": 0.0049108563812810075
376 + },
377 + "g_hardwood": {
378 + "b": 0.033418913488580115,
379 + "se": 0.011154095621616887,
380 + "p": 0.0027344648221796965
381 + },
382 + "g_income_property": {
383 + "b": -0.015281082928718548,
384 + "se": 0.007369131187798335,
385 + "p": 0.03811076806327996
386 + },
387 + "lang_en": {
388 + "b": 0.1117961062255265,
389 + "se": 0.028766842224795294,
390 + "p": 0.00010179059625158188
391 + },
392 + "lang_mixed": {
393 + "b": 0.053708392373144176,
394 + "se": 0.018882514235156286,
395 + "p": 0.004450277756183304
396 + }
397 + }
398 + },
399 + "A_fsa": {
400 + "r2": 0.7613601578784245,
401 + "n": 9446,
402 + "coef": {
403 + "v_curb": {
404 + "b": 0.017181245734599684,
405 + "se": 0.006213488488310514,
406 + "p": 0.005689611946087438
407 + },
408 + "v_reno": {
409 + "b": 0.02913062835600555,
410 + "se": 0.00866518471552936,
411 + "p": 0.0007743585263284946
412 + },
413 + "v_luxury": {
414 + "b": 0.04423659509281494,
415 + "se": 0.010353820853101102,
416 + "p": 1.9330230571668745e-05
417 + },
418 + "v_garage_vis": {
419 + "b": 0.03734060333608957,
420 + "se": 0.0060003239199587066,
421 + "p": 4.874329300926464e-10
422 + },
423 + "v_mat_brique": {
424 + "b": 0.03238723283442161,
425 + "se": 0.010519113380586495,
426 + "p": 0.0020777074106452464
427 + },
428 + "v_mat_pierre": {
429 + "b": 0.03634362862612156,
430 + "se": 0.008664004648796565,
431 + "p": 2.731316639733451e-05
432 + },
433 + "v_mat_vinyle": {
434 + "b": -0.04840783002433969,
435 + "se": 0.018446653047869965,
436 + "p": 0.008685106631411392
437 + },
438 + "v_set_rural": {
439 + "b": -0.12639182908614943,
440 + "se": 0.04328081185117937,
441 + "p": 0.003497237648930978
442 + },
443 + "v_land": {
444 + "b": 0.012681103312182654,
445 + "se": 0.005537781242784083,
446 + "p": 0.022025668911288897
447 + },
448 + "g_cond": {
449 + "b": 0.014042986971437271,
450 + "se": 0.005092267362968359,
451 + "p": 0.005820814958749212
452 + },
453 + "g_renovated_kitchen": {
454 + "b": 0.01954704770829435,
455 + "se": 0.00928173697158564,
456 + "p": 0.03520706974827266
457 + },
458 + "g_hardwood": {
459 + "b": 0.013896176008893368,
460 + "se": 0.006666798086296709,
461 + "p": 0.037125132793963206
462 + },
463 + "g_income_property": {
464 + "b": -0.012788089560278326,
465 + "se": 0.012751573385642126,
466 + "p": 0.31592664823176597
467 + }
468 + }
469 + },
470 + "A_fsa_lang": {
471 + "r2": 0.7636495766633198,
472 + "n": 9446,
473 + "coef": {
474 + "v_curb": {
475 + "b": 0.016248935580446572,
476 + "se": 0.006266364786650203,
477 + "p": 0.009513165983223961
478 + },
479 + "v_reno": {
480 + "b": 0.029235212598072574,
481 + "se": 0.008624780791463442,
482 + "p": 0.0006997526996340517
483 + },
484 + "v_luxury": {
485 + "b": 0.04475794826074414,
486 + "se": 0.010335676031969747,
487 + "p": 1.48816703394419e-05
488 + },
489 + "v_garage_vis": {
490 + "b": 0.03704006371810917,
491 + "se": 0.006307305401601367,
492 + "p": 4.29102234235383e-09
493 + },
494 + "v_mat_brique": {
495 + "b": 0.03097446907959662,
496 + "se": 0.010197993272572073,
497 + "p": 0.0023869966760450994
498 + },
499 + "v_mat_pierre": {
500 + "b": 0.037413728935033974,
501 + "se": 0.008538160274934682,
502 + "p": 1.1762575781059087e-05
503 + },
504 + "v_mat_vinyle": {
505 + "b": -0.04717895540837608,
506 + "se": 0.017800053118632878,
507 + "p": 0.008037386860489196
508 + },
509 + "v_set_rural": {
510 + "b": -0.12237916860417042,
511 + "se": 0.04215648416880492,
512 + "p": 0.003696375714822433
513 + },
514 + "v_land": {
515 + "b": 0.012748126858161887,
516 + "se": 0.005568057347587657,
517 + "p": 0.022049697114484807
518 + },
519 + "g_cond": {
520 + "b": 0.014548282942941665,
521 + "se": 0.005098494623085869,
522 + "p": 0.004324777443249617
523 + },
524 + "g_renovated_kitchen": {
525 + "b": 0.01880486827485789,
526 + "se": 0.009270238446950178,
527 + "p": 0.04250715821797065
528 + },
529 + "g_hardwood": {
530 + "b": 0.015526162898754026,
531 + "se": 0.006910168275374136,
532 + "p": 0.024649140377487517
533 + },
534 + "g_income_property": {
535 + "b": -0.014139484159004345,
536 + "se": 0.012486591753120405,
537 + "p": 0.2574774898168585
538 + },
539 + "lang_en": {
540 + "b": 0.08951918575974482,
541 + "se": 0.03446376644438621,
542 + "p": 0.009390867133295603
543 + },
544 + "lang_mixed": {
545 + "b": 0.06682706837252739,
546 + "se": 0.030328238917951333,
547 + "p": 0.027562326919126412
548 + }
549 + }
550 + },
551 + "B_fsa": {
552 + "r2": 0.7263545294217424,
553 + "n": 19123,
554 + "coef": {
555 + "v_curb": {
556 + "b": 0.014414483272076815,
557 + "se": 0.006752978595693768,
558 + "p": 0.03279884099969961
559 + },
560 + "v_reno": {
561 + "b": 0.02362309974851571,
562 + "se": 0.006386987651381948,
563 + "p": 0.0002167669061224354
564 + },
565 + "v_luxury": {
566 + "b": 0.05522265404408447,
567 + "se": 0.011801488738274954,
568 + "p": 2.878622062546483e-06
569 + },
570 + "v_garage_vis": {
571 + "b": 0.04488665381680389,
572 + "se": 0.005673121074707837,
573 + "p": 2.529566272762449e-15
574 + },
575 + "v_mat_brique": {
576 + "b": 0.047093395958372516,
577 + "se": 0.0145662424954123,
578 + "p": 0.0012247595790037217
579 + },
580 + "v_mat_pierre": {
581 + "b": 0.04852774521254276,
582 + "se": 0.005333025621243613,
583 + "p": 9.076766303267039e-20
584 + },
585 + "v_mat_vinyle": {
586 + "b": -0.06523612504184213,
587 + "se": 0.016693566121318202,
588 + "p": 9.311710177370299e-05
589 + },
590 + "v_set_rural": {
591 + "b": -0.07095781352495395,
592 + "se": 0.055364077824565636,
593 + "p": 0.199962642707183
594 + },
595 + "v_land": {
596 + "b": 0.019606183535763017,
597 + "se": 0.004473568939205577,
598 + "p": 1.1723276112067005e-05
599 + },
600 + "g_cond": {
601 + "b": 0.015734770445255054,
602 + "se": 0.004859055192010304,
603 + "p": 0.0012027101480981657
604 + },
605 + "g_renovated_kitchen": {
606 + "b": 0.022079231334420107,
607 + "se": 0.0064739961517580125,
608 + "p": 0.0006485609932588853
609 + },
610 + "g_hardwood": {
611 + "b": 0.01714089477691072,
612 + "se": 0.006270000021361955,
613 + "p": 0.006260900898443002
614 + },
615 + "g_income_property": {
616 + "b": -0.01014885437918328,
617 + "se": 0.01070211622945107,
618 + "p": 0.3429749647833692
619 + }
620 + }
621 + },
622 + "A_realtor_fsa_lang": {
623 + "r2": 0.8019629333671822,
624 + "n": 5781,
625 + "coef": {
626 + "v_curb": {
627 + "b": 0.021367211208677767,
628 + "se": 0.009612912797527693,
629 + "p": 0.026231895289707187
630 + },
631 + "v_reno": {
632 + "b": 0.04129900476874043,
633 + "se": 0.017928231499329213,
634 + "p": 0.021246578714016275
635 + },
636 + "v_luxury": {
637 + "b": 0.04203233411432983,
638 + "se": 0.014712814498368115,
639 + "p": 0.00427865237046459
640 + },
641 + "v_garage_vis": {
642 + "b": 0.04066858689843098,
643 + "se": 0.008806843246547272,
644 + "p": 3.877563337776636e-06
645 + },
646 + "v_mat_brique": {
647 + "b": 0.023721189498116607,
648 + "se": 0.017447575657522845,
649 + "p": 0.17396623551628632
650 + },
651 + "v_mat_pierre": {
652 + "b": 0.02993286402615017,
653 + "se": 0.01021427196812345,
654 + "p": 0.003384233894502081
655 + },
656 + "v_mat_vinyle": {
657 + "b": -0.050884217991728764,
658 + "se": 0.02101648171689293,
659 + "p": 0.015471161025246786
660 + },
661 + "v_set_rural": {
662 + "b": -0.11889877957340445,
663 + "se": 0.0868560204710719,
664 + "p": 0.17102487343264272
665 + },
666 + "v_land": {
667 + "b": 0.026578661718596436,
668 + "se": 0.007339042464912785,
669 + "p": 0.00029285052198313205
670 + },
671 + "g_cond": {
672 + "b": 0.012038030803227698,
673 + "se": 0.008798811347968356,
674 + "p": 0.17126736734969095
675 + },
676 + "g_renovated_kitchen": {
677 + "b": 0.030471254734996567,
678 + "se": 0.011934169854197565,
679 + "p": 0.010671424721548424
680 + },
681 + "g_hardwood": {
682 + "b": -0.0010674440390479045,
683 + "se": 0.013197890742933791,
684 + "p": 0.9355374677428574
685 + },
686 + "g_income_property": {
687 + "b": -0.030563244373362275,
688 + "se": 0.02091115359756968,
689 + "p": 0.14385738078865717
690 + },
691 + "lang_en": {
692 + "b": 0.08209491651353809,
693 + "se": 0.041682106904610224,
694 + "p": 0.048890179126368095
695 + },
696 + "lang_mixed": {
697 + "b": 0.1810730294836176,
698 + "se": 0.041883938233998004,
699 + "p": 1.537757500291537e-05
700 + }
701 + }
702 + }
703 + },
704 + "ablation_A": {
705 + "all": {
706 + "base": 0.7168982211740982,
707 + "plus_text": 0.7252251732782795,
708 + "plus_text_vision": 0.7552103110293897,
709 + "n": 9446,
710 + "d_text": 0.008326952104181329,
711 + "d_vision": 0.029985137751110225
712 + },
713 + "fr": {
714 + "base": 0.704191925205909,
715 + "plus_text": 0.7196528232071896,
716 + "plus_text_vision": 0.7534024100015527,
717 + "n": 2619,
718 + "d_text": 0.015460898001280587,
719 + "d_vision": 0.03374958679436313
720 + },
721 + "en": {
722 + "base": 0.7633876402412476,
723 + "plus_text": 0.7697083706514656,
724 + "plus_text_vision": 0.7921900184144034,
725 + "n": 4815,
726 + "d_text": 0.006320730410217967,
727 + "d_vision": 0.022481647762937818
728 + },
729 + "mixed": {
730 + "base": 0.5788853289018507,
731 + "plus_text": 0.6110278423736797,
732 + "plus_text_vision": 0.6590466596511697,
733 + "n": 2012,
734 + "d_text": 0.03214251347182895,
735 + "d_vision": 0.04801881727749002
736 + },
737 + "realtor": {
738 + "base": 0.7792712016051034,
739 + "plus_text": 0.7857284989543681,
740 + "plus_text_vision": 0.8097641057720486,
741 + "n": 5781,
742 + "d_text": 0.006457297349264701,
743 + "d_vision": 0.024035606817680466
744 + },
745 + "duproprio": {
746 + "base": 0.5641360338178396,
747 + "plus_text": 0.5975909670433623,
748 + "plus_text_vision": 0.6387544773609131,
749 + "n": 3665,
750 + "d_text": 0.03345493322552273,
751 + "d_vision": 0.04116351031755072
752 + },
753 + "realtor_no_dollar": {
754 + "base": 0.7804575160736943,
755 + "plus_text": 0.7868051788991395,
756 + "plus_text_vision": 0.8105017890993039,
757 + "n": 5640,
758 + "d_text": 0.006347662825445188,
759 + "d_vision": 0.023696610200164447
760 + },
761 + "realtor_dollar": {
762 + "base": 0.9203905155093728,
763 + "plus_text": 0.9663686451614234,
764 + "plus_text_vision": 0.9981623795399744,
765 + "n": 141,
766 + "d_text": 0.045978129652050614,
767 + "d_vision": 0.03179373437855104
768 + },
769 + "with_lang": {
770 + "base": 0.7188023071488996,
771 + "plus_text": 0.7274109943432802,
772 + "plus_text_vision": 0.7567348777632339
773 + },
774 + "fsa_fe": {
775 + "base": 0.7224359228022387,
776 + "plus_text": 0.7298409617480321,
777 + "plus_text_vision": 0.7613601578784245,
778 + "n": 9446,
779 + "d_text": 0.007405038945793474,
780 + "d_vision": 0.031519196130392335
781 + }
782 + },
783 + "cluster_bootstrap_A": {
784 + "B": 299,
785 + "n_clusters": 195,
786 + "ci95": {
787 + "v_curb": [
788 + 0.0008207600301133864,
789 + 0.03531138785517337
790 + ],
791 + "v_reno": [
792 + 0.009861320035016418,
793 + 0.04639780248828285
794 + ],
795 + "v_luxury": [
796 + 0.018702682550024947,
797 + 0.06587201000970766
798 + ],
799 + "v_garage_vis": [
800 + 0.029489483851641338,
801 + 0.05546454216073049
802 + ],
803 + "v_mat_brique": [
804 + 0.001091429965688582,
805 + 0.04435286993426672
806 + ],
807 + "v_mat_pierre": [
808 + 0.02208144167342061,
809 + 0.04941585568306584
810 + ],
811 + "v_mat_vinyle": [
812 + -0.06693216723701244,
813 + -0.007135683318845551
814 + ],
815 + "v_set_rural": [
816 + -0.1756530215378054,
817 + -0.02915765109659804
818 + ],
819 + "v_land": [
820 + -0.0003849727417672886,
821 + 0.024962880210475685
822 + ],
823 + "g_cond": [
824 + 0.002359303173937946,
825 + 0.024318565020234856
826 + ],
827 + "g_renovated_kitchen": [
828 + 0.009756118491580604,
829 + 0.03945200625378577
830 + ],
831 + "g_hardwood": [
832 + -0.0027363742113989285,
833 + 0.03581234933965173
834 + ],
835 + "g_income_property": [
836 + -0.040193714643788465,
837 + -0.0016888306322588513
838 + ]
839 + },
840 + "se": {
841 + "v_curb": 0.009179049835986632,
842 + "v_reno": 0.009511910131603225,
843 + "v_luxury": 0.01319348004117406,
844 + "v_garage_vis": 0.007007886945506273,
845 + "v_mat_brique": 0.011943996189678436,
846 + "v_mat_pierre": 0.007469085731764851,
847 + "v_mat_vinyle": 0.016653171942833497,
848 + "v_set_rural": 0.03679888572279241,
849 + "v_land": 0.006913416315742771,
850 + "g_cond": 0.006157267193711723,
851 + "g_renovated_kitchen": 0.0075529847838382794,
852 + "g_hardwood": 0.010951468962615583,
853 + "g_income_property": 0.009957463462929536
854 + }
855 + },
856 + "agreement": {
857 + "llm_vs_keyword": {
858 + "garage": {
859 + "agreement": 0.981906604612247,
860 + "kappa": 0.9625804939313405,
861 + "precision_vs_ref": 0.9797721162463193,
862 + "recall_vs_ref": 0.9760234663945925,
863 + "share_a": 0.4084610155310359,
864 + "share_ref": 0.41002980703864456,
865 + "n": 19123
866 + },
867 + "pool": {
868 + "agreement": 0.9353657898865241,
869 + "kappa": 0.8055621562739752,
870 + "precision_vs_ref": 0.9880081895291021,
871 + "recall_vs_ref": 0.7386835775202274,
872 + "share_a": 0.1787899388171312,
873 + "share_ref": 0.23913611880981017,
874 + "n": 19123
875 + },
876 + "waterfront": {
877 + "agreement": 0.8622078125817079,
878 + "kappa": 0.5007327488879811,
879 + "precision_vs_ref": 0.3984230055658627,
880 + "recall_vs_ref": 0.9766913018760659,
881 + "share_a": 0.22548763269361502,
882 + "share_ref": 0.09198347539611985,
883 + "n": 19123
884 + },
885 + "fireplace": {
886 + "agreement": 0.9667416200386969,
887 + "kappa": 0.9053787374931429,
888 + "precision_vs_ref": 0.9627329192546584,
889 + "recall_vs_ref": 0.893569844789357,
890 + "share_a": 0.21889870836165873,
891 + "share_ref": 0.23584165664383203,
892 + "n": 19123
893 + },
894 + "central_ac": {
895 + "agreement": 0.9081734037546411,
896 + "kappa": 0.6475279894680741,
897 + "precision_vs_ref": 0.9247606019151847,
898 + "recall_vs_ref": 0.5603757944183476,
899 + "share_a": 0.1146786592061915,
900 + "share_ref": 0.18924854886785547,
901 + "n": 19123
902 + },
903 + "finished_basement": {
904 + "agreement": 0.8048423364534854,
905 + "kappa": 0.5638469085392556,
906 + "precision_vs_ref": 0.6745762711864407,
907 + "recall_vs_ref": 0.750204884445173,
908 + "share_a": 0.3548083459708205,
909 + "share_ref": 0.3190398995973435,
910 + "n": 19123
911 + }
912 + },
913 + "vision_vs_text": {
914 + "garage_visible_vs_llm_garage": {
915 + "agreement": 0.675930981762586,
916 + "kappa": 0.3459617653055207,
917 + "precision_vs_ref": 0.5904805702460336,
918 + "recall_vs_ref": 0.6839792249300839,
919 + "share_a": 0.4749372065086819,
920 + "share_ref": 0.4100141967893415,
921 + "n": 18314
922 + },
923 + "garage_visible_vs_keyword_garage": {
924 + "agreement": 0.6736922572895053,
925 + "kappa": 0.34154176184060453,
926 + "precision_vs_ref": 0.5896757875373649,
927 + "recall_vs_ref": 0.6805997876857749,
928 + "share_a": 0.4749372065086819,
929 + "share_ref": 0.411488478759419,
930 + "n": 18314
931 + },
932 + "waterfront_setting_vs_keyword": {
933 + "agreement": 0.9114884787594191,
934 + "kappa": 0.38599787805522834,
935 + "precision_vs_ref": 0.5167224080267558,
936 + "recall_vs_ref": 0.3720650210716436,
937 + "share_a": 0.06530523097084198,
938 + "share_ref": 0.09069564267773288,
939 + "n": 18314
940 + }
941 + },
942 + "vision_storeys_vs_structured": {
943 + "n": 18065,
944 + "exact": 0.604705231109881,
945 + "within_half_storey": 0.8871851646830888
946 + },
947 + "vision_rowhouse_vs_listing_type": {
948 + "agreement": 0.8902478977831167,
949 + "kappa": 0.2637713396198635,
950 + "precision_vs_ref": 0.18451612903225806,
951 + "recall_vs_ref": 0.7900552486187845,
952 + "share_a": 0.12695205853445451,
953 + "share_ref": 0.02964944850933712,
954 + "n": 18314
955 + },
956 + "vision_building_type_dist": {
957 + "maison_detachee": 0.676,
958 + "chalet": 0.145,
959 + "rangee": 0.127,
960 + "jumele": 0.028,
961 + "autre": 0.011,
962 + "terrain": 0.008,
963 + "condo_immeuble": 0.005
964 + }
965 + },
966 + "cv": {
967 + "random": {
968 + "L2": {
969 + "ols": {
970 + "all": {
971 + "r2": 0.6193226824222091,
972 + "mdape": 18.059509413349794,
973 + "n": 19123
974 + },
975 + "fr": {
976 + "r2": 0.6222349135800598,
977 + "mdape": 17.639819430710666,
978 + "n": 8529
979 + },
980 + "en": {
981 + "r2": 0.5617725991605782,
982 + "mdape": 18.41895835751976,
983 + "n": 8250
984 + },
985 + "mixed": {
986 + "r2": 0.515890303878296,
987 + "mdape": 18.312344553455468,
988 + "n": 2344
989 + },
990 + "realtor": {
991 + "r2": 0.7008096863999596,
992 + "mdape": 17.771964295157733,
993 + "n": 15356
994 + },
995 + "duproprio": {
996 + "r2": -0.08256652477430992,
997 + "mdape": 19.121574375743567,
998 + "n": 3767
999 + },
1000 + "realtor_no_dollar": {
1001 + "r2": 0.7019679871863057,
1002 + "mdape": 17.76991387527135,
1003 + "n": 14973
1004 + },
1005 + "realtor_dollar": {
1006 + "r2": 0.6265270737923495,
1007 + "mdape": 18.149953869938827,
1008 + "n": 383
1009 + }
1010 + },
1011 + "gbm": {
1012 + "all": {
1013 + "r2": 0.7288688475138769,
1014 + "mdape": 15.95094002848213,
1015 + "n": 19123
1016 + },
1017 + "fr": {
1018 + "r2": 0.6796449615633335,
1019 + "mdape": 15.91046276057202,
1020 + "n": 8529
1021 + },
1022 + "en": {
1023 + "r2": 0.7373780823763605,
1024 + "mdape": 16.027535537677895,
1025 + "n": 8250
1026 + },
1027 + "mixed": {
1028 + "r2": 0.5912440590283798,
1029 + "mdape": 16.015730662547156,
1030 + "n": 2344
1031 + },
1032 + "realtor": {
1033 + "r2": 0.7478808437303475,
1034 + "mdape": 15.945941339063008,
1035 + "n": 15356
1036 + },
1037 + "duproprio": {
1038 + "r2": 0.5636061847477183,
1039 + "mdape": 15.984568849587589,
1040 + "n": 3767
1041 + },
1042 + "realtor_no_dollar": {
1043 + "r2": 0.7489442194125158,
1044 + "mdape": 15.940505502981694,
1045 + "n": 14973
1046 + },
1047 + "realtor_dollar": {
1048 + "r2": 0.6805734307104678,
1049 + "mdape": 16.252144564034566,
1050 + "n": 383
1051 + }
1052 + }
1053 + },
1054 + "L3": {
1055 + "ols": {
1056 + "all": {
1057 + "r2": 0.6277548901387373,
1058 + "mdape": 17.877027385198495,
1059 + "n": 19123
1060 + },
1061 + "fr": {
1062 + "r2": 0.6339927443036132,
1063 + "mdape": 17.5684715187848,
1064 + "n": 8529
1065 + },
1066 + "en": {
1067 + "r2": 0.5683785039724047,
1068 + "mdape": 18.372824236151033,
1069 + "n": 8250
1070 + },
1071 + "mixed": {
1072 + "r2": 0.5297335176583204,
1073 + "mdape": 17.62364511137402,
1074 + "n": 2344
1075 + },
1076 + "realtor": {
1077 + "r2": 0.7069455112941738,
1078 + "mdape": 17.708227920321868,
1079 + "n": 15356
1080 + },
1081 + "duproprio": {
1082 + "r2": -0.05437331891905939,
1083 + "mdape": 18.468988905900087,
1084 + "n": 3767
1085 + },
1086 + "realtor_no_dollar": {
1087 + "r2": 0.7080450135924478,
1088 + "mdape": 17.70580384822257,
1089 + "n": 14973
1090 + },
1091 + "realtor_dollar": {
1092 + "r2": 0.6360774250740133,
1093 + "mdape": 17.911209797858852,
1094 + "n": 383
1095 + }
1096 + },
1097 + "gbm": {
1098 + "all": {
1099 + "r2": 0.7428875872402715,
1100 + "mdape": 15.421354827745482,
1101 + "n": 19123
1102 + },
1103 + "fr": {
1104 + "r2": 0.6944195218234042,
1105 + "mdape": 15.28189314238566,
1106 + "n": 8529
1107 + },
1108 + "en": {
1109 + "r2": 0.7521933355011332,
1110 + "mdape": 15.624521878883318,
1111 + "n": 8250
1112 + },
1113 + "mixed": {
1114 + "r2": 0.6133358277852637,
1115 + "mdape": 15.255826448803095,
1116 + "n": 2344
1117 + },
1118 + "realtor": {
1119 + "r2": 0.759893059204194,
1120 + "mdape": 15.486886827978152,
1121 + "n": 15356
1122 + },
1123 + "duproprio": {
1124 + "r2": 0.5949464270750067,
1125 + "mdape": 15.275745119720096,
1126 + "n": 3767
1127 + },
1128 + "realtor_no_dollar": {
1129 + "r2": 0.7605925812873651,
1130 + "mdape": 15.477011341450542,
1131 + "n": 14973
1132 + },
1133 + "realtor_dollar": {
1134 + "r2": 0.7126936210198105,
1135 + "mdape": 15.743669623374457,
1136 + "n": 383
1137 + }
1138 + }
1139 + },
1140 + "L4": {
1141 + "ols": {
1142 + "all": {
1143 + "r2": 0.6862361284601308,
1144 + "mdape": 16.886250549053305,
1145 + "n": 19123
1146 + },
1147 + "fr": {
1148 + "r2": 0.6817811040288533,
1149 + "mdape": 16.549619270034732,
1150 + "n": 8529
1151 + },
1152 + "en": {
1153 + "r2": 0.6475580327790671,
1154 + "mdape": 17.346864270432572,
1155 + "n": 8250
1156 + },
1157 + "mixed": {
1158 + "r2": 0.5785067390470824,
1159 + "mdape": 16.704384854470554,
1160 + "n": 2344
1161 + },
1162 + "realtor": {
1163 + "r2": 0.7457133652165874,
1164 + "mdape": 16.72452985313236,
1165 + "n": 15356
1166 + },
1167 + "duproprio": {
1168 + "r2": 0.1736323411744387,
1169 + "mdape": 17.501692059965524,
1170 + "n": 3767
1171 + },
1172 + "realtor_no_dollar": {
1173 + "r2": 0.7461598824670772,
1174 + "mdape": 16.719729060070964,
1175 + "n": 14973
1176 + },
1177 + "realtor_dollar": {
1178 + "r2": 0.7116091455076672,
1179 + "mdape": 17.533352727465633,
1180 + "n": 383
1181 + }
1182 + },
1183 + "gbm": {
1184 + "all": {
1185 + "r2": 0.7695283346486282,
1186 + "mdape": 14.51330744579874,
1187 + "n": 19123
1188 + },
1189 + "fr": {
1190 + "r2": 0.7253479216543696,
1191 + "mdape": 14.46031369865064,
1192 + "n": 8529
1193 + },
1194 + "en": {
1195 + "r2": 0.778264514148376,
1196 + "mdape": 14.643620058858497,
1197 + "n": 8250
1198 + },
1199 + "mixed": {
1200 + "r2": 0.6545270741350956,
1201 + "mdape": 14.378728382901501,
1202 + "n": 2344
1203 + },
1204 + "realtor": {
1205 + "r2": 0.7863682394550251,
1206 + "mdape": 14.525707627069483,
1207 + "n": 15356
1208 + },
1209 + "duproprio": {
1210 + "r2": 0.6232264858455447,
1211 + "mdape": 14.47846798385848,
1212 + "n": 3767
1213 + },
1214 + "realtor_no_dollar": {
1215 + "r2": 0.7868299233238455,
1216 + "mdape": 14.47554680491181,
1217 + "n": 14973
1218 + },
1219 + "realtor_dollar": {
1220 + "r2": 0.7530456374467777,
1221 + "mdape": 16.399813913922358,
1222 + "n": 383
1223 + }
1224 + }
1225 + },
1226 + "L4+lang": {
1227 + "ols": {
1228 + "all": {
1229 + "r2": 0.6918219402558234,
1230 + "mdape": 17.05842969137551,
1231 + "n": 19123
1232 + },
1233 + "fr": {
1234 + "r2": 0.6848202625030179,
1235 + "mdape": 16.44751046246582,
1236 + "n": 8529
1237 + },
1238 + "en": {
1239 + "r2": 0.6556500054964745,
1240 + "mdape": 17.969645215659618,
1241 + "n": 8250
1242 + },
1243 + "mixed": {
1244 + "r2": 0.5873961733720046,
1245 + "mdape": 16.318174197559664,
1246 + "n": 2344
1247 + },
1248 + "realtor": {
1249 + "r2": 0.7491992618321922,
1250 + "mdape": 16.889568595460148,
1251 + "n": 15356
1252 + },
1253 + "duproprio": {
1254 + "r2": 0.19727097971713214,
1255 + "mdape": 17.623612689876687,
1256 + "n": 3767
1257 + },
1258 + "realtor_no_dollar": {
1259 + "r2": 0.7497042048836837,
1260 + "mdape": 16.866036879518553,
1261 + "n": 14973
1262 + },
1263 + "realtor_dollar": {
1264 + "r2": 0.7120793153718732,
1265 + "mdape": 18.12518321567541,
1266 + "n": 383
1267 + }
1268 + },
1269 + "gbm": {
1270 + "all": {
1271 + "r2": 0.7739919121365599,
1272 + "mdape": 14.660130306531617,
1273 + "n": 19123
1274 + },
1275 + "fr": {
1276 + "r2": 0.7315945900254126,
1277 + "mdape": 14.340490957027995,
1278 + "n": 8529
1279 + },
1280 + "en": {
1281 + "r2": 0.7829445306690427,
1282 + "mdape": 15.041590592111891,
1283 + "n": 8250
1284 + },
1285 + "mixed": {
1286 + "r2": 0.6540433100870278,
1287 + "mdape": 14.606359395894236,
1288 + "n": 2344
1289 + },
1290 + "realtor": {
1291 + "r2": 0.7911014627627309,
1292 + "mdape": 14.672233578074334,
1293 + "n": 15356
1294 + },
1295 + "duproprio": {
1296 + "r2": 0.625414636946326,
1297 + "mdape": 14.607433277890847,
1298 + "n": 3767
1299 + },
1300 + "realtor_no_dollar": {
1301 + "r2": 0.7916939317014717,
1302 + "mdape": 14.613188884594253,
1303 + "n": 14973
1304 + },
1305 + "realtor_dollar": {
1306 + "r2": 0.750907388936689,
1307 + "mdape": 16.528013917646,
1308 + "n": 383
1309 + }
1310 + }
1311 + }
1312 + },
1313 + "spatial_fsa": {
1314 + "L2": {
1315 + "ols": {
1316 + "all": {
1317 + "r2": 0.561152632188157,
1318 + "mdape": 19.8122629574087,
1319 + "n": 19123
1320 + },
1321 + "fr": {
1322 + "r2": 0.5436228623065937,
1323 + "mdape": 19.850036519194923,
1324 + "n": 8529
1325 + },
1326 + "en": {
1327 + "r2": 0.5093877463581487,
1328 + "mdape": 20.06619110315462,
1329 + "n": 8250
1330 + },
1331 + "mixed": {
1332 + "r2": 0.45214336918664666,
1333 + "mdape": 18.980286279205327,
1334 + "n": 2344
1335 + },
1336 + "realtor": {
1337 + "r2": 0.6383752583912143,
1338 + "mdape": 19.80884641164214,
1339 + "n": 15356
1340 + },
1341 + "duproprio": {
1342 + "r2": -0.10464893757308857,
1343 + "mdape": 19.82009565718741,
1344 + "n": 3767
1345 + },
1346 + "realtor_no_dollar": {
1347 + "r2": 0.6404969934965723,
1348 + "mdape": 19.802748905552157,
1349 + "n": 14973
1350 + },
1351 + "realtor_dollar": {
1352 + "r2": 0.5096440585931159,
1353 + "mdape": 20.478078816372538,
1354 + "n": 383
1355 + }
1356 + },
1357 + "gbm": {
1358 + "all": {
1359 + "r2": 0.6900603053345207,
1360 + "mdape": 17.36473761406859,
1361 + "n": 19123
1362 + },
1363 + "fr": {
1364 + "r2": 0.6421621168538105,
1365 + "mdape": 17.42109930626754,
1366 + "n": 8529
1367 + },
1368 + "en": {
1369 + "r2": 0.688718243018579,
1370 + "mdape": 17.578917479700877,
1371 + "n": 8250
1372 + },
1373 + "mixed": {
1374 + "r2": 0.5626362403363072,
1375 + "mdape": 16.720707329024247,
1376 + "n": 2344
1377 + },
1378 + "realtor": {
1379 + "r2": 0.7084391615866306,
1380 + "mdape": 17.589008031592723,
1381 + "n": 15356
1382 + },
1383 + "duproprio": {
1384 + "r2": 0.5299069645374576,
1385 + "mdape": 16.726463097764288,
1386 + "n": 3767
1387 + },
1388 + "realtor_no_dollar": {
1389 + "r2": 0.7096364090475892,
1390 + "mdape": 17.51468334836437,
1391 + "n": 14973
1392 + },
1393 + "realtor_dollar": {
1394 + "r2": 0.6323550861606284,
1395 + "mdape": 19.93521616887506,
1396 + "n": 383
1397 + }
1398 + }
1399 + },
1400 + "L3": {
1401 + "ols": {
1402 + "all": {
1403 + "r2": 0.5683290219864456,
1404 + "mdape": 19.696362770568044,
1405 + "n": 19123
1406 + },
1407 + "fr": {
1408 + "r2": 0.5575537822847219,
1409 + "mdape": 19.696362770568044,
1410 + "n": 8529
1411 + },
1412 + "en": {
1413 + "r2": 0.5117836597388852,
1414 + "mdape": 20.17205138796668,
1415 + "n": 8250
1416 + },
1417 + "mixed": {
1418 + "r2": 0.4651719300088949,
1419 + "mdape": 18.382741889852255,
1420 + "n": 2344
1421 + },
1422 + "realtor": {
1423 + "r2": 0.6447326358277227,
1424 + "mdape": 19.770188248697007,
1425 + "n": 15356
1426 + },
1427 + "duproprio": {
1428 + "r2": -0.09039036304700909,
1429 + "mdape": 19.168513527868257,
1430 + "n": 3767
1431 + },
1432 + "realtor_no_dollar": {
1433 + "r2": 0.6466015938683551,
1434 + "mdape": 19.77069646918952,
1435 + "n": 14973
1436 + },
1437 + "realtor_dollar": {
1438 + "r2": 0.5298926526744645,
1439 + "mdape": 19.47742298405968,
1440 + "n": 383
1441 + }
1442 + },
1443 + "gbm": {
1444 + "all": {
1445 + "r2": 0.703325356512492,
1446 + "mdape": 16.92646835300644,
1447 + "n": 19123
1448 + },
1449 + "fr": {
1450 + "r2": 0.6552480572864873,
1451 + "mdape": 17.055597496403728,
1452 + "n": 8529
1453 + },
1454 + "en": {
1455 + "r2": 0.7028836188129218,
1456 + "mdape": 17.028422588392417,
1457 + "n": 8250
1458 + },
1459 + "mixed": {
1460 + "r2": 0.5870853107740306,
1461 + "mdape": 16.184764318197765,
1462 + "n": 2344
1463 + },
1464 + "realtor": {
1465 + "r2": 0.7198598672622852,
1466 + "mdape": 17.108202343453787,
1467 + "n": 15356
1468 + },
1469 + "duproprio": {
1470 + "r2": 0.5590966224265139,
1471 + "mdape": 16.19589147436422,
1472 + "n": 3767
1473 + },
1474 + "realtor_no_dollar": {
1475 + "r2": 0.7208304950463407,
1476 + "mdape": 17.067268687449612,
1477 + "n": 14973
1478 + },
1479 + "realtor_dollar": {
1480 + "r2": 0.6564547166743768,
1481 + "mdape": 18.835113176242313,
1482 + "n": 383
1483 + }
1484 + }
1485 + },
1486 + "L4": {
1487 + "ols": {
1488 + "all": {
1489 + "r2": 0.6381422132480896,
1490 + "mdape": 18.49345368946045,
1491 + "n": 19123
1492 + },
1493 + "fr": {
1494 + "r2": 0.6211566818465292,
1495 + "mdape": 18.3535836331989,
1496 + "n": 8529
1497 + },
1498 + "en": {
1499 + "r2": 0.6003735880556736,
1500 + "mdape": 18.942228673010565,
1501 + "n": 8250
1502 + },
1503 + "mixed": {
1504 + "r2": 0.5290156505057976,
1505 + "mdape": 17.651464780500543,
1506 + "n": 2344
1507 + },
1508 + "realtor": {
1509 + "r2": 0.6962614111103841,
1510 + "mdape": 18.543247515599763,
1511 + "n": 15356
1512 + },
1513 + "duproprio": {
1514 + "r2": 0.13678723222681555,
1515 + "mdape": 18.1567191029959,
1516 + "n": 3767
1517 + },
1518 + "realtor_no_dollar": {
1519 + "r2": 0.6969711627496079,
1520 + "mdape": 18.54890748611919,
1521 + "n": 14973
1522 + },
1523 + "realtor_dollar": {
1524 + "r2": 0.6460055512251979,
1525 + "mdape": 18.517320831845442,
1526 + "n": 383
1527 + }
1528 + },
1529 + "gbm": {
1530 + "all": {
1531 + "r2": 0.734096966551949,
1532 + "mdape": 15.888054640539362,
1533 + "n": 19123
1534 + },
1535 + "fr": {
1536 + "r2": 0.6911138535952995,
1537 + "mdape": 15.919851278981433,
1538 + "n": 8529
1539 + },
1540 + "en": {
1541 + "r2": 0.7340026687012777,
1542 + "mdape": 15.992640300870665,
1543 + "n": 8250
1544 + },
1545 + "mixed": {
1546 + "r2": 0.6274103220648997,
1547 + "mdape": 15.54977512173467,
1548 + "n": 2344
1549 + },
1550 + "realtor": {
1551 + "r2": 0.7503670054946096,
1552 + "mdape": 16.021849103126563,
1553 + "n": 15356
1554 + },
1555 + "duproprio": {
1556 + "r2": 0.5923896983212749,
1557 + "mdape": 15.366955189063702,
1558 + "n": 3767
1559 + },
1560 + "realtor_no_dollar": {
1561 + "r2": 0.7509438656783993,
1562 + "mdape": 16.011707563208976,
1563 + "n": 14973
1564 + },
1565 + "realtor_dollar": {
1566 + "r2": 0.7094120414845118,
1567 + "mdape": 17.180879411943394,
1568 + "n": 383
1569 + }
1570 + }
1571 + }
1572 + },
1573 + "spatial_groups": 420
1574 + },
1575 + "blend": {
1576 + "gbm_v5_r2": 0.7695283346486282,
1577 + "nn_v5_r2_recovered": 0.7469006651629047,
1578 + "blend_v5_r2_file": 0.7785721006736874,
1579 + "gbm_v4_r2": 0.7428875872402715,
1580 + "nn_v4_r2": 0.7411448851921381,
1581 + "grid_v5": {
1582 + "0.0": 0.7469006651629047,
1583 + "0.3": 0.7707893506536754,
1584 + "0.4": 0.7754950296943983,
1585 + "0.5": 0.7785721006736874,
1586 + "0.6": 0.780020563591543,
1587 + "0.7": 0.7798404184479648,
1588 + "1.0": 0.7695283346486282
1589 + },
1590 + "grid_v4": {
1591 + "0.0": 0.7411448851921381,
1592 + "0.3": 0.7570425008571054,
1593 + "0.4": 0.7594131717834227,
1594 + "0.5": 0.7603195755620706,
1595 + "0.6": 0.7597617121930493,
1596 + "0.7": 0.7577395816763588,
1597 + "1.0": 0.7428875872402715
1598 + },
1599 + "blend_v5_by_group": {
1600 + "all": {
1601 + "r2": 0.7785721006736874,
1602 + "mdape": 13.983708727480337,
1603 + "n": 19123
1604 + },
1605 + "fr": {
1606 + "r2": 0.7454923406571599,
1607 + "mdape": 13.773788135957732,
1608 + "n": 8529
1609 + },
1610 + "en": {
1611 + "r2": 0.779432498811204,
1612 + "mdape": 14.17923446423391,
1613 + "n": 8250
1614 + },
1615 + "mixed": {
1616 + "r2": 0.669968204605575,
1617 + "mdape": 13.83430917757124,
1618 + "n": 2344
1619 + },
1620 + "realtor": {
1621 + "r2": 0.8016868730465586,
1622 + "mdape": 13.985264681378295,
1623 + "n": 15356
1624 + },
1625 + "duproprio": {
1626 + "r2": 0.5785377526665498,
1627 + "mdape": 13.958402264967651,
1628 + "n": 3767
1629 + },
1630 + "realtor_no_dollar": {
1631 + "r2": 0.8018784945115782,
1632 + "mdape": 13.974096909707988,
1633 + "n": 14973
1634 + },
1635 + "realtor_dollar": {
1636 + "r2": 0.7835409769902336,
1637 + "mdape": 15.124945766929043,
1638 + "n": 383
1639 + }
1640 + },
1641 + "nested_weight_r2": 0.7752721723613206
1642 + }
1643 +}
\ No newline at end of file
modified scripts/11_extras_v5_robustness.py +1 −1
@@ -7,7 +7,7 @@ warnings.filterwarnings("ignore")
7 7 plt.rcParams.update({"font.family":"serif","font.size":10,"axes.grid":True,
8 8 "grid.alpha":0.25,"axes.axisbelow":True,"figure.dpi":150,"savefig.bbox":"tight"})
9 9 C1,C2,C3="#3B6EA5","#C4462E","#4A8C5B"
10 −exec(open("v5_ml.py").read().split("kf=KFold")[0])
10 +exec(open("scripts/09_v5_ml_text_vision.py").read().split("kf=KFold")[0])
11 11 amen_=list(AMEN.keys())
12 12 bl=np.load("data/processed/v5_blend_oos.npy"); price=df["price"].values; y=np.log(price)
13 13 from sklearn.metrics import r2_score
modified scripts/12_ablation_extras.py +1 −1
@@ -9,7 +9,7 @@ from sklearn.model_selection import KFold, cross_val_predict
9 9 from sklearn.metrics import r2_score
10 10 warnings.filterwarnings("ignore")
11 11
12 −exec(open("v5_ml.py").read().split("kf=KFold")[0])
12 +exec(open("scripts/09_v5_ml_text_vision.py").read().split("kf=KFold")[0])
13 13 kf = KFold(n_splits=5, shuffle=True, random_state=42)
14 14 amen_ = list(AMEN.keys())
15 15
modified scripts/13_revision_extras.py +1 −1
@@ -17,7 +17,7 @@ from sklearn.metrics import r2_score
17 17 warnings.filterwarnings("ignore")
18 18
19 19 # pipeline v5 jusqu'avant la CV (construit df, AMEN, rv, gv, vv, NUM_v4, NUM_v5, MISS, cats, y, kf)
20 −exec(open("v5_ml.py").read().split("kf=KFold")[0])
20 +exec(open("scripts/09_v5_ml_text_vision.py").read().split("kf=KFold")[0])
21 21 amen_ = list(AMEN.keys())
22 22 out = {}
23 23
modified scripts/14_figures_v5.py +1 −1
@@ -16,7 +16,7 @@ plt.rcParams.update({"font.family":"serif","font.size":10,"axes.grid":True,
16 16 C_OLS,C_GBM,C_BL="#3B6EA5","#C4462E","#4A8C5B"
17 17
18 18 # build df via v5_ml prefix
19 −exec(open("v5_ml.py").read().split("kf=KFold")[0])
19 +exec(open("scripts/09_v5_ml_text_vision.py").read().split("kf=KFold")[0])
20 20 kf=KFold(n_splits=5,shuffle=True,random_state=42)
21 21 onehot=pd.get_dummies(df[cats],dtype=float).reset_index(drop=True)
22 22
modified scripts/16_make_tables_v5.py +3 −3
@@ -4,7 +4,7 @@
4 4 import numpy as np, pandas as pd, statsmodels.formula.api as smf, warnings
5 5 warnings.filterwarnings("ignore")
6 6 # construire df via le pipeline de v5_ml (jusqu'avant la CV)
7 −exec(open("v5_ml.py").read().split("kf=KFold")[0])
7 +exec(open("scripts/09_v5_ml_text_vision.py").read().split("kf=KFold")[0])
8 8 amen_=list(AMEN.keys())
9 9 df["ln_price"]=np.log(df["price"])
10 10 df["ln_sqft"]=np.log(df["sqft_final"].fillna(df.groupby("ptype")["sqft_final"].transform("median")).fillna(df["sqft_final"].median()))
@@ -111,8 +111,8 @@ models=r"""% Auto-generated v5
111 111 common seed (identical folds across rows). $R^2_{\text{OOS}}$ is on
112 112 $\ln(\text{Price})$; ``Median AE'' is the median absolute percentage error in
113 113 levels (Equation~\ref{eq:cvmetrics}). Panel~A uses structural, amenity, room-detail,
114 − listing-signal, and LLM text-derived features; Panel~B adds the twelve façade
115 − vision attributes. City, type, and source enter as one-hot indicators for the
114 + listing-signal, and LLM text-derived features; Panel~B adds the façade
115 + vision layer (21 regressors derived from the twelve schema fields). City, type, and source enter as one-hot indicators for the
116 116 linear and tree models and as learned embeddings for the neural networks. The tree
117 117 and neural models handle missing living area natively. The best model is the
118 118 text\,+\,vision blend.
added scripts/18_revision_language_leakage_spatial.py +284 −0
@@ -0,0 +1,284 @@
1 +#!/usr/bin/env python3
2 +# -*- coding: utf-8 -*-
3 +"""Révision scientifique 2026-10-17 (editorial board) — réanalyses locales, sans appel API.
4 +
5 + (a) langue de la description (FR / EN / mixte, règle de stop-words comme WP 2) et indicatrice de langue ;
6 + (b) fuite d'information : prix demandé et évaluation municipale présents dans le texte lu par le LLM
7 + (100 % des descriptions DuProprio contiennent « Asking Price $… ») -> gains de la couche texte par source,
8 + par présence d'un montant en dollars ;
9 + (c) effets fixes plus fins (RTA = 3 premiers caractères du code postal) et validation spatiale
10 + (GroupKFold par RTA) vs 5-fold aléatoire ;
11 + (d) bootstrap en grappes (municipalités) des coefficients IA du modèle A ;
12 + (e) accord inter-sources des extractions (LLM vs mots-clés / champs structurés ; vision vs texte / structuré) ;
13 + (f) sensibilité du blend au poids w et vérification du 50/50.
14 +
15 +Résultats -> results/revision_2026-10-17.json ; les tables LaTeX (paper/tables/rob_*.tex, agreement.tex) sont ensuite
16 +générées par scripts/19_make_revision_tables.py à partir du JSON.
17 +Exécution : ~3 min sur un Mac Apple Silicon M-series (9 validations croisées GBM, 299 réplications bootstrap).
18 +"""
19 +import json, re, time, warnings
20 +import numpy as np, pandas as pd
21 +import statsmodels.formula.api as smf
22 +from sklearn.linear_model import LinearRegression
23 +from sklearn.ensemble import HistGradientBoostingRegressor
24 +from sklearn.model_selection import KFold, GroupKFold, cross_val_predict
25 +from sklearn.metrics import r2_score
26 +warnings.filterwarnings("ignore")
27 +T0 = time.time()
28 +
29 +# pipeline v5 jusqu'avant la CV : df, y, data, specs, facade, AMEN, rv, gv, vv, NUM_v4, NUM_v5, cats
30 +exec(open("scripts/09_v5_ml_text_vision.py").read().split("kf=KFold")[0])
31 +amen_ = list(AMEN.keys())
32 +y32 = y.copy() # float32, exactly as in scripts 09/12 (keeps the GBM fits identical to the paper's tables)
33 +y = y.astype(np.float64)
34 +OUT = {}
35 +
36 +# ------------------------------------------------------------------ texte, langue, code postal
37 +meta = {}
38 +for d in data:
39 + uid = f"{d.get('source')}:{d.get('listingId')}"
40 + meta[uid] = ((d.get("description") or ""), ((d.get("address") or {}).get("postalCode") or ""))
41 +desc = df["uid"].map(lambda u: meta[u][0])
42 +pc = df["uid"].map(lambda u: meta[u][1]).str.upper().str.replace(" ", "", regex=False)
43 +df["fsa"] = pc.str[:3].where(pc.str.len() >= 3, "")
44 +
45 +EN = set("the and with this that for from has have is are of to in on your you its it be by".split())
46 +FR = set("le la les et avec cette ce pour des une un du au aux est sont vous votre dans sur qui que".split())
47 +def detect_language(t):
48 + w = re.findall(r"[a-zàâçéèêëîïôûùüÿœ']+", t.lower())
49 + e = sum(1 for x in w if x in EN); f = sum(1 for x in w if x in FR)
50 + if e == 0 and f == 0: return "mixed"
51 + if e >= 1.5 * f: return "en"
52 + if f >= 1.5 * e: return "fr"
53 + return "mixed"
54 +df["lang"] = [detect_language(t) for t in desc]
55 +df["lang_en"] = (df["lang"] == "en").astype(int); df["lang_mixed"] = (df["lang"] == "mixed").astype(int)
56 +df["desc_len"] = desc.str.len().clip(upper=6000) / 1000.0
57 +
58 +DOLLAR = re.compile(r"\d[\d\s,.]{3,}\s?\$|\$\s?\d[\d,.]{3,}")
59 +ASSESS = re.compile(r"(évaluation municipale|evaluation municipale|valeur municipale|municipal assessment|municipal (evaluation|valuation)|évalu[ée]e? à|assessed at)", re.I)
60 +ASKING = re.compile(r"Asking Price\s*\$?\s*([\d,]+)", re.I)
61 +df["price_in_text"] = [int(bool(DOLLAR.search(t))) for t in desc]
62 +df["assess_in_text"] = [int(bool(ASSESS.search(t))) for t in desc]
63 +ask = [ASKING.search(t) for t in desc]
64 +df["asking_in_text"] = [int(m is not None) for m in ask]
65 +eq = sum(1 for m, p in zip(ask, df["price"]) if m and int(m.group(1).replace(",", "")) == int(p))
66 +OUT["text_leakage"] = {
67 + "n": int(len(df)),
68 + "asking_price_in_text": int(df["asking_in_text"].sum()),
69 + "asking_price_equals_list_price": int(eq),
70 + "asking_by_source": df.groupby("source")["asking_in_text"].sum().astype(int).to_dict(),
71 + "dollar_amount_in_text": int(df["price_in_text"].sum()),
72 + "dollar_by_source": df.groupby("source")["price_in_text"].mean().round(4).to_dict(),
73 + "assessment_mention": int(df["assess_in_text"].sum()),
74 + "assessment_by_source": df.groupby("source")["assess_in_text"].mean().round(4).to_dict(),
75 + "realtor_dollar_share": float(df.loc[df.source == "realtor", "price_in_text"].mean()),
76 +}
77 +print("leakage:", OUT["text_leakage"])
78 +
79 +lang_tab = (df.assign(lnp=np.log(df["price"]))
80 + .groupby(["source", "lang"]).agg(n=("price", "size"), median_price=("price", "median"),
81 + mean_lnp=("lnp", "mean"), desc_len=("desc_len", "mean"))
82 + .reset_index())
83 +OUT["language"] = {"by_source": lang_tab.to_dict(orient="records"),
84 + "share_en_overall": float(df["lang_en"].mean()),
85 + "share_en_realtor": float(df.loc[df.source == "realtor", "lang_en"].mean()),
86 + "share_by_lang": df["lang"].value_counts(normalize=True).round(4).to_dict()}
87 +print(lang_tab)
88 +
89 +# ------------------------------------------------------------------ coverage of AI layers
90 +df["has_specs"] = df["uid"].map(lambda u: int(u in specs)); df["has_facade"] = df["uid"].map(lambda u: int(u in facade))
91 +OUT["coverage"] = {"specs": int(df.has_specs.sum()), "facade": int(df.has_facade.sum()),
92 + "is_exterior": int(df.v_is_ext.sum()), "n": int(len(df))}
93 +print("coverage:", OUT["coverage"])
94 +
95 +# ------------------------------------------------------------------ OLS helpers
96 +df_cv = df.copy() # pristine copy (NaN kept) for the tree models; the OLS block below imputes df in place
97 +df["ln_price"] = np.log(df["price"])
98 +df["ln_sqft"] = np.log(df["sqft_final"].fillna(df.groupby("ptype")["sqft_final"].transform("median")).fillna(df["sqft_final"].median()))
99 +df["ln_lot"] = np.log(df["lot_sqft"].fillna(df["lot_sqft"].median())); df["has_lot"] = df["lot_sqft"].notna().astype(int)
100 +df["has_age"] = df["age"].notna().astype(int)
101 +for c in ["beds", "baths", "storeys", "age", "age2"]:
102 + df[c] = df[c].fillna(df.groupby("ptype")[c].transform("median")).fillna(df[c].median())
103 +df["parking"] = df["parking"].fillna(0)
104 +vc = df["fsa"].value_counts(); keepf = set(vc[(vc >= 20)].index) - {""}
105 +df["fsa_fe"] = df["fsa"].where(df["fsa"].isin(keepf), "__other__")
106 +OUT["fsa"] = {"n_fsa_raw": int(df.loc[df.fsa != "", "fsa"].nunique()), "n_fsa_fe": int(df["fsa_fe"].nunique()),
107 + "n_city_fe": int(df["city_fe"].nunique())}
108 +print("FSA:", OUT["fsa"])
109 +
110 +CORE = ("ln_lot+has_lot+beds+baths+parking+storeys+age+age2+has_age+C(source)+C(ptype_fe)+"
111 + + "+".join(amen_) + "+n_images_c+virtual_tour+" + "+".join(rv))
112 +TEXT = "+".join(gv); VIS = "+".join(vv)
113 +def form(fe="C(city_fe)", area=True, text=True, vis=True, extra=""):
114 + f = "ln_price ~ " + ("ln_sqft+" if area else "") + CORE
115 + if text: f += "+" + TEXT
116 + if vis: f += "+" + VIS
117 + if extra: f += "+" + extra
118 + return f + "+" + fe
119 +KEYS = ["v_curb", "v_reno", "v_luxury", "v_garage_vis", "v_mat_brique", "v_mat_pierre", "v_mat_vinyle", "v_set_rural",
120 + "v_land", "g_cond", "g_renovated_kitchen", "g_hardwood", "g_income_property", "lang_en", "lang_mixed"]
121 +def grab(m):
122 + return {k: {"b": float(m.params[k]), "se": float(m.bse[k]), "p": float(m.pvalues[k])} for k in KEYS if k in m.params.index}
123 +def fit(f, d, groups):
124 + return smf.ols(f, data=d).fit(cov_type="cluster", cov_kwds={"groups": d[groups]})
125 +def ablation(d, fe="C(city_fe)", area=True):
126 + r = {}
127 + for nm, (t, v) in {"base": (False, False), "plus_text": (True, False), "plus_text_vision": (True, True)}.items():
128 + r[nm] = float(smf.ols(form(fe, area, t, v), data=d).fit().rsquared)
129 + r["n"] = int(len(d)); r["d_text"] = r["plus_text"] - r["base"]; r["d_vision"] = r["plus_text_vision"] - r["plus_text"]
130 + return r
131 +
132 +dfA = df[df["has_sqft"] == 1].copy()
133 +
134 +# ------------------------------------------------------------------ (a) language indicator
135 +mA = fit(form(), dfA, "city_fe")
136 +mA_lang = fit(form(extra="lang_en+lang_mixed+desc_len"), dfA, "city_fe")
137 +mB = fit(form(area=False), df, "city_fe")
138 +mB_lang = fit(form(area=False, extra="lang_en+lang_mixed+desc_len"), df, "city_fe")
139 +OUT["ols"] = {"A": {"r2": mA.rsquared, "n": int(mA.nobs), "coef": grab(mA)},
140 + "A_lang": {"r2": mA_lang.rsquared, "n": int(mA_lang.nobs), "coef": grab(mA_lang)},
141 + "B": {"r2": mB.rsquared, "n": int(mB.nobs), "coef": grab(mB)},
142 + "B_lang": {"r2": mB_lang.rsquared, "n": int(mB_lang.nobs), "coef": grab(mB_lang)}}
143 +print("lang_en A:", OUT["ols"]["A_lang"]["coef"]["lang_en"], "| B:", OUT["ols"]["B_lang"]["coef"]["lang_en"])
144 +# raw language gap (no controls, city FE only)
145 +m_gap = smf.ols("ln_price ~ lang_en + lang_mixed + C(source) + C(city_fe)", data=df).fit()
146 +OUT["language"]["raw_gap_en_cityFE"] = float(m_gap.params["lang_en"])
147 +OUT["language"]["raw_gap_en_nocontrols"] = float(smf.ols("ln_price ~ lang_en + lang_mixed", data=df).fit().params["lang_en"])
148 +
149 +# in-sample ablation by language / source / price-in-text (model A, city FE)
150 +OUT["ablation_A"] = {"all": ablation(dfA),
151 + "fr": ablation(dfA[dfA.lang == "fr"]), "en": ablation(dfA[dfA.lang == "en"]),
152 + "mixed": ablation(dfA[dfA.lang == "mixed"]),
153 + "realtor": ablation(dfA[dfA.source == "realtor"]), "duproprio": ablation(dfA[dfA.source == "duproprio"]),
154 + "realtor_no_dollar": ablation(dfA[(dfA.source == "realtor") & (dfA.price_in_text == 0)]),
155 + "realtor_dollar": ablation(dfA[(dfA.source == "realtor") & (dfA.price_in_text == 1)]),
156 + "with_lang": {k: float(v) for k, v in zip(["base", "plus_text", "plus_text_vision"],
157 + [smf.ols(form(text=t, vis=v, extra="lang_en+lang_mixed+desc_len"), data=dfA).fit().rsquared
158 + for t, v in [(False, False), (True, False), (True, True)]])}}
159 +for k, v in OUT["ablation_A"].items(): print("ablation", k, {a: round(b, 4) for a, b in v.items()})
160 +
161 +# ------------------------------------------------------------------ (c) FSA fixed effects
162 +mA_fsa = fit(form(fe="C(fsa_fe)"), dfA, "fsa_fe"); mB_fsa = fit(form(fe="C(fsa_fe)", area=False), df, "fsa_fe")
163 +mA_fsa_lang = fit(form(fe="C(fsa_fe)", extra="lang_en+lang_mixed+desc_len"), dfA, "fsa_fe")
164 +OUT["ols"]["A_fsa"] = {"r2": mA_fsa.rsquared, "n": int(mA_fsa.nobs), "coef": grab(mA_fsa)}
165 +OUT["ols"]["A_fsa_lang"] = {"r2": mA_fsa_lang.rsquared, "n": int(mA_fsa_lang.nobs), "coef": grab(mA_fsa_lang)}
166 +OUT["ols"]["B_fsa"] = {"r2": mB_fsa.rsquared, "n": int(mB_fsa.nobs), "coef": grab(mB_fsa)}
167 +OUT["ablation_A"]["fsa_fe"] = ablation(dfA, fe="C(fsa_fe)")
168 +# Realtor-only with FSA FE + language (the cleanest specification: no price in text)
169 +dR = dfA[dfA.source == "realtor"]
170 +mA_realtor = fit(form(fe="C(fsa_fe)", extra="lang_en+lang_mixed+desc_len"), dR, "fsa_fe")
171 +OUT["ols"]["A_realtor_fsa_lang"] = {"r2": mA_realtor.rsquared, "n": int(mA_realtor.nobs), "coef": grab(mA_realtor)}
172 +print("FSA A R2=%.4f B R2=%.4f realtor-FSA-lang R2=%.4f" % (mA_fsa.rsquared, mB_fsa.rsquared, mA_realtor.rsquared))
173 +
174 +# ------------------------------------------------------------------ (d) cluster (pairs) bootstrap, model A
175 +import patsy
176 +yA, XA = patsy.dmatrices(form(), data=dfA, return_type="dataframe")
177 +XA = XA.values; yA = yA.values.ravel(); cols = list(patsy.dmatrices(form(), data=dfA, return_type="dataframe")[1].columns)
178 +groups = dfA["city_fe"].values; ug = np.unique(groups); gidx = {g: np.where(groups == g)[0] for g in ug}
179 +rng = np.random.RandomState(20261017); B = 299; keep_ix = [cols.index(k) for k in KEYS if k in cols]
180 +boot = np.full((B, len(keep_ix)), np.nan)
181 +for b in range(B):
182 + samp = rng.choice(ug, size=len(ug), replace=True)
183 + rows = np.concatenate([gidx[g] for g in samp])
184 + Xb = XA[rows]; yb = yA[rows]
185 + nz = Xb.std(0) > 0; nz[0] = True
186 + beta = np.full(XA.shape[1], np.nan)
187 + beta[nz] = np.linalg.lstsq(Xb[:, nz], yb, rcond=None)[0]
188 + boot[b] = beta[keep_ix]
189 +OUT["cluster_bootstrap_A"] = {"B": B, "n_clusters": int(len(ug)),
190 + "ci95": {k: [float(np.nanpercentile(boot[:, j], 2.5)), float(np.nanpercentile(boot[:, j], 97.5))]
191 + for j, k in enumerate([k for k in KEYS if k in cols])},
192 + "se": {k: float(np.nanstd(boot[:, j], ddof=1)) for j, k in enumerate([k for k in KEYS if k in cols])}}
193 +print("bootstrap CI:", {k: [round(a, 4) for a in v] for k, v in OUT["cluster_bootstrap_A"]["ci95"].items()})
194 +
195 +# ------------------------------------------------------------------ (e) agreement between extraction sources
196 +def agree(a, b):
197 + a = np.asarray(a).astype(int); b = np.asarray(b).astype(int)
198 + po = float(np.mean(a == b)); pe = float(np.mean(a) * np.mean(b) + (1 - np.mean(a)) * (1 - np.mean(b)))
199 + kappa = (po - pe) / (1 - pe) if pe < 1 else np.nan
200 + tp = int(((a == 1) & (b == 1)).sum()); fp = int(((a == 1) & (b == 0)).sum()); fn = int(((a == 0) & (b == 1)).sum())
201 + prec = tp / (tp + fp) if tp + fp else np.nan; rec = tp / (tp + fn) if tp + fn else np.nan
202 + return {"agreement": po, "kappa": kappa, "precision_vs_ref": prec, "recall_vs_ref": rec,
203 + "share_a": float(np.mean(a)), "share_ref": float(np.mean(b)), "n": int(len(a))}
204 +ds = df[df.has_specs == 1]
205 +OUT["agreement"] = {"llm_vs_keyword": {
206 + "garage": agree(ds.g_garage, ds.garage), "pool": agree(ds.g_pool, ds.pool), "waterfront": agree(ds.g_waterfront, ds.waterfront),
207 + "fireplace": agree(ds.g_fireplace, ds.fireplace), "central_ac": agree(ds.g_central_ac, ds.central_air),
208 + "finished_basement": agree(ds.g_finished_basement, ds.finished_bsmt)}}
209 +dv = df[(df.has_facade == 1) & (df.v_is_ext == 1) & (df.has_specs == 1)]
210 +OUT["agreement"]["vision_vs_text"] = {"garage_visible_vs_llm_garage": agree(dv.v_garage_vis, dv.g_garage),
211 + "garage_visible_vs_keyword_garage": agree(dv.v_garage_vis, dv.garage),
212 + "waterfront_setting_vs_keyword": agree(dv.v_set_bord_eau, dv.waterfront)}
213 +# vision storeys vs structured storeys ; vision building type vs listing type
214 +sv = dv["uid"].map(lambda u: facade[u].get("storeys_visual")); st = dv["storeys"]
215 +m = sv.isin(["1", "1.5", "2", "3+"]) & st.notna()
216 +def st_cat(x): return "1" if x < 1.25 else "1.5" if x < 1.75 else "2" if x < 2.75 else "3+"
217 +OUT["agreement"]["vision_storeys_vs_structured"] = {"n": int(m.sum()),
218 + "exact": float(np.mean(sv[m].values == st[m].map(st_cat).values)),
219 + "within_half_storey": float(np.mean(np.abs(sv[m].map({"1": 1, "1.5": 1.5, "2": 2, "3+": 3}).values - st[m].clip(upper=3).values) <= 0.5))}
220 +bt = dv["uid"].map(lambda u: facade[u].get("building_type_visual"))
221 +row_types = {"Row / Townhouse", "Townhouse"}
222 +is_row = dv["ptype"].isin(row_types).astype(int); vis_row = (bt == "rangee").astype(int)
223 +OUT["agreement"]["vision_rowhouse_vs_listing_type"] = agree(vis_row, is_row)
224 +OUT["agreement"]["vision_building_type_dist"] = bt.value_counts(normalize=True).round(3).to_dict()
225 +print("agreement:", json.dumps(OUT["agreement"], indent=0, default=float)[:1500])
226 +
227 +# ------------------------------------------------------------------ CV: random vs spatial (RTA) holdout, by subgroup
228 +kf = KFold(n_splits=5, shuffle=True, random_state=42)
229 +grp = df["fsa"].where(df["fsa"] != "", df["city_fe"]).values
230 +gkf = GroupKFold(n_splits=5)
231 +Xcat = pd.get_dummies(df[cats], dtype=float).reset_index(drop=True)
232 +dfi = df.copy()
233 +for c in ["sqft_final", "lot_sqft", "beds", "baths", "storeys", "age", "age2"]:
234 + dfi[c] = dfi.groupby("ptype")[c].transform(lambda s: s.fillna(s.median())); dfi[c] = dfi[c].fillna(dfi[c].median())
235 +dfi["parking"] = dfi["parking"].fillna(0)
236 +def stats(pred, mask=None):
237 + mask = np.ones(len(y), bool) if mask is None else mask
238 + e = np.abs(np.exp(pred[mask] - y[mask]) - 1)
239 + return {"r2": float(r2_score(y[mask], pred[mask])), "mdape": float(np.median(e) * 100), "n": int(mask.sum())}
240 +def cv_ols(numcols, cv, groups=None):
241 + X = pd.concat([dfi[numcols].reset_index(drop=True), Xcat], axis=1); X = X.fillna(X.median())
242 + return cross_val_predict(LinearRegression(), X, y, cv=cv, groups=groups, n_jobs=-1)
243 +def cv_gbm(numcols, cv, groups=None):
244 + X = pd.concat([df_cv[numcols].reset_index(drop=True), Xcat], axis=1)
245 + m = HistGradientBoostingRegressor(max_iter=600, learning_rate=0.05, max_leaf_nodes=63, l2_regularization=1.0, random_state=42)
246 + return cross_val_predict(m, X, y32, cv=cv, groups=groups, n_jobs=-1).astype(np.float64)
247 +L1 = ["sqft_final", "has_sqft", "lot_sqft", "beds", "baths", "parking", "storeys", "age", "age2"] + amen_
248 +L2 = L1 + ["n_images_c", "virtual_tour"] + rv; L3 = L2 + gv; L4 = L3 + vv
249 +L4L = L4 + ["lang_en", "lang_mixed", "desc_len"]
250 +masks = {"all": np.ones(len(y), bool), "fr": (df.lang == "fr").values, "en": (df.lang == "en").values,
251 + "mixed": (df.lang == "mixed").values, "realtor": (df.source == "realtor").values,
252 + "duproprio": (df.source == "duproprio").values,
253 + "realtor_no_dollar": ((df.source == "realtor") & (df.price_in_text == 0)).values,
254 + "realtor_dollar": ((df.source == "realtor") & (df.price_in_text == 1)).values}
255 +OUT["cv"] = {"random": {}, "spatial_fsa": {}}
256 +for lname, cols in [("L2", L2), ("L3", L3), ("L4", L4), ("L4+lang", L4L)]:
257 + po = cv_ols(cols, kf); pg = cv_gbm(cols, kf)
258 + OUT["cv"]["random"][lname] = {"ols": {k: stats(po, m) for k, m in masks.items()}, "gbm": {k: stats(pg, m) for k, m in masks.items()}}
259 + print(f"random {lname}: OLS {OUT['cv']['random'][lname]['ols']['all']} GBM {OUT['cv']['random'][lname]['gbm']['all']} [{time.time()-T0:.0f}s]")
260 +for lname, cols in [("L2", L2), ("L3", L3), ("L4", L4)]:
261 + po = cv_ols(cols, gkf, grp); pg = cv_gbm(cols, gkf, grp)
262 + OUT["cv"]["spatial_fsa"][lname] = {"ols": {k: stats(po, m) for k, m in masks.items()}, "gbm": {k: stats(pg, m) for k, m in masks.items()}}
263 + print(f"spatial {lname}: OLS {OUT['cv']['spatial_fsa'][lname]['ols']['all']} GBM {OUT['cv']['spatial_fsa'][lname]['gbm']['all']} [{time.time()-T0:.0f}s]")
264 +OUT["cv"]["spatial_groups"] = int(len(np.unique(grp)))
265 +
266 +# ------------------------------------------------------------------ (f) blend weights
267 +g5 = cv_gbm(NUM_v5, kf); b5 = np.load("data/processed/v5_blend_oos.npy").astype(np.float64); n5 = 2 * b5 - g5
268 +g4 = cv_gbm(NUM_v4, kf); n4 = np.load("data/processed/nn_robust_oos.npy").astype(np.float64)
269 +OUT["blend"] = {"gbm_v5_r2": float(r2_score(y, g5)), "nn_v5_r2_recovered": float(r2_score(y, n5)),
270 + "blend_v5_r2_file": float(r2_score(y, b5)), "gbm_v4_r2": float(r2_score(y, g4)), "nn_v4_r2": float(r2_score(y, n4)),
271 + "grid_v5": {str(w): float(r2_score(y, w * g5 + (1 - w) * n5)) for w in [0.0, 0.3, 0.4, 0.5, 0.6, 0.7, 1.0]},
272 + "grid_v4": {str(w): float(r2_score(y, w * g4 + (1 - w) * n4)) for w in [0.0, 0.3, 0.4, 0.5, 0.6, 0.7, 1.0]},
273 + "blend_v5_by_group": {k: stats(b5, m) for k, m in masks.items()}}
274 +# optimal stacking weight by nested CV (weight chosen on 4 folds, applied to the 5th)
275 +wgrid = np.linspace(0, 1, 21); pred_nested = np.zeros_like(y)
276 +for tr, te in kf.split(y):
277 + wbest = wgrid[np.argmax([r2_score(y[tr], w * g5[tr] + (1 - w) * n5[tr]) for w in wgrid])]
278 + pred_nested[te] = wbest * g5[te] + (1 - wbest) * n5[te]
279 +OUT["blend"]["nested_weight_r2"] = float(r2_score(y, pred_nested))
280 +print("blend:", {k: v for k, v in OUT["blend"].items() if k != "blend_v5_by_group"})
281 +
282 +json.dump(OUT, open("results/revision_2026-10-17.json", "w"), indent=1, default=float)
283 +print(f"-> results/revision_2026-10-17.json ({time.time()-T0:.0f}s)")
284 +print("Next: python3 scripts/19_make_revision_tables.py")
added scripts/19_make_revision_tables.py +208 −0
@@ -0,0 +1,208 @@
1 +#!/usr/bin/env python3
2 +# -*- coding: utf-8 -*-
3 +"""Génère les tables LaTeX de la révision 2026-10-17 à partir de results/revision_2026-10-17.json
4 +(produit par scripts/18_revision_language_leakage_spatial.py). Aucun calcul : mise en forme seulement.
5 +Sorties : paper/tables/rob_language_source.tex, rob_spatial.tex, rob_ai_coefs.tex, agreement.tex."""
6 +import json
7 +OUT = json.load(open("results/revision_2026-10-17.json"))
8 +y = [0] * OUT["text_leakage"]["n"]
9 +
10 +def f3(x): return f"${x:.3f}$" if x < 0 else f"{x:.3f}"
11 +def pct(x): return f"{x*100:.1f}\\%"
12 +def num(n): return f"{n:,}".replace(",", "{,}")
13 +
14 +# ---- Table: language and text leakage (robustness of the AI layers) ----
15 +A = OUT["ablation_A"]; R = OUT["cv"]["random"]
16 +def row_abl(label, key):
17 + a = A[key]; return f" {label} & {num(a['n'])} & {f3(a['base'])} & {a['d_text']*100:+.1f} & {a['d_vision']*100:+.1f} \\\\"
18 +def row_cv(label, key, est):
19 + r2 = [R[L][est][key]["r2"] for L in ("L2", "L3", "L4")]; n = R["L2"][est][key]["n"]
20 + return f" {label} & {num(n)} & {f3(r2[0])} & {(r2[1]-r2[0])*100:+.1f} & {(r2[2]-r2[1])*100:+.1f} \\\\"
21 +tab = r"""% Auto-generated by scripts/18_revision_language_leakage_spatial.py (2026-10-17)
22 +\begin{table}[htbp]
23 + \centering
24 + \begin{threeparttable}
25 + \caption{Gains from the AI Feature Layers by Language, Platform, and Presence of a Price in the Text}
26 + \label{tab:rob_language_source}
27 + \small
28 + \begin{tabular}{@{}lcccc@{}}
29 + \toprule
30 + Subsample & $N$ & $R^2$ base & $\Delta R^2$ text (pp) & $\Delta R^2$ vision (pp) \\
31 + \midrule
32 + \multicolumn{5}{@{}l}{\textit{Panel A: In-sample $R^2$, Model~A (measured area), municipality FE}} \\[2pt]
33 +""" + "\n".join([row_abl("All listings", "all"), row_abl("\\quad French descriptions", "fr"),
34 + row_abl("\\quad English descriptions", "en"), row_abl("\\quad Mixed / bilingual", "mixed"),
35 + row_abl("\\quad Realtor.ca (no asking price in text)", "realtor"),
36 + row_abl("\\quad DuProprio (asking price in text)", "duproprio"),
37 + row_abl("\\quad Realtor.ca, no dollar amount in text", "realtor_no_dollar"),
38 + row_abl("All listings, RTA fixed effects", "fsa_fe")]) + r"""
39 + \addlinespace
40 + \multicolumn{5}{@{}l}{\textit{Panel B: Out-of-sample $R^2$ (5-fold, random), OLS, full sample}} \\[2pt]
41 +""" + "\n".join([row_cv("All listings", "all", "ols"), row_cv("\\quad French", "fr", "ols"), row_cv("\\quad English", "en", "ols"),
42 + row_cv("\\quad Realtor.ca", "realtor", "ols"), row_cv("\\quad DuProprio", "duproprio", "ols")]) + r"""
43 + \addlinespace
44 + \multicolumn{5}{@{}l}{\textit{Panel C: Out-of-sample $R^2$ (5-fold, random), gradient boosting, full sample}} \\[2pt]
45 +""" + "\n".join([row_cv("All listings", "all", "gbm"), row_cv("\\quad French", "fr", "gbm"), row_cv("\\quad English", "en", "gbm"),
46 + row_cv("\\quad Realtor.ca", "realtor", "gbm"), row_cv("\\quad DuProprio", "duproprio", "gbm")]) + r"""
47 + \bottomrule
48 + \end{tabular}
49 + \begin{tablenotes}[flushleft]
50 + \footnotesize
51 + \item \textit{Notes:} Panel~A re-estimates the nested in-sample ablation of Model~A (structural, amenity, room, and
52 + listing controls with municipality and type fixed effects; then $+$ LLM text layer; then $+$ façade vision layer) on
53 + each subsample; ``$\Delta R^2$'' is the gain, in percentage points, from adding the layer. Language is classified
54 + from French and English stop-word counts (ratio rule of 1.5; ``mixed'' listings are mostly DuProprio descriptions that
55 + concatenate an English translation and the French original). Every DuProprio description contains the asking price
56 + (``Asking Price \$\dots''); """ + f"{OUT['text_leakage']['realtor_dollar_share']*100:.1f}" + r"""\% of Realtor.ca
57 + descriptions contain some dollar amount (usually a municipal assessment, a renovation budget, or a tax; the 141
58 + Model~A listings concerned are too few for a separate regression). Panels~B and
59 + C evaluate the full-sample cross-validated predictions (Table~\ref{tab:ablation}, identical folds) within each
60 + subsample; the base is the structural $+$ amenity $+$ rooms $+$ listing specification. Within-subsample $R^2$
61 + values can be negative (DuProprio, OLS) when the pooled model is miscalibrated for the subsample.
62 + \end{tablenotes}
63 + \end{threeparttable}
64 +\end{table}
65 +"""
66 +open("paper/tables/rob_language_source.tex", "w").write(tab)
67 +
68 +# ---- Table: random vs spatial holdout ----
69 +S = OUT["cv"]["spatial_fsa"]
70 +def row_sp(label, est):
71 + r = [R[L][est]["all"]["r2"] for L in ("L2", "L3", "L4")]; s = [S[L][est]["all"]["r2"] for L in ("L2", "L3", "L4")]
72 + mr = [R[L][est]["all"]["mdape"] for L in ("L2", "L3", "L4")]; ms = [S[L][est]["all"]["mdape"] for L in ("L2", "L3", "L4")]
73 + return (f" {label} & {f3(r[0])} & {f3(r[1])} & {f3(r[2])} & {mr[2]:.1f}\\% & {f3(s[0])} & {f3(s[1])} & {f3(s[2])} & {ms[2]:.1f}\\% \\\\")
74 +tab = r"""% Auto-generated by scripts/18_revision_language_leakage_spatial.py (2026-10-17)
75 +\begin{table}[htbp]
76 + \centering
77 + \begin{threeparttable}
78 + \caption{Random versus Spatial (RTA-Grouped) Cross-Validation}
79 + \label{tab:rob_spatial}
80 + \small
81 + \begin{tabular}{@{}lcccc@{\hskip 0.8cm}cccc@{}}
82 + \toprule
83 + & \multicolumn{4}{c}{Random 5-fold} & \multicolumn{4}{c}{Spatial 5-fold (RTA groups)} \\
84 + \cmidrule(lr){2-5}\cmidrule(lr){6-9}
85 + Estimator & Base & $+$Text & $+$Vision & MdAPE & Base & $+$Text & $+$Vision & MdAPE \\
86 + \midrule
87 +""" + "\n".join([row_sp("OLS (linear hedonic)", "ols"), row_sp("Gradient boosting", "gbm")]) + r"""
88 + \bottomrule
89 + \end{tabular}
90 + \begin{tablenotes}[flushleft]
91 + \footnotesize
92 + \item \textit{Notes:} Out-of-sample $R^2$ on $\ln(\text{Price})$ and median absolute percentage error (MdAPE, for the
93 + full text\,$+$\,vision specification). ``Base'' is the structural $+$ amenity $+$ rooms $+$ listing specification; the
94 + next two columns add the LLM text layer and the façade vision layer. In the spatial design the """ + num(OUT["cv"]["spatial_groups"]) + r""" forward
95 + sortation areas (RTA, first three characters of the postal code) are assigned to five folds by \texttt{GroupKFold}, so
96 + no listing is predicted from a model that has seen a listing of the same RTA; municipality indicators of RTAs held
97 + out in their entirety are unobserved in training and the model falls back on the reference category. Random-fold
98 + entries may differ by $\pm0.001$ from Table~\ref{tab:ablation}, which comes from a separate run. $N=$""" + num(len(y)) + r""".
99 + \end{tablenotes}
100 + \end{threeparttable}
101 +\end{table}
102 +"""
103 +open("paper/tables/rob_spatial.tex", "w").write(tab)
104 +
105 +# ---- Table: robustness of AI coefficients ----
106 +O = OUT["ols"]; CB = OUT["cluster_bootstrap_A"]
107 +def star(p): return "$^{***}$" if p < .01 else "$^{**}$" if p < .05 else "$^{*}$" if p < .1 else ""
108 +def cellc(spec, k):
109 + c = O[spec]["coef"].get(k)
110 + if not c: return "", ""
111 + s = "$-$" if c["b"] < 0 else ""
112 + return f"{s}{abs(c['b']):.3f}{star(c['p'])}", f"({c['se']:.3f})"
113 +labels = [("v_curb", "Curb appeal (1--10)"), ("v_reno", "Renovation level (ord.)"), ("v_luxury", "Luxury (visual)"),
114 + ("v_garage_vis", "Garage visible"), ("v_mat_brique", "Brick façade"), ("v_mat_pierre", "Stone façade"),
115 + ("v_mat_vinyle", "Vinyl façade"), ("v_set_rural", "Rural setting"), ("v_land", "Landscaping (ord.)"),
116 + ("g_cond", "Condition (LLM, ord.)"), ("g_renovated_kitchen", "Reno.\\ kitchen (LLM)"), ("g_hardwood", "Hardwood floor (LLM)"),
117 + ("lang_en", "English description"), ("lang_mixed", "Mixed-language description")]
118 +specs_ = ["A", "A_lang", "A_fsa_lang", "A_realtor_fsa_lang"]
119 +L = []
120 +for k, lab in labels:
121 + cells = [cellc(s, k) for s in specs_]
122 + ci = CB["ci95"].get(k)
123 + cic = f"[${ci[0]:.3f}$, ${ci[1]:.3f}$]" if ci else ""
124 + L.append(f" {lab} & " + " & ".join(c[0] for c in cells) + f" & {cic} \\\\")
125 + L.append(" & " + " & ".join(c[1] for c in cells) + " & \\\\[2pt]")
126 +tab = r"""% Auto-generated by scripts/18_revision_language_leakage_spatial.py (2026-10-17)
127 +\begin{table}[htbp]
128 + \centering
129 + \begin{threeparttable}
130 + \caption{Robustness of the AI-Derived Implicit Prices (Model~A)}
131 + \label{tab:rob_ai_coefs}
132 + \footnotesize
133 + \setlength{\tabcolsep}{4pt}
134 + \begin{tabular}{@{}lccccc@{}}
135 + \toprule
136 + & (1) & (2) & (3) & (4) & (5) \\
137 + & Baseline & $+$Language & RTA FE & Realtor.ca only & Cluster bootstrap \\
138 + & & & $+$Language & RTA FE $+$Lang. & 95\% CI \\
139 + \midrule
140 +""" + "\n".join(L) + r"""
141 + \midrule
142 + Fixed effects & Municipality & Municipality & RTA & RTA & Municipality \\
143 + Observations & """ + " & ".join(num(O[s]["n"]) for s in specs_) + r""" & """ + num(O["A"]["n"]) + r""" \\
144 + $R^2$ & """ + " & ".join(f3(O[s]["r2"]) for s in specs_) + r""" & \\
145 + \bottomrule
146 + \end{tabular}
147 + \begin{tablenotes}[flushleft]
148 + \footnotesize
149 + \item \textit{Notes:} OLS estimates of Equation~\eqref{eq:hedonic} on the measured-area subsample with the full
150 + control set (coefficients of the controls not shown). Column~(1) is the baseline of Table~\ref{tab:regression}.
151 + Column~(2) adds indicators for English and mixed-language descriptions and the description length (thousands of
152 + characters). Column~(3) replaces the """ + str(OUT["fsa"]["n_city_fe"]) + r""" municipality fixed effects by """ + str(OUT["fsa"]["n_fsa_fe"]) + r""" forward-sortation-area (RTA) fixed
153 + effects (RTAs with at least 20 listings; the remainder pooled). Column~(4) restricts the sample to Realtor.ca
154 + listings, whose descriptions never contain the asking price. Standard errors in parentheses are clustered at the
155 + level of the fixed effects. Column~(5) reports the 2.5th and 97.5th percentiles of a pairs cluster bootstrap
156 + (""" + str(CB["B"]) + r""" replications, resampling the """ + str(CB["n_clusters"]) + r""" municipalities with replacement) for the baseline
157 + specification. $^{***}$, $^{**}$, $^{*}$: significance at 1\%, 5\%, 10\%.
158 + \end{tablenotes}
159 + \end{threeparttable}
160 +\end{table}
161 +"""
162 +open("paper/tables/rob_ai_coefs.tex", "w").write(tab)
163 +
164 +# ---- Table: agreement ----
165 +AG = OUT["agreement"]
166 +def row_ag(label, r):
167 + return (f" {label} & {num(r['n'])} & {pct(r['share_a'])} & {pct(r['share_ref'])} & {pct(r['agreement'])} & {r['kappa']:.2f} & "
168 + f"{pct(r['precision_vs_ref'])} & {pct(r['recall_vs_ref'])} \\\\")
169 +rows = [row_ag(l, AG["llm_vs_keyword"][k]) for k, l in [("garage", "Garage"), ("pool", "Swimming pool"), ("waterfront", "Waterfront"),
170 + ("fireplace", "Fireplace"), ("central_ac", "Central A/C or heat pump"),
171 + ("finished_basement", "Finished basement")]]
172 +rows2 = [row_ag("Garage visible vs.\\ garage (LLM)", AG["vision_vs_text"]["garage_visible_vs_llm_garage"]),
173 + row_ag("Garage visible vs.\\ garage (keyword)", AG["vision_vs_text"]["garage_visible_vs_keyword_garage"]),
174 + row_ag("Waterfront setting vs.\\ waterfront (keyword)", AG["vision_vs_text"]["waterfront_setting_vs_keyword"]),
175 + row_ag("Row house vs.\\ townhouse (listing type)", AG["vision_rowhouse_vs_listing_type"])]
176 +sto = AG["vision_storeys_vs_structured"]
177 +tab = r"""% Auto-generated by scripts/18_revision_language_leakage_spatial.py (2026-10-17)
178 +\begin{table}[htbp]
179 + \centering
180 + \begin{threeparttable}
181 + \caption{Agreement Between Machine-Generated Attributes and Independent Sources}
182 + \label{tab:agreement}
183 + \footnotesize
184 + \setlength{\tabcolsep}{3.5pt}
185 + \begin{tabular}{@{}lccccccc@{}}
186 + \toprule
187 + Attribute & $N$ & Share (AI) & Share (ref.) & Agreement & $\kappa$ & Precision & Recall \\
188 + \midrule
189 + \multicolumn{8}{@{}l}{\textit{Panel A: LLM text layer vs.\ bilingual keyword rule on the same text}} \\[2pt]
190 +""" + "\n".join(rows) + r"""
191 + \addlinespace
192 + \multicolumn{8}{@{}l}{\textit{Panel B: Façade vision layer vs.\ text or structured fields (exterior shots only)}} \\[2pt]
193 +""" + "\n".join(rows2) + r"""
194 + \bottomrule
195 + \end{tabular}
196 + \begin{tablenotes}[flushleft]
197 + \footnotesize
198 + \item \textit{Notes:} ``Share (AI)'' is the prevalence of the attribute according to the AI layer, ``Share (ref.)''
199 + according to the reference source; ``Agreement'' is the share of listings on which the two coincide, $\kappa$ is
200 + Cohen's kappa, and precision and recall treat the reference as ground truth (it is not: keyword rules miss
201 + negations and synonyms, and a garage need not be visible on the façade photograph). Visual storey count agrees
202 + exactly with the structured storey field for """ + f"{sto['exact']*100:.1f}" + r"""\% of """ + num(sto["n"]) + r""" listings and within half a storey for """ + f"{sto['within_half_storey']*100:.1f}" + r"""\%.
203 + \end{tablenotes}
204 + \end{threeparttable}
205 +\end{table}
206 +"""
207 +open("paper/tables/agreement.tex", "w").write(tab)
208 +print("-> paper/tables/rob_language_source.tex, rob_spatial.tex, rob_ai_coefs.tex, agreement.tex")
209