Changelog — révision scientifique complète (editorial board, 2026-10-17)
Papier : WP 11 — Half a Million Prices, Twenty Models (v1.0, August 2026 → v1.1, October 2026).
État de référence : tag git pre-revision-20261017, PDF docs/main_pre-revision-20261017.pdf (23 pages).
Rapport scientifique associé : docs/REVIEW-REPORT.md.
1. Corrections majeures (où / avant / après / pourquoi / preuve)
1.1 Règle de retransformation asymétrique → scoreboard uniforme (correction centrale)
- Où :
src/wp11/models.py(fit_predict,run_horserace), §4.3 Methodology, §5, §7.1, abstract, intro, conclusion. - Avant : les modèles linéaires en log (A2, A3, A5, A6, B*, C*, D1, D2) étaient multipliés par le facteur de
smearing de Duan (ŝ ≈ 1.06–1.08) ; les ensembles d'arbres (D3, D4, D5) et le k-NN (D6) ne l'étaient pas
(
np.exp(pred)nu). Les métriques log (RMSE_ln, R²_ln) étaient calculées sur la prédiction smearée pour une famille et non pour l'autre. - Après : règle « médiane » (inversion directe de la prédiction transformée, éq. 10) appliquée à tous les modèles pour le tableau principal ; règle « moyenne » (Duan, éq. 11, généralisée au Box–Cox par moyenne sur 1 000 résidus) calculée pour tous et rapportée en robustesse (Table 12).
- Pourquoi : la comparaison v1.0 opposait un A6 smearé (19.1 %) à un D4 non smearé (21.2 %) dans le split temporel — le « renversement » annoncé en titre de la v1.0 était un artefact : (i) résidus log asymétriques à gauche (skewness −1.4 à −1.8, médiane +0.06) donc le smearing abaisse par coïncidence la MdAPE de tout modèle log ; (ii) marché haussier en 2025-26 : tout facteur > 1 compense une partie de la staleness. Appliquée uniformément, aucune des deux règles ne renverse le classement (médiane : A6 21.7 vs D4 21.2 ; moyenne : 19.1 vs 18.4).
- Preuve :
results/reproduced/horserace_{random,temporal}.csv(colonnesmdapevsmdape_mean,smear),results/tables/retransformation.tex, médianes de résidus vérifiées surdata/processed/predictions_*.parquet. - Chiffres modifiés (ancien → nouveau, MdAPE %) — random : A1 18.2→18.2, A2 18.2→19.0, A3 17.0→18.0, A4 18.6→18.6, A5 18.1→19.2, A6 16.5→17.6, B1 21.1→22.0, B2 18.1→19.1, B4 18.3→19.3, C1 27.9→28.8, C3 17.5→18.7, C4 19.4→21.0, D2 16.7→17.7 ; D3/D4/D5/D6 inchangés (14.6/14.4/23.2/21.2). Forward : A1 20.4→20.4, A2 20.6→23.2, A3 19.2→21.8, A4 21.0→21.0, A5 20.5→23.3, A6 19.1→21.7, B1 25.4→29.3, B2 21.0→23.7, B4 20.3→22.9, C1 26.5→29.0, C3 21.3→24.3, C4 22.1→25.3, D2 19.5→22.3 ; D3/D4/D5/D6 inchangés (21.3/21.2/26.2/25.6). R²_ln des modèles log : +0.01 à +0.04 (random), −0.03 à −0.04 (forward).
- Conséquence narrative : « the ranking reverses » (v1.0) → « the gap shrinks from 3.2 to 0.5 points and loses significance ; relative skill transfers intact, the level does not » (v1.1). Abstract, §1, §5.5, §8, §9 réécrits.
1.2 Inférence statistique ajoutée (absente en v1.0)
- Avant : aucun intervalle, aucun test ; le README affirmait « design clustered by municipality » sans aucune implémentation.
- Après :
src/wp11/inference.py— bootstrap par grappes (municipalités, B = 500) pour la MdAPE de chaque modèle et pour les différences ; test de type Diebold–Mariano / Giacomini–White à erreurs-types groupées sur les pertes APE et log² (éq. 13). Tables 4 (tous les modèles vs A6) et 5 (13 paires ciblées). - Résultats clés : D4 vs A6 random −3.2 [−4.7, −1.9] (t = −3.6) ; forward −0.5 [−1.7, +0.6] (t = −1.6, p = 0.11) ; C3 vs C2 random −0.45 [−1.07, +0.37] (n.s.) ; C4 vs C2 +1.8 [+1.2, +2.3] ; B4 vs B3 +0.19 [+0.01, +0.37] ; D2 vs A6 +0.06 [−0.02, +0.16] (n.s.).
- Conséquence : la phrase « the 5.5 km grid beats municipality effects » est ramenée à « nominally better, within bootstrap noise » ; « month effects indistinguishable from quarter » précisé (légèrement pires, significatif).
1.3 Décomposition du « generalization gap » (nouveau)
- Comparaison à période fixe (modèles random évalués sur les 31 465 ventes 2025-26 de leur test) : D4 13.7 → 21.2 (coût de staleness 7.5), A6 17.0 → 21.7 (4.7), A1 17.1 → 20.4 (3.3). Biais de niveau médian forward : D4 +0.183, A6 +0.161, A1 +0.102. MdAPE re-nivelée forward : D4 13.9 vs A6 16.8 (écart 2.9 ≈ écart random 3.5). Par trimestre : avance D4 sur A6 de 1.5 pt (2025Q1) → 0 (2026Q3).
- Fichiers :
same_period.csv,relevel.csv,by_quarter.csv; Tables 8–9, Figure 6 (nouvelle).
1.4 Fuite d'information dans le split aléatoire (nouveau)
- Faits : 20.6 % des ventes sont des reventes d'une même parcelle ; 30.3 % partagent des coordonnées exactes avec
une autre vente (91.6 % des condos). Colonnes
parcel,coordajoutées àsample.py(reconstruction du parquet d'analyse, 514 212 ventes identiques). - Splits groupés (
split("group_parcel"),split("group_coord")) : course complète. Parcelle : D4 14.5 / A6 17.6 (≈ inchangé). Bâtiment : D4 14.6 / A6 17.8 (écart 3.2 identique) ; seul D6 se dégrade (21.2 → 22.3). Décomposition vu/non vu : D4 12.0 vs 15.3 (coord.), A6 17.8 vs 17.6 ; condos D4 8.9 vs 10.2. - Tables 13–14 ; §4.4, §7.2.
1.5 Effets spatiaux fins : « seventeen K sales » et repli arbitraire
- Avant (§5.3) : « its cells average seventeen K sales each » — faux : 514 212 / 30 548 = 16.8 ventes par cellule (médiane 3). Corrigé. Cellules non vues (2.5 % des ventes test) tarifées au niveau de la cellule de référence : désormais explicité.
- Nouveau : C3′ (muni + grille 5.5 km) et C4′ (muni + grille 1.1 km) — solution de norme minimale (ridge α→0)
≈ pooling partiel vers la municipalité. Random : C4′ 17.3 % (meilleure spécification linéaire du papier),
C3′ 17.7 % ; forward : 21.6 / 21.5 vs C2 23.3. Conclusion de l'axe C reformulée : « hierarchy, not coarseness ».
Table 15 ;
nested_fe.csv.
1.6 Box–Cox : grille fine
- Grille 0.05 sur [−0.25, 0.75] : λ̂ = 0.35 (profil plat 0.25–0.55 à la tolérance du solveur itératif) ;
MdAPE random 18.4 % (vs 18.6 % à 0.25).
boxcox_fine.csv. Texte §5.1 et §7.4.
1.7 Hyper-paramètres du boosting
- Quatre configurations alternatives (shallow, deep, régularisée, early stopping sur les 10 % derniers mois
d'entraînement) sous les deux splits.
hgb_sensitivity.csv, Table 16, §7.5. Random 14.3–14.7 % (baseline 14.4), forward 21.2–21.6 % (baseline 21.2) ; early stopping : 700 / 1 000 itérations, reproduit le baseline à 0.1 pt.
1.8 Statistiques descriptives incohérentes
- Avant (§3.2, README) : « Prices average $462,000 (median $399,000) ». Table 1 : 440 207 / 397 375.
Après : texte aligné sur la table ; ajout des 86 logements de classe indéterminée (la somme des classes v1.0
faisait 514 126 ≠ 514 212) ; répartition par année ; nettoyage des lots > P99.5 explicité (était silencieux
dans
sample.py) ; bornes âge/étages/logements explicitées.
1.9 Indices et profils : affirmations adoucies
- Avant : « all five [indices] agree to within a few index points » ; « curves sit within a tenth of a log
point ». Après : les quatre formes linéaires s'accordent à ≤ 2.5 points ; l'indice boosting finit 9 points
plus bas (160.5 vs 168.6–171.0 ; écart max 10.9) ; profil d'âge du boosting uniformément plus plat de 0.10–0.13
log-point à partir de 20 ans (−0.20 vs −0.31 à 40 ans), surface : ≤ 0.05. Chiffres vérifiés dans
index.csv,profiles.csv. - Learning curves recalculées (règle médiane) : OLS 19.1 → 19.2 (v1.0 : 18.4 → 18.1) ; GB 17.6 → 14.5 ; écart 1.6 → 4.7 pts (v1.0 : < 1 → 3.6). Segments : condo 18.2 vs 9.1 (v1.0 OLS 20.4), grandes villes 17.7 vs 11.9, petites municipalités 24.8 vs 18.7 (v1.0 23.3), chalets 34.3 vs 32.1 (v1.0 33.9).
1.10 Méthodologie formalisée
- §4 réécrit : équations (1)–(13) — fonction hédonique générale, six formes A1–A6, profil de vraisemblance Box–Cox (avec jacobien), ridge, règle k-NN, règles médiane/Duan, métriques, test DM groupé. Documentation du traitement des catégories non vues, du carry-forward et de son analogue pour les arbres (correction de la phrase « clamped month counter » : rien n'est écrêté dans le code ; les arbres sont constants par morceaux).
1.11 Autres corrections éditoriales
- « 13 % improvement » → « 18 % » (14.4 vs 17.6) ; « 13–21 % » → « 18–25 % » ; intro « 27.9 % versus 17.5 % » vs README « vs 18.1 % » harmonisé (28.8 vs 19.2, référence = backbone C2).
- Hiérarchie de la discussion : liste à puces → prose (règle de style du protocole).
- Mots-clés harmonisés au vocabulaire commun (« hedonic price function », « forward-in-time holdout », « automated valuation model »).
\WPversion1.0 → 1.1 ;\WPdateAugust 2026 → October 2026 ; page titre canonique intacte ; macro\companionpaperconservée (main.tex / main_anon.tex).
2. Références
Vérifiées (Crossref api.crossref.org/works?query.bibliographic=…, 2026-10-17) et corrigées
Toutes les entrées conservées de la v1.0 (31) ont été retrouvées sur Crossref et dotées de leur DOI. Corrections :
box1964analysis: pages 211–252 → 211–243 (244–252 = discussion).bogin2020house: number 1 → 1–2 ; DOI 10.1007/s11146-019-09712-0.silver2018house: l'article EURONA 2018 n'est pas indexé ; remplacé par la version vérifiée IMF Working Paper 16/213 (2016), DOI 10.5089/9781475552249.001, même titre (ordre des mots corrigé).ke2017lightgbm: @article → @inproceedings (NIPS 2017, non indexé Crossref ; actes Curran Associates).malpezzi2003hedonic: DOI 10.1002/9780470690680.ch5 ;anselin1988spatial: DOI 10.1007/978-94-015-7799-1.
Retirées (non citées dans le texte et bibliographiquement défectueuses)
knight2003listing(volume/pages vides ; en réalité chapitre Springer 2008, pp. 39–54).diewert2004hedonic(« Mimeo » en champ journal ; non indexé).
Ajoutées (27, toutes vérifiées sur Crossref, DOI dans le .bib)
hill2018geospatial (RIW 64(4) 737–756) · pace2020residuals (JREFE 60 170–180) · lorenz2023interpretable (REE 51(5) 1178–1208) · diewert2016tokyo (RSUE 60 300–315) · diewert2015decomposition (Econometric Reviews 34 106–126) · diebold1995comparing (JBES 13(3) 253–263) · giacomini2006tests (Econometrica 74(6) 1545–1578) · bergmeir2012crossvalidation (Information Sciences 191 192–213) · roberts2017crossvalidation (Ecography 40(8) 913–929) · kuminoff2010which (JEEM 60(3) 145–160) · bishop2020best (REEP 14(2) 260–281) · bourassa2007spatial (JREFE 35(2) 143–160) · bourassa2003submarkets (JHE 12(1) 12–28) · goodman2003segmentation (JHE 12(3) 181–201) · zurada2011comparison (JRER 33(3) 349–388) · antipov2012mass (ESWA 39(2) 1772–1778) · perezrave2019machine (J. Property Research 36(1) 59–96) · mccluskey2013prediction (JPR 30(4) 239–265) · schulz2014automated (JPR 31(2) 131–153) · fik2003modeling (REE 31(4) 623–646) · clapp2003semiparametric (JREFE 27(3) 303–320) · pace1998spatiotemporal (JREFE 17(1) 15–33) · dube2013pooled (LSRS 6(1) 1–18) · hill2009boom (JHE 18(3) 193–205) · manning2001estimating (J. Health Econ. 20(4) 461–494) · thibodeau2003marking (REE 31(1) 1–22) · khoshnoud2023evolution (JREL 31(1) 1–47). Total : 33 → 58 entrées (31 conservées + 27 ajoutées), toutes citées, toutes vérifiées.
3. Réanalyses exécutées (laptop, Apple Silicon 18 cœurs, Python 3.14 / scikit-learn 1.6.1)
| Script | Contenu | Durée | Sorties |
|---|---|---|---|
01_build_sample.py |
reconstruction de l'échantillon (+ clés parcel, coord) — 514 212 ventes identiques |
2 s | data/processed/analysis.parquet |
02_horserace.py |
20 modèles × 2 splits, règles médiane + Duan, prédictions sauvegardées | 3 min 28 s | horserace_*.csv, boxcox.csv, predictions_*.parquet (non commités) |
03_extensions.py |
indices, profils, learning curves, segments (règle médiane) | 4 min 18 s | index.csv, profiles.csv, learning.csv, segments.csv |
06_robustness.py (nouveau) |
bootstrap + DM, même période, par trimestre, fuite, re-nivellement, paires, splits groupés (2 courses complètes), FE emboîtés, Box–Cox fin, sensibilité HGB | 16 min 16 s | inference_*.csv, same_period.csv, by_quarter.csv, leakage.csv, relevel.csv, pairs.csv, horserace_group_*.csv, nested_fe.csv, boxcox_fine.csv, hgb_sensitivity.csv |
04_make_figures.py |
10 figures (+ fig_horizon.png) |
< 1 min | figures/ |
05_make_tables.py |
16 tables LaTeX (9 nouvelles) | < 1 s | results/tables/ |
Tous les nombres du texte proviennent de ces sorties ; aucune table n'est tapée à la main.
4. Éléments non modifiés / analyses recommandées non exécutées
- Modèle linéaire à surface spatiale (krigeage des résidus, splines bivariées en lat/lng, effets hiérarchiques bayésiens) : non estimé (hors cadre « 20 modèles ») — recommandé pour fermer l'écart linéaire/boosting par l'autre côté.
- Holdout spatialement bloqué (cellules entières en test) : non exécuté (change la question en extrapolation spatiale ; les FE spatiaux y sont par construction inopérants).
- Re-nivellement par un indice publié (hors échantillon, non oracle) : non exécuté (aucun indice externe chargé ; le re-nivellement présenté utilise le ratio médian du test, borne supérieure).
- Variante « médiane calibrée » (exp(ẑ + médiane des résidus d'entraînement)) : discutée, non tabulée.
- Fenêtres temporelles alternatives (p. ex. train < 2024, test 2024) : non exécutées — limite déclarée §8.4.
- Comparaison avec LightGBM / XGBoost natifs ou réseaux de neurones : hors périmètre.
- La règle de nettoyage des lots > P99.5 → 0 (héritée) est documentée mais conservée pour préserver l'échantillon.