Annexes B (formulaires d'inspection) et C (modèles de rapport) livrées ; fusion étendue aux annexes
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
13 changed files +734 −62
modified
analysis/python/cases/ch14_nettoyage.py
+26 −4
@@ -164,8 +164,13 @@ def regles(b: pd.DataFrame) -> dict: | ||
| 164 | 164 | df.loc[sans, "cle_unite"] = ("ATTR" + df.loc[sans, "secteur"] + df.loc[sans, "superficie_habitable_m2"].astype(str) + df.loc[sans, "superficie_terrain_m2"].astype(str) + df.loc[sans, "annee_construction"].astype(str)) |
| 165 | 165 | df = df.sort_values(["date_acte", "id_inscription"]) |
| 166 | 166 | dup = df.duplicated(subset=["cle_unite", "date_promesse", "prix_vente"], keep="last") |
| 167 | − _journal(j, df, df.index[dup], "R1", "exclusion", "doublon d'inscription (même unité, même promesse, même prix) : inscription la plus récente conservée") | |
| 168 | − df = df[~dup]; etapes.append(("R1 doublons", len(df))) | |
| 167 | + _journal(j, df, df.index[dup], "R1", "exclusion", "doublon d'inscription (même clé d'unité, même promesse, même prix) : inscription la plus récente conservée") | |
| 168 | + df = df[~dup] | |
| 169 | + # R1b — doublon par attributs (la clé peut être absente ou mal saisie sur l'une des deux inscriptions) | |
| 170 | + attr = ["secteur", "type", "superficie_habitable_m2", "superficie_terrain_m2", "annee_construction", "date_promesse", "prix_vente"] | |
| 171 | + dup2 = df.duplicated(subset=attr, keep="last") | |
| 172 | + _journal(j, df, df.index[dup2], "R1", "exclusion", "doublon d'inscription repéré par les attributs (clé d'unité absente ou discordante) : inscription la plus récente conservée") | |
| 173 | + df = df[~dup2]; etapes.append(("R1 doublons", len(df))) | |
| 169 | 174 | # R3 — meubles : le prix de l'immeuble est le prix déclaré moins la contrepartie des meubles (correction) |
| 170 | 175 | m = df["montant_meubles"].fillna(0) > 0; ids = df.index[m] |
| 171 | 176 | _journal(j, df, ids, "R3", "correction", "contrepartie des meubles retranchée du prix", "prix_vente", df.loc[ids, "prix_vente"].tolist(), |
@@ -389,7 +394,7 @@ def appariement_role(b: pd.DataFrame, role: pd.DataFrame, verite: pd.DataFrame | | ||
| 389 | 394 | t = b.copy(); r = role.copy() |
| 390 | 395 | r["cle"] = r["matricule"].map(normaliser_matricule) |
| 391 | 396 | t["cle"] = t["matricule"].map(normaliser_matricule) |
| 392 | − t["palier"] = ""; t["matricule_apparie"] = np.nan | |
| 397 | + t["palier"] = ""; t["matricule_apparie"] = pd.Series([None] * len(t), index=t.index, dtype=object) | |
| 393 | 398 | # palier 1 : exact |
| 394 | 399 | exact = t["matricule"].isin(r["matricule"]); t.loc[exact, "palier"] = "1 exact"; t.loc[exact, "matricule_apparie"] = t.loc[exact, "matricule"] |
| 395 | 400 | # palier 2 : normalisé |
@@ -425,6 +430,22 @@ def appariement_role(b: pd.DataFrame, role: pd.DataFrame, verite: pd.DataFrame | | ||
| 425 | 430 | out["table"] = t |
| 426 | 431 | return out |
| 427 | 432 | |
| 433 | +# ----------------------------------------------------------------------------- 5b. effet de masquage (illustration) | |
| 434 | + | |
| 435 | +def illustration_masquage(b: pd.DataFrame, seuil: float = SEUIL_MAD) -> dict: | |
| 436 | + """Sur le jeu après R1-R3 seulement (prix symboliques encore présents) : combien d'observations dépassent le seuil selon | |
| 437 | + le score classique (moyenne, écart-type) et selon le score robuste (médiane, MAD) du ln du prix au m² par secteur ?""" | |
| 438 | + df = b.copy(); df["date_promesse"] = pd.to_datetime(df["date_promesse"]); df["date_acte"] = pd.to_datetime(df["date_acte"]) | |
| 439 | + df["prix_net"] = df["prix_vente"] - df["montant_meubles"].fillna(0) | |
| 440 | + df["lpm"] = np.log(df["prix_net"].clip(lower=1) / df["superficie_habitable_m2"]) | |
| 441 | + out = [] | |
| 442 | + for sct, g in df.groupby("secteur"): | |
| 443 | + x = g["lpm"].values; zc = (x - x.mean()) / x.std(ddof=1); zr = z_robuste(x) | |
| 444 | + out.append({"secteur": sct, "n": len(x), "moyenne": x.mean(), "ecart_type": x.std(ddof=1), "mediane": np.median(x), "mad_x1.4826": C_MAD * np.median(np.abs(x - np.median(x))), | |
| 445 | + "n_classique": int((np.abs(zc) > seuil).sum()), "n_robuste": int((np.abs(zr) > seuil).sum()), "n_prix_1": int((g["prix_net"] < SEUIL_PRIX_MIN).sum()), | |
| 446 | + "z_classique_min": zc.min(), "z_robuste_min": zr.min()}) | |
| 447 | + return {"tableau": pd.DataFrame(out).set_index("secteur"), "df": df} | |
| 448 | + | |
| 428 | 449 | # ----------------------------------------------------------------------------- 6. imputation comparée et sensibilité |
| 429 | 450 | |
| 430 | 451 | def comparer_imputations(P: dict) -> pd.DataFrame: |
@@ -476,11 +497,12 @@ def main(ecrire: bool = True, verbose: bool = True) -> dict: | ||
| 476 | 497 | P0(P["scores"]["exclus"][["id_inscription", "secteur", "superficie_habitable_m2", "prix_vente", "z_prix_m2", "z_superficie", "z_terrain"]].round(2).to_string(index=False)) |
| 477 | 498 | E = P["regression"]["exclus"] |
| 478 | 499 | P0("== Exclus E1 :\n", E[["id_inscription", "secteur", "superficie_habitable_m2", "prix_vente", "t_ext", "cook", "levier"]].round(3).to_string(index=False)) |
| 500 | + M = illustration_masquage(b); P0("== Masquage (après R1-R3, prix symboliques présents) :\n", M["tableau"].round(3).to_string()) | |
| 479 | 501 | G = simulation_geocodage(); P0("== Géocodage (simulation) :\n", G["tableau"].round(3).to_string(), "\nβ_dist référence :", round(G["beta_dist_ref"], 4), "; ventes dans le rayon de 3 km :", G["n_dans_rayon"]) |
| 480 | 502 | A = appariement_role(b, role, verite); P0("== Appariement :\n", A["taux"].to_string(), "\n", A["pct"].to_string(), "\nambigus :", A["ambigus"], "; faux appariements :", A.get("faux_appariements"), A.get("faux_par_palier")) |
| 481 | 503 | I = comparer_imputations(P); P0("== Imputations :\n", I.round(4).to_string()) |
| 482 | 504 | T = sensibilite_seuils(b, verite); P0("== Sensibilité aux seuils :\n", T.round(4).to_string(index=False)) |
| 483 | − return {"brut": b, "verite": verite, "role": role, "pipeline": P, "modeles": C, "geocodage": G, "appariement": A, "imputations": I, "sensibilite": T} | |
| 505 | + return {"brut": b, "verite": verite, "role": role, "pipeline": P, "modeles": C, "geocodage": G, "appariement": A, "imputations": I, "sensibilite": T, "masquage": M} | |
| 484 | 506 | |
| 485 | 507 | if __name__ == "__main__": |
| 486 | 508 | main(ecrire="--sans-ecriture" not in sys.argv) |
added
analysis/python/cases/ch15_segmentation.py
+325 −0
@@ -0,0 +1,325 @@ | ||
| 1 | +#!/usr/bin/env python3 | |
| 2 | +"""Chapitre 15 — Études de marché et segmentation. Calculs de l'exemple intégral et des tableaux du chapitre. | |
| 3 | + | |
| 4 | +Trois blocs, tous reproductibles (graine 20261031) : | |
| 5 | + A. indicateurs de marché (mois d'inventaire, taux d'absorption, délais) à partir des chiffres APCIQ du T3 2026 cités | |
| 6 | + avec attribution (data/synthetic/calibration.json, sources/notes-E.md) — chiffres réels, calculs de l'auteur ; | |
| 7 | + B. segmentation des 600 ventes SYNTHÉTIQUES du cas 1 (Gatineau) : a priori par secteur, puis statistique (k-moyennes, | |
| 8 | + classification hiérarchique de Ward, silhouette) ; test de Chow (classique et Wald robuste HC1) de stabilité des | |
| 9 | + coefficients hédoniques entre segments, via traite.hedonique.mco ; validation croisée pooled vs segmenté ; | |
| 10 | + C. étude de marché locatif pour le cas 3 (12 logements, Rosemont) : loyers, inoccupation, univers, rotation et offre | |
| 11 | + neuve de la zone SCHL 9 (Enquête sur les logements locatifs, octobre 2025) confrontés à l'état locatif synthétique | |
| 12 | + (traite.cas3.revenus) — on ne recalcule aucun RNE ici. | |
| 13 | +Usage (racine du dépôt) : PYTHONPATH=analysis/python python3 analysis/python/cases/ch15_segmentation.py""" | |
| 14 | +from __future__ import annotations | |
| 15 | +import sys, math, pathlib | |
| 16 | +import numpy as np, pandas as pd | |
| 17 | +sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1])) | |
| 18 | +from traite import cas1, cas3 | |
| 19 | +from traite import hedonique as h | |
| 20 | +from scipy import stats | |
| 21 | +from scipy.cluster.hierarchy import linkage, fcluster | |
| 22 | +from sklearn.cluster import KMeans | |
| 23 | +from sklearn.metrics import silhouette_score, silhouette_samples, adjusted_rand_score | |
| 24 | +from sklearn.model_selection import KFold | |
| 25 | + | |
| 26 | +ROOT = pathlib.Path(__file__).resolve().parents[3] | |
| 27 | +GRAINE = 20261031 | |
| 28 | + | |
| 29 | +# =============================================================================== A. indicateurs de marché (APCIQ T3 2026) | |
| 30 | +# Source : APCIQ par le système Centris, communiqué du 14 octobre 2026 et baromètres FSMI T3 2026 (consultés le 2026-10-31). | |
| 31 | +# Ventes résidentielles du trimestre, inscriptions en vigueur ; délais de vente médians (jours) unifamiliale / copropriété / plex. | |
| 32 | +APCIQ_T3_2026 = { | |
| 33 | + "Province de Québec": {"ventes": 20935, "inscriptions": 42408, "delais": (45, 58, 48)}, | |
| 34 | + "RMR de Montréal": {"ventes": 9265, "inscriptions": 20350, "delais": (40, 61, 51)}, | |
| 35 | + "RMR de Québec": {"ventes": 2161, "inscriptions": 2204, "delais": (29, 34, 30)}, | |
| 36 | + "RMR de Gatineau": {"ventes": 1044, "inscriptions": 2256, "delais": (34, 36, 38)}, | |
| 37 | + "RMR de Sherbrooke": {"ventes": 569, "inscriptions": 825, "delais": None}, | |
| 38 | + "RMR de Trois-Rivières": {"ventes": 372, "inscriptions": 447, "delais": None}, | |
| 39 | + "RMR de Saguenay": {"ventes": 371, "inscriptions": 498, "delais": None}, | |
| 40 | +} | |
| 41 | +# Baromètre Gatineau T3 2026 : ventes résidentielles par secteur Centris et médiane unifamiliale ($). | |
| 42 | +GATINEAU_SECTEURS_T3_2026 = { | |
| 43 | + "Gatineau": {"ventes": 346, "mediane_unif": 464900}, "Hull": {"ventes": 174, "mediane_unif": 475000}, | |
| 44 | + "Périphérie": {"ventes": 215, "mediane_unif": 530000}, "Buckingham/Masson-Angers": {"ventes": 108, "mediane_unif": 395000}, | |
| 45 | + "Aylmer": {"ventes": 201, "mediane_unif": 530000}, | |
| 46 | +} | |
| 47 | +JOURS_PAR_MOIS = 365.25 / 12 | |
| 48 | + | |
| 49 | +def mois_inventaire(inscriptions: float, ventes_trimestre: float) -> float: | |
| 50 | + """MI = inscriptions en vigueur / (ventes du trimestre / 3).""" | |
| 51 | + return inscriptions / (ventes_trimestre / 3.0) | |
| 52 | + | |
| 53 | +def taux_absorption(inscriptions: float, ventes_trimestre: float) -> float: | |
| 54 | + """Part du stock écoulée par mois = ventes mensuelles / inscriptions = 1 / MI.""" | |
| 55 | + return (ventes_trimestre / 3.0) / inscriptions | |
| 56 | + | |
| 57 | +def indicateurs_apciq() -> pd.DataFrame: | |
| 58 | + rows = [] | |
| 59 | + for terr, d in APCIQ_T3_2026.items(): | |
| 60 | + mi = mois_inventaire(d["inscriptions"], d["ventes"]) | |
| 61 | + r = {"territoire": terr, "ventes_trim": d["ventes"], "inscriptions": d["inscriptions"], "ventes_mois": d["ventes"] / 3.0, | |
| 62 | + "mois_inventaire": mi, "taux_absorption_mensuel": taux_absorption(d["inscriptions"], d["ventes"])} | |
| 63 | + if d["delais"]: | |
| 64 | + r.update({"delai_unif_j": d["delais"][0], "delai_copro_j": d["delais"][1], "delai_plex_j": d["delais"][2], | |
| 65 | + "delai_unif_mois": d["delais"][0] / JOURS_PAR_MOIS, "ratio_stock_flux": mi / (d["delais"][0] / JOURS_PAR_MOIS)}) | |
| 66 | + rows.append(r) | |
| 67 | + return pd.DataFrame(rows) | |
| 68 | + | |
| 69 | +def parts_secteurs_gatineau(v: pd.DataFrame) -> pd.DataFrame: | |
| 70 | + """Parts des ventes par secteur : marché réel (APCIQ T3 2026) vs jeu synthétique du cas 1 (Buckingham + Masson-Angers regroupés ; | |
| 71 | + la périphérie n'existe pas dans le jeu synthétique).""" | |
| 72 | + reel = pd.Series({k: d["ventes"] for k, d in GATINEAU_SECTEURS_T3_2026.items()}) | |
| 73 | + syn = v["secteur"].replace({"Buckingham": "Buckingham/Masson-Angers", "Masson-Angers": "Buckingham/Masson-Angers"}).value_counts() | |
| 74 | + out = pd.DataFrame({"ventes_apciq_t3_2026": reel, "part_apciq": reel / reel.sum(), "ventes_synthetiques": syn.reindex(reel.index).fillna(0).astype(int)}) | |
| 75 | + out["part_synthetique"] = out["ventes_synthetiques"] / out["ventes_synthetiques"].sum() | |
| 76 | + return out | |
| 77 | + | |
| 78 | +# =============================================================================== B. segmentation (cas 1, synthétique) | |
| 79 | +def matrice_base(df: pd.DataFrame) -> pd.DataFrame: | |
| 80 | + """Régresseurs du modèle de référence SANS les indicatrices de secteur (les segments les remplacent).""" | |
| 81 | + X = cas1.matrice(df) | |
| 82 | + return X[[c for c in X.columns if not c.startswith("sect_")]] | |
| 83 | + | |
| 84 | +def _ssr(X: np.ndarray, y: np.ndarray) -> float: | |
| 85 | + r = h.mco(X, y); return float(r["residus"] @ r["residus"]) | |
| 86 | + | |
| 87 | +def chow(v: pd.DataFrame, groupes: pd.Series, nom: str = "groupe") -> dict: | |
| 88 | + """Test de Chow (1960) de l'égalité des coefficients entre G segments. | |
| 89 | + Version « pentes » : le modèle contraint garde une constante propre à chaque segment (indicatrices) et des pentes communes ; | |
| 90 | + le modèle libre estime une équation par segment. F = [(SSR_c − SSR_l)/q] / [SSR_l/(n − G k0)], q = (G − 1)(k0 − 1). | |
| 91 | + Version « classique » : modèle contraint entièrement commun (constante incluse), q = (G − 1) k0. | |
| 92 | + Version robuste : Wald HC1 sur les interactions (régresseurs × indicatrices) du modèle empilé (χ² à q degrés de liberté).""" | |
| 93 | + Xb = matrice_base(v); y = np.log(v["prix_vente"].astype(float)).values | |
| 94 | + g = pd.Series(groupes, index=v.index).astype(str); niveaux = sorted(g.unique()); G = len(niveaux); n, k0 = Xb.shape | |
| 95 | + ref = niveaux[0] | |
| 96 | + D = pd.get_dummies(g, prefix="g").astype(float); D = D[[f"g_{l}" for l in niveaux[1:]]] | |
| 97 | + # contraint (constantes par segment) et classique (tout commun) | |
| 98 | + ssr_c = _ssr(np.column_stack([Xb.values, D.values]), y) | |
| 99 | + ssr_classique = _ssr(Xb.values, y) | |
| 100 | + # libre : une régression par segment | |
| 101 | + par_segment = {}; ssr_l = 0.0 | |
| 102 | + for l in niveaux: | |
| 103 | + m = (g == l).values; r = h.mco(Xb.values[m], y[m]); ssr_l += float(r["residus"] @ r["residus"]) | |
| 104 | + par_segment[l] = {"n": int(m.sum()), "coef": pd.Series(r["beta"], index=Xb.columns), "se_hc1": pd.Series(r["se_hc1"], index=Xb.columns), | |
| 105 | + "sigma": float(np.sqrt(r["sigma2"])), "r2": float(r["r2"])} | |
| 106 | + q = (G - 1) * (k0 - 1); df2 = n - G * k0 | |
| 107 | + F = ((ssr_c - ssr_l) / q) / (ssr_l / df2); p = float(stats.f.sf(F, q, df2)) | |
| 108 | + q0 = (G - 1) * k0; F0 = ((ssr_classique - ssr_l) / q0) / (ssr_l / df2); p0 = float(stats.f.sf(F0, q0, df2)) | |
| 109 | + # Wald robuste sur le modèle empilé : X_b, puis X_b(sans const) × D_g pour g ≠ ref, puis D_g (constantes) | |
| 110 | + pentes = [c for c in Xb.columns if c != "const"] | |
| 111 | + blocs = [Xb.values]; noms = list(Xb.columns) | |
| 112 | + for l in niveaux[1:]: | |
| 113 | + d = D[f"g_{l}"].values[:, None] | |
| 114 | + blocs.append(Xb[pentes].values * d); noms += [f"{c}×{l}" for c in pentes] | |
| 115 | + blocs.append(d); noms.append(f"const×{l}") | |
| 116 | + Xs = np.column_stack(blocs); r = h.mco(Xs, y) | |
| 117 | + idx = [i for i, nme in enumerate(noms) if "×" in nme and not nme.startswith("const×")] | |
| 118 | + theta = r["beta"][idx]; V = r["V_hc1"][np.ix_(idx, idx)] | |
| 119 | + W = float(theta @ np.linalg.solve(V, theta)); pW = float(stats.chi2.sf(W, len(idx))) | |
| 120 | + return {"nom": nom, "niveaux": niveaux, "G": G, "n": n, "k0": k0, "ssr_contraint": ssr_c, "ssr_classique": ssr_classique, "ssr_libre": ssr_l, | |
| 121 | + "q": q, "df2": df2, "F": float(F), "p": p, "F_classique": float(F0), "q_classique": q0, "p_classique": p0, | |
| 122 | + "wald_hc1": W, "p_wald": pW, "q_wald": len(idx), "par_segment": par_segment, | |
| 123 | + "sigma_contraint": math.sqrt(ssr_c / (n - k0 - G + 1)), "sigma_libre": math.sqrt(ssr_l / df2)} | |
| 124 | + | |
| 125 | +def tableau_coefficients(ch: dict, variables=("ln_S", "ln_T", "age", "qualite", "garage", "mois")) -> pd.DataFrame: | |
| 126 | + rows = [] | |
| 127 | + for l, d in ch["par_segment"].items(): | |
| 128 | + row = {"segment": l, "n": d["n"], "sigma": d["sigma"], "r2": d["r2"]} | |
| 129 | + for vv in variables: | |
| 130 | + row[vv] = d["coef"][vv]; row[f"se_{vv}"] = d["se_hc1"][vv] | |
| 131 | + rows.append(row) | |
| 132 | + return pd.DataFrame(rows) | |
| 133 | + | |
| 134 | +def caracteristiques_segmentation(v: pd.DataFrame, jeu: str = "attributs+coordonnees") -> pd.DataFrame: | |
| 135 | + """Variables de classification, centrées-réduites. jeu ∈ {attributs, coordonnees, attributs+coordonnees}.""" | |
| 136 | + xy = h.coordonnees_km(v["lat"].values, v["lon"].values) | |
| 137 | + F = pd.DataFrame(index=v.index) | |
| 138 | + if "attributs" in jeu: | |
| 139 | + F["ln_S"] = np.log(v["superficie_habitable_m2"]); F["ln_T"] = np.log(v["superficie_terrain_m2"]) | |
| 140 | + F["age"] = v["age"].astype(float); F["qualite"] = v["qualite_1a5"].astype(float); F["etages"] = (v["type"] == "à étages").astype(float) | |
| 141 | + if "coordonnees" in jeu: | |
| 142 | + F["x_km"] = xy[:, 0]; F["y_km"] = xy[:, 1]; F["dist_centre"] = v["dist_centre_km"].astype(float) | |
| 143 | + Z = (F - F.mean()) / F.std(ddof=0) | |
| 144 | + return Z | |
| 145 | + | |
| 146 | +def silhouette_manuelle(Z: np.ndarray, labels: np.ndarray) -> np.ndarray: | |
| 147 | + """s(i) = (b_i − a_i)/max(a_i, b_i) — implémentation directe (vérification de sklearn dans les tests).""" | |
| 148 | + d = np.sqrt(((Z[:, None, :] - Z[None, :, :]) ** 2).sum(-1)); s = np.zeros(len(Z)) | |
| 149 | + for i in range(len(Z)): | |
| 150 | + own = labels == labels[i]; own[i] = False | |
| 151 | + if own.sum() == 0: s[i] = 0.0; continue | |
| 152 | + a = d[i, own].mean() | |
| 153 | + b = min(d[i, labels == l].mean() for l in np.unique(labels) if l != labels[i]) | |
| 154 | + s[i] = (b - a) / max(a, b) | |
| 155 | + return s | |
| 156 | + | |
| 157 | +def classifier(Z: pd.DataFrame, k_max: int = 8) -> dict: | |
| 158 | + """k-moyennes (20 initialisations, graine fixée) et Ward pour k = 2..k_max ; silhouette moyenne pour chaque k.""" | |
| 159 | + Zv = Z.values; L = linkage(Zv, method="ward") | |
| 160 | + rows = []; labels_km = {}; labels_w = {}; inertie = {} | |
| 161 | + for k in range(2, k_max + 1): | |
| 162 | + km = KMeans(n_clusters=k, n_init=20, random_state=GRAINE).fit(Zv) | |
| 163 | + lw = fcluster(L, t=k, criterion="maxclust") | |
| 164 | + labels_km[k] = km.labels_; labels_w[k] = lw; inertie[k] = float(km.inertia_) | |
| 165 | + rows.append({"k": k, "silhouette_kmeans": float(silhouette_score(Zv, km.labels_)), "silhouette_ward": float(silhouette_score(Zv, lw)), | |
| 166 | + "inertie_kmeans": float(km.inertia_), "ari_kmeans_ward": float(adjusted_rand_score(km.labels_, lw))}) | |
| 167 | + tab = pd.DataFrame(rows) | |
| 168 | + k_opt = int(tab.loc[tab["silhouette_kmeans"].idxmax(), "k"]) | |
| 169 | + return {"linkage": L, "tableau": tab, "labels_kmeans": labels_km, "labels_ward": labels_w, "k_opt": k_opt, "inertie": inertie} | |
| 170 | + | |
| 171 | +def profil_segments(v: pd.DataFrame, labels: np.ndarray) -> pd.DataFrame: | |
| 172 | + d = v.assign(segment=labels) | |
| 173 | + g = d.groupby("segment") | |
| 174 | + out = pd.DataFrame({"n": g.size(), "superficie_med": g["superficie_habitable_m2"].median(), "age_med": g["age"].median(), | |
| 175 | + "qualite_moy": g["qualite_1a5"].mean(), "dist_centre_med": g["dist_centre_km"].median(), | |
| 176 | + "prix_med": g["prix_vente"].median(), "prix_m2_med": (d["prix_vente"] / d["superficie_habitable_m2"]).groupby(d["segment"]).median(), | |
| 177 | + "secteur_dominant": g["secteur"].agg(lambda s: s.value_counts().index[0]), | |
| 178 | + "part_secteur_dominant": g["secteur"].agg(lambda s: s.value_counts().iloc[0] / len(s))}) | |
| 179 | + return out | |
| 180 | + | |
| 181 | +def validation_croisee(v: pd.DataFrame, segments: dict, n_plis: int = 10) -> pd.DataFrame: | |
| 182 | + """RMSE (log) et erreur absolue médiane (%) hors échantillon, 10 plis : modèle de référence (pentes communes + indicatrices de | |
| 183 | + secteur), pentes communes + indicatrices de segment, et une équation par segment (variance accrue sur les petits segments).""" | |
| 184 | + y = np.log(v["prix_vente"].astype(float)).values; Xb = matrice_base(v) | |
| 185 | + kf = KFold(n_splits=n_plis, shuffle=True, random_state=GRAINE) | |
| 186 | + def ajuster_predire(Xtr, ytr, Xte): | |
| 187 | + b = h.mco(Xtr, ytr)["beta"]; return Xte @ b | |
| 188 | + resultats = [] | |
| 189 | + specs = {"Référence : pentes communes + indicatrices de secteur": ("dummies", v["secteur"].astype(str))} | |
| 190 | + for nom, lab in segments.items(): | |
| 191 | + specs[f"Pentes communes + indicatrices ({nom})"] = ("dummies", pd.Series(lab, index=v.index).astype(str)) | |
| 192 | + specs[f"Une équation par segment ({nom})"] = ("separe", pd.Series(lab, index=v.index).astype(str)) | |
| 193 | + for nom, (mode, lab) in specs.items(): | |
| 194 | + err = np.zeros(len(y)) | |
| 195 | + D = pd.get_dummies(lab).astype(float).values | |
| 196 | + for tr, te in kf.split(Xb): | |
| 197 | + if mode == "dummies": | |
| 198 | + Xd = np.column_stack([Xb.values, D[:, 1:]]) | |
| 199 | + err[te] = y[te] - ajuster_predire(Xd[tr], y[tr], Xd[te]) | |
| 200 | + else: | |
| 201 | + for l in np.unique(lab): | |
| 202 | + mtr = tr[(lab.values[tr] == l)]; mte = te[(lab.values[te] == l)] | |
| 203 | + if len(mte) == 0: continue | |
| 204 | + if len(mtr) <= Xb.shape[1] + 2: # segment trop petit : repli sur le modèle commun | |
| 205 | + err[mte] = y[mte] - ajuster_predire(Xb.values[tr], y[tr], Xb.values[mte]) | |
| 206 | + else: | |
| 207 | + err[mte] = y[mte] - ajuster_predire(Xb.values[mtr], y[mtr], Xb.values[mte]) | |
| 208 | + resultats.append({"specification": nom, "rmse_log": float(np.sqrt(np.mean(err ** 2))), "mape_mediane_pct": float(100 * np.median(np.abs(np.exp(err) - 1))), | |
| 209 | + "erreur_abs_moy_pct": float(100 * np.mean(np.abs(np.exp(err) - 1)))}) | |
| 210 | + return pd.DataFrame(resultats) | |
| 211 | + | |
| 212 | +# =============================================================================== C. marché locatif, cas 3 (SCHL zone 9 Rosemont) | |
| 213 | +# Source : SCHL, Enquête sur les logements locatifs, octobre 2025 (Rapport sur le marché locatif publié le 11 décembre 2025), | |
| 214 | +# tableaux de données RMR de Montréal : 1.1.1 (inoccupation), 1.1.2 (loyer moyen), 1.1.3 (univers), 1.1.5 (variation même échantillon), | |
| 215 | +# 1.1.6 (rotation), 1.1.9 (loyer des logements inoccupés vs occupés), 1.3.1-1.3.4 (taille d'immeuble), 3.1.7 (parc neuf). | |
| 216 | +# Lettres : indicateur de qualité SCHL (a excellent, b très bon, c bon, d passable) ; None = donnée supprimée (**). | |
| 217 | +SCHL_ZONE9 = { | |
| 218 | + "zone": "Zone 9 — Rosemont/La Petite-Patrie", | |
| 219 | + "loyer_2025": {"studio": 1010, "1 ch.": 1055, "2 ch.": 1348, "3 ch. +": 1750, "tous": 1237}, | |
| 220 | + "qualite_loyer_2025": {"studio": "c", "1 ch.": "d", "2 ch.": "c", "3 ch. +": "c", "tous": "c"}, | |
| 221 | + "loyer_2024": {"studio": 819, "1 ch.": 1093, "2 ch.": None, "3 ch. +": 1782, "tous": 1237}, | |
| 222 | + "univers_2025": {"studio": 5553, "1 ch.": 12405, "2 ch.": 22520, "3 ch. +": 4076, "tous": 44554}, | |
| 223 | + "inocc_2025": {"studio": None, "1 ch.": 0.5, "2 ch.": None, "3 ch. +": None, "tous": None}, | |
| 224 | + "inocc_2024": {"studio": 2.0, "1 ch.": 0.4, "2 ch.": None, "3 ch. +": 0.0, "tous": 0.7}, | |
| 225 | + "variation_2025": {"studio": None, "1 ch.": 8.8, "2 ch.": 6.3, "3 ch. +": 4.2, "tous": 7.5}, | |
| 226 | + "inocc_taille_2025": {"3 à 5": None, "6 à 19": None, "20 à 49": 4.1, "50 à 99": 0.0, "100 +": 2.8}, | |
| 227 | + "loyer_inoccupe_vs_occupe_2025": {"studio": (905, 1012), "1 ch.": (1295, 1054), "2 ch.": (None, 1285), "3 ch. +": (None, 1750), "tous": (None, 1213)}, | |
| 228 | + "neuf_2022_2025": {"univers": 627, "loyer_tous": 1920, "loyer_2ch": 2226, "univers_2ch": 306}, | |
| 229 | +} | |
| 230 | +SCHL_ILE_6_19 = {"inocc_2025": 2.2, "inocc_2024": 1.7, "loyer_2ch_2025": 1177, "loyer_tous_2025": 1162, "rotation_2025": 8.5, "rotation_2024": 9.4} | |
| 231 | +SCHL_RMR = {"inocc_2025": 2.9, "inocc_2024": 2.1, "loyer_2ch_2025": 1346, "loyer_tous_2025": 1290, "rotation_2025": 9.4, "rotation_2024": 11.1, | |
| 232 | + "loyer_inoccupe_tous": 1628, "loyer_occupe_tous": 1281, "loyer_inoccupe_2ch": 1817, "loyer_occupe_2ch": 1333, "univers_2025": 667266} | |
| 233 | + | |
| 234 | +def etude_locative_cas3() -> dict: | |
| 235 | + c = cas3.charger(); etat = c["etat"]; hyp = c["hyp"]; rev = cas3.revenus(etat, hyp) | |
| 236 | + corr = {"3½": "1 ch.", "4½": "2 ch.", "5½": "3 ch. +"} | |
| 237 | + g = etat.groupby("pieces").agg(n=("logement", "size"), contrat=("loyer_mensuel_contrat", "mean"), marche=("loyer_mensuel_marche", "mean"), | |
| 238 | + superficie=("superficie_m2", "mean")) | |
| 239 | + g["type_schl"] = [corr[p] for p in g.index] | |
| 240 | + g["loyer_schl_zone9_2025"] = [SCHL_ZONE9["loyer_2025"][t] for t in g["type_schl"]] | |
| 241 | + g["ratio_marche_schl"] = g["marche"] / g["loyer_schl_zone9_2025"] | |
| 242 | + g["ecart_contrat_marche_pct"] = 100 * (g["marche"] / g["contrat"] - 1) | |
| 243 | + u = SCHL_ZONE9["univers_2025"] | |
| 244 | + parts = {k: u[k] / u["tous"] for k in ["studio", "1 ch.", "2 ch.", "3 ch. +"]} | |
| 245 | + ecart_1ch = SCHL_ZONE9["loyer_inoccupe_vs_occupe_2025"]["1 ch."] | |
| 246 | + ecart_rmr = (SCHL_RMR["loyer_inoccupe_tous"] / SCHL_RMR["loyer_occupe_tous"] - 1) | |
| 247 | + rot = {"hypothese_cas3": hyp["rotation_annuelle"], "schl_rmr_2025": SCHL_RMR["rotation_2025"] / 100, "schl_ile_6_19_2025": SCHL_ILE_6_19["rotation_2025"] / 100} | |
| 248 | + rattrapage = {k: r * rev["ecart_marche"] for k, r in rot.items()} # part de l'écart récupérable la première année | |
| 249 | + horizon = {k: 1.0 / r for k, r in rot.items()} # années pour renouveler tous les baux au rythme r | |
| 250 | + return {"par_type": g, "revenus": rev, "ecart_global_pct": 100 * (rev["rbp_marche"] / rev["rbp_contrat"] - 1), "parts_univers": parts, | |
| 251 | + "ecart_inoccupe_occupe_1ch_zone9_pct": 100 * (ecart_1ch[0] / ecart_1ch[1] - 1), "ecart_inoccupe_occupe_rmr_pct": 100 * ecart_rmr, | |
| 252 | + "rotation": rot, "rattrapage_an1": rattrapage, "horizon_renouvellement": horizon, | |
| 253 | + "neuf_prime_pct": 100 * (SCHL_ZONE9["neuf_2022_2025"]["loyer_tous"] / SCHL_ZONE9["loyer_2025"]["tous"] - 1), | |
| 254 | + "neuf_part_univers_pct": 100 * SCHL_ZONE9["neuf_2022_2025"]["univers"] / u["tous"], | |
| 255 | + "inocc_hyp_cas3_pct": 100 * hyp["taux_inoccupation_pertes"]} | |
| 256 | + | |
| 257 | +# =============================================================================== tout | |
| 258 | +def tout() -> dict: | |
| 259 | + d = cas1.charger(); v = d["ventes"] | |
| 260 | + ind = indicateurs_apciq(); parts = parts_secteurs_gatineau(v) | |
| 261 | + ref = cas1.hedonique(v) | |
| 262 | + # segmentation a priori : secteurs ; autres partitions a priori (type, période) | |
| 263 | + ch_secteur = chow(v, v["secteur"], "secteur") | |
| 264 | + est = v["secteur"].replace({"Buckingham": "Est", "Masson-Angers": "Est"}) | |
| 265 | + ch_secteur4 = chow(v, est, "secteur (4 groupes, Est regroupé)") | |
| 266 | + ch_type = chow(v, v["type"], "type de bâtiment") | |
| 267 | + periode = np.where(v["date_vente"] >= pd.Timestamp("2025-01-01"), "2025-2026", "2023-2024") | |
| 268 | + ch_periode = chow(v, pd.Series(periode, index=v.index), "période") | |
| 269 | + # segmentation statistique | |
| 270 | + Z = caracteristiques_segmentation(v, "attributs+coordonnees"); cl = classifier(Z) | |
| 271 | + k = cl["k_opt"]; lab_km = cl["labels_kmeans"][k]; lab_w = cl["labels_ward"][k] | |
| 272 | + sil_i = silhouette_samples(Z.values, lab_km) | |
| 273 | + prof = profil_segments(v, lab_km + 1) | |
| 274 | + croise = pd.crosstab(pd.Series(lab_km + 1, index=v.index, name="segment"), v["secteur"]) | |
| 275 | + ari_secteur = float(adjusted_rand_score(v["secteur"], lab_km)); ari_ward = float(adjusted_rand_score(lab_km, lab_w)) | |
| 276 | + jeux = {} | |
| 277 | + for jeu in ["attributs", "coordonnees", "attributs+coordonnees"]: | |
| 278 | + Zj = caracteristiques_segmentation(v, jeu); cj = classifier(Zj); kj = cj["k_opt"] | |
| 279 | + jeux[jeu] = {"k_opt": kj, "silhouette": float(cj["tableau"].loc[cj["tableau"]["k"] == kj, "silhouette_kmeans"].iloc[0]), | |
| 280 | + "ari_secteur": float(adjusted_rand_score(v["secteur"], cj["labels_kmeans"][kj])), | |
| 281 | + "silhouette_k5": float(cj["tableau"].loc[cj["tableau"]["k"] == 5, "silhouette_kmeans"].iloc[0]), | |
| 282 | + "ari_secteur_k5": float(adjusted_rand_score(v["secteur"], cj["labels_kmeans"][5]))} | |
| 283 | + ch_kmeans = chow(v, pd.Series(lab_km + 1, index=v.index), f"k-moyennes (k = {k})") | |
| 284 | + ch_ward = chow(v, pd.Series(lab_w, index=v.index), f"Ward (k = {k})") | |
| 285 | + cv = validation_croisee(v, {"k-moyennes": lab_km + 1, "Ward": lab_w}) | |
| 286 | + loc = etude_locative_cas3() | |
| 287 | + return {"ventes": v, "sujet": d["sujet"], "indicateurs": ind, "parts_gatineau": parts, "reference": ref, | |
| 288 | + "chow": {"secteur": ch_secteur, "secteur4": ch_secteur4, "type": ch_type, "periode": ch_periode, "kmeans": ch_kmeans, "ward": ch_ward}, | |
| 289 | + "Z": Z, "classification": cl, "k_opt": k, "labels_kmeans": lab_km, "labels_ward": lab_w, "silhouette_i": sil_i, "profil": prof, | |
| 290 | + "croise": croise, "ari_secteur": ari_secteur, "ari_kmeans_ward": ari_ward, "jeux": jeux, "cv": cv, "locatif": loc} | |
| 291 | + | |
| 292 | +def main(): | |
| 293 | + pd.set_option("display.width", 220); pd.set_option("display.max_columns", 30) | |
| 294 | + r = tout() | |
| 295 | + print("=== A. Indicateurs APCIQ T3 2026 (chiffres cités ; calculs de l'auteur)") | |
| 296 | + print(r["indicateurs"].round(3).to_string(index=False)) | |
| 297 | + print("\nParts des ventes par secteur, Gatineau (APCIQ vs synthétique) :\n", r["parts_gatineau"].round(3).to_string()) | |
| 298 | + print("\n=== B. Modèle de référence (traite.cas1.hedonique) : R² = %.3f, σ = %.4f" % (r["reference"]["r2"], r["reference"]["sigma"])) | |
| 299 | + for key, ch in r["chow"].items(): | |
| 300 | + print(f"\n--- Chow [{ch['nom']}] : G = {ch['G']}, k0 = {ch['k0']}, n = {ch['n']}") | |
| 301 | + print(f" SSR contraint (constantes par segment) = {ch['ssr_contraint']:.4f} ; SSR classique = {ch['ssr_classique']:.4f} ; SSR libre = {ch['ssr_libre']:.4f}") | |
| 302 | + print(f" F pentes = {ch['F']:.3f} (q = {ch['q']}, df2 = {ch['df2']}), p = {ch['p']:.4f} ; F classique = {ch['F_classique']:.3f} (q = {ch['q_classique']}), p = {ch['p_classique']:.4g}") | |
| 303 | + print(f" Wald HC1 = {ch['wald_hc1']:.2f} (q = {ch['q_wald']}), p = {ch['p_wald']:.4f} ; σ contraint {ch['sigma_contraint']:.4f} vs libre {ch['sigma_libre']:.4f}") | |
| 304 | + if key in ("secteur", "kmeans"): | |
| 305 | + print(tableau_coefficients(ch).round(4).to_string(index=False)) | |
| 306 | + print("\n--- Classification (attributs + coordonnées) :\n", r["classification"]["tableau"].round(3).to_string(index=False)) | |
| 307 | + print(f" k optimal (silhouette k-moyennes) = {r['k_opt']} ; ARI(k-moyennes, secteurs) = {r['ari_secteur']:.3f} ; ARI(k-moyennes, Ward) = {r['ari_kmeans_ward']:.3f}") | |
| 308 | + print(" silhouette moyenne par segment :", pd.Series(r["silhouette_i"]).groupby(r["labels_kmeans"] + 1).mean().round(3).to_dict()) | |
| 309 | + print("\n--- Profil des segments k-moyennes :\n", r["profil"].round(2).to_string()) | |
| 310 | + print("\n--- Segments × secteurs :\n", r["croise"].to_string()) | |
| 311 | + print("\n--- Jeux de variables :\n", pd.DataFrame(r["jeux"]).T.round(3).to_string()) | |
| 312 | + print("\n--- Validation croisée (10 plis) :\n", r["cv"].round(4).to_string(index=False)) | |
| 313 | + L = r["locatif"] | |
| 314 | + print("\n=== C. Étude de marché locatif, cas 3 (SCHL zone 9, oct. 2025 ; état locatif synthétique)") | |
| 315 | + print(L["par_type"].round(3).to_string()) | |
| 316 | + print(" RBP contrat %.0f $, RBP marché %.0f $, écart %.0f $ (%.1f %%)" % (L["revenus"]["rbp_contrat"], L["revenus"]["rbp_marche"], L["revenus"]["ecart_marche"], L["ecart_global_pct"])) | |
| 317 | + print(" parts de l'univers zone 9 :", {k: round(x, 3) for k, x in L["parts_univers"].items()}) | |
| 318 | + print(" écart loyer inoccupé/occupé : 1 ch. zone 9 %.1f %% ; tous RMR %.1f %%" % (L["ecart_inoccupe_occupe_1ch_zone9_pct"], L["ecart_inoccupe_occupe_rmr_pct"])) | |
| 319 | + print(" rotation :", {k: round(x, 3) for k, x in L["rotation"].items()}, "; rattrapage an 1 :", {k: round(x) for k, x in L["rattrapage_an1"].items()}, | |
| 320 | + "; horizon (ans) :", {k: round(x, 1) for k, x in L["horizon_renouvellement"].items()}) | |
| 321 | + print(" parc neuf 2022-2025 zone 9 : prime de loyer %.1f %%, part de l'univers %.2f %%" % (L["neuf_prime_pct"], L["neuf_part_univers_pct"])) | |
| 322 | + return r | |
| 323 | + | |
| 324 | +if __name__ == "__main__": | |
| 325 | + main() | |
added
analysis/python/figures/ch22_ecarts.py
+63 −0
@@ -0,0 +1,63 @@ | ||
| 1 | +#!/usr/bin/env python3 | |
| 2 | +"""Figure ch22 — métré de contrôle du sujet du cas 1 : écart relatif entre la quantité recalculée depuis le plan coté | |
| 3 | +et la quantité du devis, par poste mesuré ou dénombré, avec les bandes de tolérance (±5 % conforme, ±10 % à préciser). | |
| 4 | +DONNÉES SYNTHÉTIQUES, graine 20261031. Sortie : assets/figures/ch22/ch22-fig-ecarts-metre.pdf""" | |
| 5 | +import sys, pathlib | |
| 6 | +ROOT = pathlib.Path(__file__).resolve().parents[3] | |
| 7 | +sys.path.insert(0, str(ROOT / "analysis/python")); sys.path.insert(0, str(ROOT / "analysis/python/cases")) | |
| 8 | +import matplotlib.pyplot as plt | |
| 9 | +from matplotlib.lines import Line2D | |
| 10 | +from traite.style import setup, palette, savefig, LARGEUR | |
| 11 | +import ch21_cout_neuf as c21 | |
| 12 | +import ch22_metre as cas | |
| 13 | + | |
| 14 | +COURT = {"Excavation et remblai": "Excavation et remblai (m³)", "Fondations béton (semelles, murs)": "Fondations béton (m³)", | |
| 15 | + "Dalle de sous-sol": "Dalle de sous-sol (m²)", "Drain français et imperméabilisation": "Drain français (m lin.)", | |
| 16 | + "Charpente murs et planchers": "Charpente murs et planchers (m²)", "Fermes de toit": "Fermes de toit (m²)", | |
| 17 | + "Couverture bardeaux d'asphalte": "Couverture (m²)", "Revêtement extérieur (brique 40 % / fibrociment 60 %)": "Revêtement extérieur (m²)", | |
| 18 | + "Isolation et pare-vapeur": "Isolation et pare-vapeur (m²)", "Fenêtres (PVC double vitrage)": "Fenêtres (unités)", | |
| 19 | + "Portes extérieures": "Portes extérieures (unités)", "Porte de garage": "Porte de garage (unité)", | |
| 20 | + "Gypse et tirage de joints": "Gypse (m²)", "Peinture intérieure": "Peinture (m²)", "Planchers bois franc": "Planchers bois franc (m²)", | |
| 21 | + "Planchers céramique": "Planchers céramique (m²)", "Planchers flottants (sous-sol)": "Planchers flottants (m²)", | |
| 22 | + "Escaliers": "Escaliers (unités)", "Portes intérieures et moulures": "Portes intérieures (unités)", | |
| 23 | + "Plomberie brute et finition": "Plomberie (appareils)", "Gouttières et solins": "Gouttières et solins (m lin.)", | |
| 24 | + "Balcon et perron": "Balcon et perron (m²)", "Entrée asphaltée": "Entrée asphaltée (m²)", "Supervision de chantier (salaires)": "Supervision (semaines)"} | |
| 25 | +COUL = {"conforme": palette.cat[0], "à préciser": palette.status["warning"], "à justifier": palette.status["critical"]} | |
| 26 | + | |
| 27 | + | |
| 28 | +def main(): | |
| 29 | + setup() | |
| 30 | + devis, _ = c21.charger() | |
| 31 | + V = cas.verification(devis) | |
| 32 | + V = V[V["classe"] != "forfait"].copy() | |
| 33 | + V["ordre"] = V["corps"].map({c: i for i, c in enumerate(c21.ORDRE_CORPS)}) | |
| 34 | + V = V.sort_values(["ordre"], kind="stable").reset_index(drop=True) | |
| 35 | + V = V.iloc[::-1].reset_index(drop=True) # premier poste en haut | |
| 36 | + y = list(range(len(V))) | |
| 37 | + fig, ax = plt.subplots(figsize=(LARGEUR, 4.6)) | |
| 38 | + ax.axvspan(-10, -5, color=palette.status["warning"], alpha=0.10, lw=0); ax.axvspan(5, 10, color=palette.status["warning"], alpha=0.10, lw=0) | |
| 39 | + ax.axvspan(-5, 5, color=palette.cat[0], alpha=0.07, lw=0) | |
| 40 | + ax.axvline(0, color=palette.ink2, lw=0.8) | |
| 41 | + for k, r in V.iterrows(): | |
| 42 | + ax.plot([0, r["ecart"] * 100], [k, k], color=palette.grid, lw=1.2, zorder=1) | |
| 43 | + ax.plot(r["ecart"] * 100, k, "o", color=COUL[r["verdict"]], ms=5.5, zorder=3) | |
| 44 | + if abs(r["ecart"]) > 0.0005: | |
| 45 | + ax.text(r["ecart"] * 100 + (0.6 if r["ecart"] >= 0 else -0.6), k, f"{r['ecart']*100:+.1f} %", va="center", | |
| 46 | + ha="left" if r["ecart"] >= 0 else "right", fontsize=7.2, color=palette.ink) | |
| 47 | + ax.set_yticks(y); ax.set_yticklabels([COURT.get(p, p) for p in V["poste"]], fontsize=7.6) | |
| 48 | + ax.set_xlim(-22, 26); ax.set_xlabel("Écart du métré de contrôle par rapport au devis (%) : (quantité recalculée − quantité du devis) / quantité du devis") | |
| 49 | + ax.grid(axis="y", visible=False) | |
| 50 | + # séparateurs de corps | |
| 51 | + prev = None | |
| 52 | + for k, c in enumerate(V["corps"]): | |
| 53 | + if prev is not None and c != prev: | |
| 54 | + ax.axhline(k - 0.5, color=palette.grid, lw=0.6) | |
| 55 | + prev = c | |
| 56 | + nb = V["verdict"].value_counts() | |
| 57 | + handles = [Line2D([], [], marker="o", color=COUL[v], ls="", ms=5.5, label=f"{v} ({int(nb.get(v, 0))})") for v in ("conforme", "à préciser", "à justifier")] | |
| 58 | + ax.legend(handles=handles, loc="lower right", fontsize=7.5, title="Verdict (tolérances ±5 % et ±10 %)", title_fontsize=7.5) | |
| 59 | + out = savefig(fig, "ch22/ch22-fig-ecarts-metre.pdf"); print("OK", out) | |
| 60 | + | |
| 61 | + | |
| 62 | +if __name__ == "__main__": | |
| 63 | + main() | |
added
analysis/python/figures/ch22_repartition.py
+47 −0
@@ -0,0 +1,47 @@ | ||
| 1 | +#!/usr/bin/env python3 | |
| 2 | +"""Figure ch22 — répartition du devis du sujet du cas 1 par corps de métier et par type d'unité de mesure | |
| 3 | +(mesuré en m³/m²/m lin., dénombré en unités/appareils/semaines, forfait). DONNÉES SYNTHÉTIQUES, graine 20261031. | |
| 4 | +Mêmes sept blocs et même ordre que la figure 21 (ch21_decomposition.py). | |
| 5 | +Sortie : assets/figures/ch22/ch22-fig-repartition-corps.pdf""" | |
| 6 | +import sys, pathlib | |
| 7 | +ROOT = pathlib.Path(__file__).resolve().parents[3] | |
| 8 | +sys.path.insert(0, str(ROOT / "analysis/python")); sys.path.insert(0, str(ROOT / "analysis/python/cases")) | |
| 9 | +import matplotlib.pyplot as plt | |
| 10 | +from traite.style import setup, palette, savefig, LARGEUR, fmt_dollars | |
| 11 | +import ch21_cout_neuf as c21 | |
| 12 | +import ch22_metre as cas | |
| 13 | + | |
| 14 | + | |
| 15 | +def main(): | |
| 16 | + setup() | |
| 17 | + devis, _ = c21.charger() | |
| 18 | + rep = cas.repartition_par_type(devis) | |
| 19 | + types = [c for c in ("mesuré", "dénombré", "forfait") if c in rep.columns] | |
| 20 | + etiquettes = {"mesuré": "Mesuré (m³, m², m lin.)", "dénombré": "Dénombré (unités, appareils, semaines)", "forfait": "Forfait"} | |
| 21 | + couleurs = {"mesuré": palette.cat[0], "dénombré": palette.cat[2], "forfait": palette.cat[1]} | |
| 22 | + fig, ax = plt.subplots(figsize=(LARGEUR, 3.3)) | |
| 23 | + ordre = list(rep.index)[::-1] # premier bloc en haut | |
| 24 | + y = list(range(len(ordre))) | |
| 25 | + gauche = [0.0] * len(ordre) | |
| 26 | + for t in types: | |
| 27 | + vals = [float(rep.loc[c, t]) for c in ordre] | |
| 28 | + ax.barh(y, vals, left=gauche, color=couleurs[t], height=0.66, label=etiquettes[t]) | |
| 29 | + gauche = [g + v for g, v in zip(gauche, vals)] | |
| 30 | + tot_di = float(rep.to_numpy().sum()) | |
| 31 | + for k, c in enumerate(ordre): | |
| 32 | + tot = float(rep.loc[c].sum()) | |
| 33 | + ax.text(tot + 900, k, f"{fmt_dollars(tot)} ({tot/tot_di*100:.1f} %)", va="center", ha="left", fontsize=8, color=palette.ink) | |
| 34 | + ax.set_yticks(y); ax.set_yticklabels(ordre) | |
| 35 | + ax.set_xlim(0, max(gauche) * 1.45) | |
| 36 | + ax.xaxis.set_major_formatter(plt.FuncFormatter(fmt_dollars)) | |
| 37 | + ax.set_xlabel("Montant du devis (dollars de 2026) ; part du sous-total directs + indirects") | |
| 38 | + ax.grid(axis="y", visible=False) | |
| 39 | + parts = rep.sum() / tot_di * 100 | |
| 40 | + ax.set_title("Sous-total D + I = " + fmt_dollars(tot_di) + " : " + ", ".join(f"{etiquettes[t].split(' (')[0].lower()} {parts[t]:.1f} %" for t in types), | |
| 41 | + loc="left", fontsize=8.5, color=palette.ink) | |
| 42 | + ax.legend(loc="upper center", bbox_to_anchor=(0.45, -0.2), ncol=3, fontsize=7.5) | |
| 43 | + out = savefig(fig, "ch22/ch22-fig-repartition-corps.pdf"); print("OK", out) | |
| 44 | + | |
| 45 | + | |
| 46 | +if __name__ == "__main__": | |
| 47 | + main() | |
modified
data/synthetic/cas1_gatineau_journal_exclusions.csv
+31 −26
@@ -9,19 +9,24 @@ ch14_nettoyage.py 1.0 (2026-10-31),R2,B0394,correction,date de promesse postéri | ||
| 9 | 9 | ch14_nettoyage.py 1.0 (2026-10-31),R2,B0418,correction,date de promesse postérieure à l'acte : champs permutés,date_promesse,2025-09-28,2025-06-30 |
| 10 | 10 | ch14_nettoyage.py 1.0 (2026-10-31),R2,B0550,correction,date de promesse postérieure à l'acte : champs permutés,date_promesse,2026-07-25,2026-05-06 |
| 11 | 11 | ch14_nettoyage.py 1.0 (2026-10-31),R2,B0561,correction,date de promesse postérieure à l'acte : champs permutés,date_promesse,2026-08-12,2026-06-03 |
| 12 | −ch14_nettoyage.py 1.0 (2026-10-31),R1,B0155,exclusion,"doublon d'inscription (même unité, même promesse, même prix) : inscription la plus récente conservée",,, | |
| 13 | −ch14_nettoyage.py 1.0 (2026-10-31),R1,B0197,exclusion,"doublon d'inscription (même unité, même promesse, même prix) : inscription la plus récente conservée",,, | |
| 14 | −ch14_nettoyage.py 1.0 (2026-10-31),R1,B0198,exclusion,"doublon d'inscription (même unité, même promesse, même prix) : inscription la plus récente conservée",,, | |
| 15 | −ch14_nettoyage.py 1.0 (2026-10-31),R1,B0207,exclusion,"doublon d'inscription (même unité, même promesse, même prix) : inscription la plus récente conservée",,, | |
| 16 | −ch14_nettoyage.py 1.0 (2026-10-31),R1,B0224,exclusion,"doublon d'inscription (même unité, même promesse, même prix) : inscription la plus récente conservée",,, | |
| 17 | −ch14_nettoyage.py 1.0 (2026-10-31),R1,B0255,exclusion,"doublon d'inscription (même unité, même promesse, même prix) : inscription la plus récente conservée",,, | |
| 18 | −ch14_nettoyage.py 1.0 (2026-10-31),R1,B0291,exclusion,"doublon d'inscription (même unité, même promesse, même prix) : inscription la plus récente conservée",,, | |
| 19 | −ch14_nettoyage.py 1.0 (2026-10-31),R1,B0399,exclusion,"doublon d'inscription (même unité, même promesse, même prix) : inscription la plus récente conservée",,, | |
| 20 | −ch14_nettoyage.py 1.0 (2026-10-31),R1,B0456,exclusion,"doublon d'inscription (même unité, même promesse, même prix) : inscription la plus récente conservée",,, | |
| 21 | −ch14_nettoyage.py 1.0 (2026-10-31),R1,B0483,exclusion,"doublon d'inscription (même unité, même promesse, même prix) : inscription la plus récente conservée",,, | |
| 22 | −ch14_nettoyage.py 1.0 (2026-10-31),R1,B0498,exclusion,"doublon d'inscription (même unité, même promesse, même prix) : inscription la plus récente conservée",,, | |
| 23 | −ch14_nettoyage.py 1.0 (2026-10-31),R1,B0497,exclusion,"doublon d'inscription (même unité, même promesse, même prix) : inscription la plus récente conservée",,, | |
| 24 | −ch14_nettoyage.py 1.0 (2026-10-31),R1,B0580,exclusion,"doublon d'inscription (même unité, même promesse, même prix) : inscription la plus récente conservée",,, | |
| 12 | +ch14_nettoyage.py 1.0 (2026-10-31),R1,B0155,exclusion,"doublon d'inscription (même clé d'unité, même promesse, même prix) : inscription la plus récente conservée",,, | |
| 13 | +ch14_nettoyage.py 1.0 (2026-10-31),R1,B0197,exclusion,"doublon d'inscription (même clé d'unité, même promesse, même prix) : inscription la plus récente conservée",,, | |
| 14 | +ch14_nettoyage.py 1.0 (2026-10-31),R1,B0198,exclusion,"doublon d'inscription (même clé d'unité, même promesse, même prix) : inscription la plus récente conservée",,, | |
| 15 | +ch14_nettoyage.py 1.0 (2026-10-31),R1,B0207,exclusion,"doublon d'inscription (même clé d'unité, même promesse, même prix) : inscription la plus récente conservée",,, | |
| 16 | +ch14_nettoyage.py 1.0 (2026-10-31),R1,B0224,exclusion,"doublon d'inscription (même clé d'unité, même promesse, même prix) : inscription la plus récente conservée",,, | |
| 17 | +ch14_nettoyage.py 1.0 (2026-10-31),R1,B0255,exclusion,"doublon d'inscription (même clé d'unité, même promesse, même prix) : inscription la plus récente conservée",,, | |
| 18 | +ch14_nettoyage.py 1.0 (2026-10-31),R1,B0291,exclusion,"doublon d'inscription (même clé d'unité, même promesse, même prix) : inscription la plus récente conservée",,, | |
| 19 | +ch14_nettoyage.py 1.0 (2026-10-31),R1,B0399,exclusion,"doublon d'inscription (même clé d'unité, même promesse, même prix) : inscription la plus récente conservée",,, | |
| 20 | +ch14_nettoyage.py 1.0 (2026-10-31),R1,B0456,exclusion,"doublon d'inscription (même clé d'unité, même promesse, même prix) : inscription la plus récente conservée",,, | |
| 21 | +ch14_nettoyage.py 1.0 (2026-10-31),R1,B0483,exclusion,"doublon d'inscription (même clé d'unité, même promesse, même prix) : inscription la plus récente conservée",,, | |
| 22 | +ch14_nettoyage.py 1.0 (2026-10-31),R1,B0498,exclusion,"doublon d'inscription (même clé d'unité, même promesse, même prix) : inscription la plus récente conservée",,, | |
| 23 | +ch14_nettoyage.py 1.0 (2026-10-31),R1,B0497,exclusion,"doublon d'inscription (même clé d'unité, même promesse, même prix) : inscription la plus récente conservée",,, | |
| 24 | +ch14_nettoyage.py 1.0 (2026-10-31),R1,B0580,exclusion,"doublon d'inscription (même clé d'unité, même promesse, même prix) : inscription la plus récente conservée",,, | |
| 25 | +ch14_nettoyage.py 1.0 (2026-10-31),R1,B0050,exclusion,doublon d'inscription repéré par les attributs (clé d'unité absente ou discordante) : inscription la plus récente conservée,,, | |
| 26 | +ch14_nettoyage.py 1.0 (2026-10-31),R1,B0208,exclusion,doublon d'inscription repéré par les attributs (clé d'unité absente ou discordante) : inscription la plus récente conservée,,, | |
| 27 | +ch14_nettoyage.py 1.0 (2026-10-31),R1,B0274,exclusion,doublon d'inscription repéré par les attributs (clé d'unité absente ou discordante) : inscription la plus récente conservée,,, | |
| 28 | +ch14_nettoyage.py 1.0 (2026-10-31),R1,B0525,exclusion,doublon d'inscription repéré par les attributs (clé d'unité absente ou discordante) : inscription la plus récente conservée,,, | |
| 29 | +ch14_nettoyage.py 1.0 (2026-10-31),R1,B0523,exclusion,doublon d'inscription repéré par les attributs (clé d'unité absente ou discordante) : inscription la plus récente conservée,,, | |
| 25 | 30 | ch14_nettoyage.py 1.0 (2026-10-31),R3,B0077,correction,contrepartie des meubles retranchée du prix,prix_vente,334000.0,305500.0 |
| 26 | 31 | ch14_nettoyage.py 1.0 (2026-10-31),R3,B0132,correction,contrepartie des meubles retranchée du prix,prix_vente,376000.0,341500.0 |
| 27 | 32 | ch14_nettoyage.py 1.0 (2026-10-31),R3,B0225,correction,contrepartie des meubles retranchée du prix,prix_vente,514500.0,481000.0 |
@@ -85,24 +90,24 @@ ch14_nettoyage.py 1.0 (2026-10-31),G1,B0430,imputation,géocodage de repli au ce | ||
| 85 | 90 | ch14_nettoyage.py 1.0 (2026-10-31),G1,B0489,imputation,géocodage de repli au centre du secteur (précision kilométrique) ; variables de distance à utiliser avec prudence,lat;lon,,45.47700;-75.65000 |
| 86 | 91 | ch14_nettoyage.py 1.0 (2026-10-31),G1,B0534,imputation,géocodage de repli au centre du secteur (précision kilométrique) ; variables de distance à utiliser avec prudence,lat;lon,,45.54500;-75.42000 |
| 87 | 92 | ch14_nettoyage.py 1.0 (2026-10-31),G1,B0552,imputation,géocodage de repli au centre du secteur (précision kilométrique) ; variables de distance à utiliser avec prudence,lat;lon,,45.42800;-75.71400 |
| 88 | −ch14_nettoyage.py 1.0 (2026-10-31),I1,B0026,imputation,terrain manquant : imputation (mediane),superficie_terrain_m2,,1030.0 | |
| 93 | +ch14_nettoyage.py 1.0 (2026-10-31),I1,B0026,imputation,terrain manquant : imputation (mediane),superficie_terrain_m2,,1039.5 | |
| 89 | 94 | ch14_nettoyage.py 1.0 (2026-10-31),I1,B0055,imputation,terrain manquant : imputation (mediane),superficie_terrain_m2,,537.5 |
| 90 | 95 | ch14_nettoyage.py 1.0 (2026-10-31),I1,B0063,imputation,terrain manquant : imputation (mediane),superficie_terrain_m2,,538.0 |
| 91 | 96 | ch14_nettoyage.py 1.0 (2026-10-31),I1,B0102,imputation,terrain manquant : imputation (mediane),superficie_terrain_m2,,560.5 |
| 92 | 97 | ch14_nettoyage.py 1.0 (2026-10-31),I1,B0088,imputation,terrain manquant : imputation (mediane),superficie_terrain_m2,,560.5 |
| 93 | 98 | ch14_nettoyage.py 1.0 (2026-10-31),I1,B0145,imputation,terrain manquant : imputation (mediane),superficie_terrain_m2,,536.0 |
| 94 | −ch14_nettoyage.py 1.0 (2026-10-31),I1,B0133,imputation,terrain manquant : imputation (mediane),superficie_terrain_m2,,562.0 | |
| 99 | +ch14_nettoyage.py 1.0 (2026-10-31),I1,B0133,imputation,terrain manquant : imputation (mediane),superficie_terrain_m2,,566.0 | |
| 95 | 100 | ch14_nettoyage.py 1.0 (2026-10-31),I1,B0186,imputation,terrain manquant : imputation (mediane),superficie_terrain_m2,,538.0 |
| 96 | 101 | ch14_nettoyage.py 1.0 (2026-10-31),I1,B0269,imputation,terrain manquant : imputation (mediane),superficie_terrain_m2,,1195.0 |
| 97 | 102 | ch14_nettoyage.py 1.0 (2026-10-31),I1,B0319,imputation,terrain manquant : imputation (mediane),superficie_terrain_m2,,560.5 |
| 98 | −ch14_nettoyage.py 1.0 (2026-10-31),I1,B0351,imputation,terrain manquant : imputation (mediane),superficie_terrain_m2,,562.0 | |
| 103 | +ch14_nettoyage.py 1.0 (2026-10-31),I1,B0351,imputation,terrain manquant : imputation (mediane),superficie_terrain_m2,,566.0 | |
| 99 | 104 | ch14_nettoyage.py 1.0 (2026-10-31),I1,B0368,imputation,terrain manquant : imputation (mediane),superficie_terrain_m2,,560.5 |
| 100 | 105 | ch14_nettoyage.py 1.0 (2026-10-31),I1,B0415,imputation,terrain manquant : imputation (mediane),superficie_terrain_m2,,560.5 |
| 101 | 106 | ch14_nettoyage.py 1.0 (2026-10-31),I1,B0431,imputation,terrain manquant : imputation (mediane),superficie_terrain_m2,,537.5 |
| 102 | 107 | ch14_nettoyage.py 1.0 (2026-10-31),I1,B0454,imputation,terrain manquant : imputation (mediane),superficie_terrain_m2,,537.5 |
| 103 | −ch14_nettoyage.py 1.0 (2026-10-31),I1,B0471,imputation,terrain manquant : imputation (mediane),superficie_terrain_m2,,540.5 | |
| 104 | −ch14_nettoyage.py 1.0 (2026-10-31),I1,B0477,imputation,terrain manquant : imputation (mediane),superficie_terrain_m2,,562.0 | |
| 105 | −ch14_nettoyage.py 1.0 (2026-10-31),I1,B0533,imputation,terrain manquant : imputation (mediane),superficie_terrain_m2,,1030.0 | |
| 108 | +ch14_nettoyage.py 1.0 (2026-10-31),I1,B0471,imputation,terrain manquant : imputation (mediane),superficie_terrain_m2,,542.0 | |
| 109 | +ch14_nettoyage.py 1.0 (2026-10-31),I1,B0477,imputation,terrain manquant : imputation (mediane),superficie_terrain_m2,,566.0 | |
| 110 | +ch14_nettoyage.py 1.0 (2026-10-31),I1,B0533,imputation,terrain manquant : imputation (mediane),superficie_terrain_m2,,1039.5 | |
| 106 | 111 | ch14_nettoyage.py 1.0 (2026-10-31),I1,B0543,imputation,terrain manquant : imputation (mediane),superficie_terrain_m2,,536.0 |
| 107 | 112 | ch14_nettoyage.py 1.0 (2026-10-31),I1,B0560,imputation,terrain manquant : imputation (mediane),superficie_terrain_m2,,560.5 |
| 108 | 113 | ch14_nettoyage.py 1.0 (2026-10-31),I1,B0047,imputation,qualite manquant : imputation (mediane),qualite_1a5,,3.0 |
@@ -122,7 +127,7 @@ ch14_nettoyage.py 1.0 (2026-10-31),I1,B0383,imputation,qualite manquant : imputa | ||
| 122 | 127 | ch14_nettoyage.py 1.0 (2026-10-31),I1,B0460,imputation,qualite manquant : imputation (mediane),qualite_1a5,,3.0 |
| 123 | 128 | ch14_nettoyage.py 1.0 (2026-10-31),I1,B0051,imputation,annee manquant : imputation (mediane),annee_construction,,1985.0 |
| 124 | 129 | ch14_nettoyage.py 1.0 (2026-10-31),I1,B0079,imputation,annee manquant : imputation (mediane),annee_construction,,1985.0 |
| 125 | −ch14_nettoyage.py 1.0 (2026-10-31),I1,B0128,imputation,annee manquant : imputation (mediane),annee_construction,,1983.5 | |
| 130 | +ch14_nettoyage.py 1.0 (2026-10-31),I1,B0128,imputation,annee manquant : imputation (mediane),annee_construction,,1984.0 | |
| 126 | 131 | ch14_nettoyage.py 1.0 (2026-10-31),I1,B0217,imputation,annee manquant : imputation (mediane),annee_construction,,1987.0 |
| 127 | 132 | ch14_nettoyage.py 1.0 (2026-10-31),I1,B0258,imputation,annee manquant : imputation (mediane),annee_construction,,1987.0 |
| 128 | 133 | ch14_nettoyage.py 1.0 (2026-10-31),I1,B0290,imputation,annee manquant : imputation (mediane),annee_construction,,1985.0 |
@@ -131,10 +136,10 @@ ch14_nettoyage.py 1.0 (2026-10-31),I1,B0365,imputation,annee manquant : imputati | ||
| 131 | 136 | ch14_nettoyage.py 1.0 (2026-10-31),I1,B0375,imputation,annee manquant : imputation (mediane),annee_construction,,1986.0 |
| 132 | 137 | ch14_nettoyage.py 1.0 (2026-10-31),I1,B0388,imputation,annee manquant : imputation (mediane),annee_construction,,1987.0 |
| 133 | 138 | ch14_nettoyage.py 1.0 (2026-10-31),S1,B0003,exclusion,"score robuste |z| > 3.5 (prix au m² par secteur, superficie ou terrain)",z_prix_m2;z_superficie;z_terrain,,-3.51;2.14;-2.06 |
| 134 | −ch14_nettoyage.py 1.0 (2026-10-31),S1,B0006,exclusion,"score robuste |z| > 3.5 (prix au m² par secteur, superficie ou terrain)",z_prix_m2;z_superficie;z_terrain,,-5.33;0.50;-0.34 | |
| 135 | −ch14_nettoyage.py 1.0 (2026-10-31),S1,B0105,exclusion,"score robuste |z| > 3.5 (prix au m² par secteur, superficie ou terrain)",z_prix_m2;z_superficie;z_terrain,,-5.31;-0.78;0.27 | |
| 136 | −ch14_nettoyage.py 1.0 (2026-10-31),S1,B0173,exclusion,"score robuste |z| > 3.5 (prix au m² par secteur, superficie ou terrain)",z_prix_m2;z_superficie;z_terrain,,-6.49;-0.61;0.37 | |
| 139 | +ch14_nettoyage.py 1.0 (2026-10-31),S1,B0006,exclusion,"score robuste |z| > 3.5 (prix au m² par secteur, superficie ou terrain)",z_prix_m2;z_superficie;z_terrain,,-5.47;0.50;-0.34 | |
| 140 | +ch14_nettoyage.py 1.0 (2026-10-31),S1,B0021,exclusion,"score robuste |z| > 3.5 (prix au m² par secteur, superficie ou terrain)",z_prix_m2;z_superficie;z_terrain,,-3.50;0.63;1.05 | |
| 141 | +ch14_nettoyage.py 1.0 (2026-10-31),S1,B0105,exclusion,"score robuste |z| > 3.5 (prix au m² par secteur, superficie ou terrain)",z_prix_m2;z_superficie;z_terrain,,-5.32;-0.78;0.26 | |
| 142 | +ch14_nettoyage.py 1.0 (2026-10-31),S1,B0173,exclusion,"score robuste |z| > 3.5 (prix au m² par secteur, superficie ou terrain)",z_prix_m2;z_superficie;z_terrain,,-6.50;-0.61;0.37 | |
| 137 | 143 | ch14_nettoyage.py 1.0 (2026-10-31),S1,B0558,exclusion,"score robuste |z| > 3.5 (prix au m² par secteur, superficie ou terrain)",z_prix_m2;z_superficie;z_terrain,,0.19;0.84;-3.92 |
| 138 | −ch14_nettoyage.py 1.0 (2026-10-31),E1,B0021,exclusion,résidu studentisé externe |t| > 3.0 (itération 1),t_ext;cook;levier,,-6.32;0.094;0.039 | |
| 139 | −ch14_nettoyage.py 1.0 (2026-10-31),E1,B0039,exclusion,résidu studentisé externe |t| > 3.0 (itération 1),t_ext;cook;levier,,-5.55;0.075;0.040 | |
| 140 | −ch14_nettoyage.py 1.0 (2026-10-31),E1,B0410,exclusion,résidu studentisé externe |t| > 3.0 (itération 1),t_ext;cook;levier,,-6.50;0.045;0.018 | |
| 144 | +ch14_nettoyage.py 1.0 (2026-10-31),E1,B0039,exclusion,résidu studentisé externe |t| > 3.0 (itération 1),t_ext;cook;levier,,-5.84;0.084;0.040 | |
| 145 | +ch14_nettoyage.py 1.0 (2026-10-31),E1,B0410,exclusion,résidu studentisé externe |t| > 3.0 (itération 1),t_ext;cook;levier,,-6.70;0.048;0.018 | |
modified
manuscript/chapters/ch18.tex
+2 −1
@@ -215,7 +215,8 @@ Secteur Buckingham & -0.2504 & 0.0718 & -3.5 & -22.15 & -0.1844 \\ | ||
| 215 | 215 | Secteur Masson-Angers & -0.2631 & 0.0672 & -3.9 & -23.14 & -0.2044 \\ |
| 216 | 216 | Mois depuis le 1\ier{} juillet 2024 & 0.0036 & 0.0003 & 13.5 & 0.36 & 0.0036 \\ |
| 217 | 217 | \midrule |
| 218 | −\multicolumn{6}{@{}l}{$n = 600$ ; $k = 16$ ; $R^2 = 0{,}853$ ; $R^2$ ajusté $= 0{,}849$ ; $\hat\sigma = 0{,}083$ (vrai : 0,085) ; 15 paramètres sur 15 dans l'IC à 95 \%} \\ | |
| 218 | +\multicolumn{6}{@{}l}{$n = 600$ ; $k = 16$ ; $R^2 = 0{,}853$ ; $R^2$ ajusté $= 0{,}849$ ; $\hat\sigma = 0{,}083$ (vrai : 0,085)} \\ | |
| 219 | +\multicolumn{6}{@{}l}{15 paramètres de génération sur 15 dans l'intervalle de confiance à 95 \%} \\ | |
| 219 | 220 | \bottomrule |
| 220 | 221 | \end{tabular} |
| 221 | 222 | \tabsource{\synth{\texttt{traite.cas1.hedonique} ; \texttt{analysis/python/cases/ch18\_hedonique.py}}. Effet (\%) : $100\,[\exp(\hat\beta) - 1]$ pour une unité ; les primes de secteur vraies sont $\ln$(médiane APCIQ du secteur / médiane de Hull), Masson-Angers moins 0,02.} |
modified
manuscript/chapters/ch19.tex
+136 −1
@@ -1,2 +1,137 @@ | ||
| 1 | 1 | \chapter{Modèles spatiaux et temporels}\label{ch:19} |
| 2 | −\stubchapter | |
| 2 | + | |
| 3 | +Le modèle hédonique du \cref{ch:18} traite chaque vente comme une observation isolée : le prix d'une maison dépend de ses caractéristiques, d'une date et d'un terme aléatoire indépendant de celui de la maison voisine. Or rien n'est moins indépendant que deux maisons de la même rue. Elles partagent l'école, le bruit de l'autoroute, la réputation du quartier, le même bassin d'acheteurs et souvent le même courtier ; leurs prix se répondent, et ce qui n'est pas mesuré pour l'une ne l'est pas non plus pour l'autre. Le présent chapitre introduit les outils qui prennent cette interdépendance au sérieux. Il distingue d'abord deux phénomènes que le langage courant confond, la \emph{dépendance} spatiale, qui porte sur les résidus, et l'\emph{hétérogénéité} spatiale, qui porte sur les coefficients ; il construit ensuite les matrices de pondération qui formalisent la notion de voisinage, y compris dans sa dimension temporelle, puis il présente les tests, les modèles autorégressifs, la régression géographiquement pondérée, les effets fixes et le krigeage. L'exemple intégral reprend les 600 ventes du dossier de Gatineau et pose une question simple que l'évaluateur doit se poser devant tout modèle : les résidus sont-ils encore corrélés dans l'espace, et si oui, qu'est-ce que cela révèle ? | |
| 4 | + | |
| 5 | +\begin{objectifs} | |
| 6 | +\begin{itemize} | |
| 7 | +\item Distinguer la dépendance spatiale de l'hétérogénéité spatiale et reconnaître les conséquences de chacune pour l'estimation par moindres carrés ordinaires. | |
| 8 | +\item Construire et standardiser une matrice de pondération spatiale (contiguïté, plus proches voisins, distance inverse) et une matrice spatio-temporelle qui ne fait parler que les ventes antérieures. | |
| 9 | +\item Tester la dépendance spatiale des résidus par la statistique $I$ de Moran, le corrélogramme et les tests du multiplicateur de Lagrange, et en déduire la forme de modèle appropriée. | |
| 10 | +\item Estimer par maximum de vraisemblance les modèles à décalage spatial (SAR), à erreur spatiale (SEM) et de Durbin spatial (SDM), et interpréter correctement leurs effets directs, indirects et totaux. | |
| 11 | +\item Estimer une régression géographiquement pondérée, choisir sa bande passante par validation croisée et cartographier les coefficients locaux sans sur-interpréter leurs variations. | |
| 12 | +\item Modéliser le résidu de localisation par un variogramme et un krigeage ordinaire, et comparer les indications de valeur qui en résultent pour un sujet. | |
| 13 | +\end{itemize} | |
| 14 | +\end{objectifs} | |
| 15 | + | |
| 16 | +\section{Dépendance et hétérogénéité spatiales}\label{sec:19:dependance} | |
| 17 | + | |
| 18 | +\subsection{Pourquoi l'espace compte} | |
| 19 | + | |
| 20 | +Trois mécanismes font que les prix de maisons voisines ne sont pas indépendants. Le premier est celui des \emph{externalités de voisinage} : la qualité de l'école, l'entretien des propriétés adjacentes, la vue, la proximité d'un parc ou d'une artère bruyante affectent simultanément toutes les maisons d'un même secteur, et ces attributs ne figurent presque jamais dans les bases de données de transactions. Le deuxième est celui des \emph{sous-marchés} : les acheteurs raisonnent par quartier, les courtiers aussi, et un même panier de caractéristiques ne se négocie pas au même prix à Aylmer et à Masson-Angers ; la segmentation étudiée au \cref{ch:15} est la forme la plus grossière de la dépendance spatiale, celle que l'on capte par des indicatrices de secteur. Le troisième mécanisme est propre au processus de formation des prix : vendeurs, acheteurs, courtiers et évaluateurs fixent leurs attentes à partir des ventes récentes du voisinage, de sorte que le prix d'une maison dépend \emph{directement} des prix auxquels ses voisines se sont vendues peu avant \parencite{desrosiersDubeTheriault2011}. Ce dernier mécanisme a une direction temporelle : ce sont les ventes antérieures qui informent les ventes postérieures, non l'inverse, et le \cref{sec:19:poids} en tirera les conséquences. | |
| 21 | + | |
| 22 | +La formule de Tobler, selon laquelle tout interagit avec tout mais les choses proches interagissent davantage que les choses éloignées, résume ces mécanismes, mais elle les confond aussi. Pour l'économètre, il importe de séparer deux situations qui appellent des remèdes différents. | |
| 23 | + | |
| 24 | +\begin{definition}{Dépendance spatiale}{dependance-spatiale} | |
| 25 | +Il y a \motcle{dépendance spatiale} (ou autocorrélation spatiale) lorsque la covariance entre les termes d'erreur, ou entre les variables dépendantes, de deux observations est une fonction non nulle de leur position relative dans l'espace : $\Cov[\varepsilon_i, \varepsilon_j] \neq 0$ pour des observations $i$ et $j$ voisines. Elle est positive si des résidus de même signe tendent à se regrouper, négative s'ils alternent. | |
| 26 | +\end{definition} | |
| 27 | + | |
| 28 | +\begin{definition}{Hétérogénéité spatiale}{heterogeneite-spatiale} | |
| 29 | +Il y a \motcle{hétérogénéité spatiale} lorsque la relation entre le prix et les caractéristiques varie d'un lieu à l'autre : les coefficients $\boldsymbol\beta$, la variance $\sigma^2$ ou la forme fonctionnelle dépendent de la position. L'élasticité du prix à la superficie peut être plus faible dans un quartier central dense que dans une banlieue pavillonnaire ; la prime du garage peut être nulle là où le stationnement sur rue est abondant. | |
| 30 | +\end{definition} | |
| 31 | + | |
| 32 | +Les deux phénomènes coexistent souvent et l'un peut se déguiser en l'autre : une hétérogénéité ignorée produit des résidus corrélés dans l'espace, et une dépendance ignorée peut faire croire à des coefficients instables lorsqu'on estime le modèle par sous-échantillons. La \cref{fig:19:taxonomie} ordonne les modèles de ce chapitre selon le phénomène qu'ils visent et la manière dont ils le traitent. Les modèles à effets fixes et les modèles autorégressifs traitent la dépendance ; la régression géographiquement pondérée traite l'hétérogénéité ; le krigeage traite la dépendance résiduelle en vue de la prédiction. \textcite{anselin1988} a fixé le cadre d'ensemble, \textcite{lesagePace2009} l'interprétation des modèles autorégressifs, et \textcite{dubeLegros2014} ont adapté ces outils aux microdonnées immobilières, qui ont la particularité d'être localisées en des points irréguliers et datées. | |
| 33 | + | |
| 34 | +\begin{figure}[htbp] | |
| 35 | +\centering | |
| 36 | +\begin{tikzpicture}[node distance=5mm and 6mm, every node/.style={font=\sffamily\footnotesize}, boite/.style={draw=brandPrimary, fill=brandLight, rounded corners=1pt, align=center, minimum height=9mm, text width=36mm}, racine/.style={boite, fill=brandSecondary!35, text width=46mm}, feuille/.style={boite, fill=white, text width=30mm, minimum height=11mm}, fleche/.style={-{Stealth[length=2mm]}, brandGray, thick}] | |
| 37 | +\node[racine] (r) {Effets spatiaux dans un modèle hédonique}; | |
| 38 | +\node[boite, below left=of r, xshift=-14mm] (d) {\textbf{Dépendance}\\covariance des résidus ou des prix entre voisins}; | |
| 39 | +\node[boite, below right=of r, xshift=14mm] (h) {\textbf{Hétérogénéité}\\coefficients qui varient avec le lieu}; | |
| 40 | +\node[feuille, below left=of d, xshift=4mm] (fe) {Effets fixes de quartier\\(\cref{sec:19:effets-fixes})}; | |
| 41 | +\node[feuille, below=of d] (ar) {SAR, SEM, SDM\\(\cref{sec:19:modeles})}; | |
| 42 | +\node[feuille, below right=of d, xshift=-4mm] (kr) {Krigeage des résidus\\(\cref{sec:19:krigeage})}; | |
| 43 | +\node[feuille, below left=of h, xshift=4mm] (seg) {Sous-marchés, interactions\\(\cref{ch:15}, \cref{ch:18})}; | |
| 44 | +\node[feuille, below right=of h, xshift=-4mm] (gwr) {Régression géographiquement pondérée (\cref{sec:19:gwr})}; | |
| 45 | +\foreach \a/\b in {r/d, r/h, d/fe, d/ar, d/kr, h/seg, h/gwr} \draw[fleche] (\a) -- (\b); | |
| 46 | +\node[below=3mm of ar, text=brandGray, align=center] {Diagnostic commun : $I$ de Moran, corrélogramme, tests LM (\cref{sec:19:tests}) ; matrice $\mathbf{W}$ (\cref{sec:19:poids})}; | |
| 47 | +\end{tikzpicture} | |
| 48 | +\caption{Les deux familles d'effets spatiaux et les outils qui leur répondent. La dépendance se diagnostique sur les résidus et se traite par des effets fixes, des modèles autorégressifs ou le krigeage ; l'hétérogénéité se traite par la segmentation ou par des coefficients locaux.}\label{fig:19:taxonomie} | |
| 49 | +\figsource{schéma de l'auteur, d'après \textcite{anselin1988} et \textcite{dubeLegros2014}.} | |
| 50 | +\end{figure} | |
| 51 | + | |
| 52 | +\subsection{Le modèle hédonique avec effets spatiaux et temporels} | |
| 53 | + | |
| 54 | +L'écriture générale retenue dans l'ouvrage pour le modèle hédonique, | |
| 55 | +\begin{equation}\label{eq:19:hedonique} | |
| 56 | +\ln P_i = \alpha + \mathbf{x}_i\T\boldsymbol\beta + \tau_{t(i)} + \mu_{s(i)} + \varepsilon_i , | |
| 57 | +\end{equation} | |
| 58 | +contient déjà un effet temporel $\tau_{t(i)}$ et un effet spatial $\mu_{s(i)}$. Tout ce chapitre peut se lire comme une discussion de la nature de $\mu$. Dans le modèle de référence du cas 1 (\texttt{traite.cas1.hedonique}), $\mu_{s(i)}$ est un effet fixe de secteur, cinq constantes pour les cinq secteurs de Gatineau, complété par un gradient de distance au centre. Cette spécification suppose que la localisation agit par paliers, identiques pour toutes les maisons d'un secteur, et qu'à l'intérieur d'un secteur les résidus sont indépendants. Les modèles de ce chapitre relâchent ces deux hypothèses : $\mu$ peut être une surface continue (krigeage, régression géographiquement pondérée), un processus autorégressif qui lie chaque résidu à ceux de ses voisins (modèle à erreur spatiale), ou encore une dépendance directe du prix aux prix voisins (modèle à décalage spatial). De même, $\tau$ peut être une tendance linéaire, une suite d'indicatrices trimestrielles, ou une pondération des ventes antérieures qui décroît avec leur ancienneté. | |
| 59 | + | |
| 60 | +\subsection{Ce que la dépendance fait aux moindres carrés} | |
| 61 | + | |
| 62 | +Les conséquences de la dépendance spatiale pour l'estimateur des moindres carrés ordinaires dépendent de sa forme. Si la dépendance réside dans le terme d'erreur, comme dans le modèle à erreur spatiale de la \cref{sec:19:modeles}, les coefficients MCO restent sans biais et convergents, mais ils ne sont plus efficaces et, surtout, leurs erreurs-types habituelles sont fausses : avec une autocorrélation positive, chaque observation apporte moins d'information qu'une observation indépendante, l'échantillon « effectif » est plus petit que $n$, et les erreurs-types usuelles, même robustes à l'hétéroscédasticité, sous-estiment l'incertitude. Les intervalles de confiance sur les prix implicites, et donc sur les ajustements qu'on en dérive au \cref{ch:17}, sont trop étroits. | |
| 63 | + | |
| 64 | +Si la dépendance porte sur la variable dépendante elle-même, c'est-à-dire si le prix d'une maison dépend des prix des maisons voisines, la situation est plus grave. Écrivons $\mathbf{y} = \rho\mathbf{W}\mathbf{y} + \mathbf{X}\boldsymbol\beta + \boldsymbol\varepsilon$, où $\mathbf{W}\mathbf{y}$ est le vecteur des moyennes des prix voisins (la matrice $\mathbf{W}$ est définie à la \cref{sec:19:poids}). Le régresseur $\mathbf{W}\mathbf{y}$ est corrélé avec $\boldsymbol\varepsilon$, parce que le prix de la voisine dépend de son propre terme d'erreur, lequel se répercute sur le prix du sujet, et ainsi de suite. L'estimateur MCO de $\rho$ est alors biaisé et non convergent, et le biais contamine $\hat{\boldsymbol\beta}$. Omettre purement et simplement $\mathbf{W}\mathbf{y}$ ne vaut guère mieux : la variable omise est corrélée avec $\mathbf{X}$ par l'intermédiaire des caractéristiques des voisines, et les coefficients absorbent une part de l'effet de voisinage. Le \cref{tab:19:coefficients} illustrera le phénomène : le coefficient du terrain passe du simple au double selon que la localisation est ou non modélisée. | |
| 65 | + | |
| 66 | +La leçon pratique est double. D'abord, un diagnostic de dépendance spatiale sur les résidus fait partie des vérifications normales d'un modèle hédonique, au même titre que l'hétéroscédasticité ou la multicolinéarité du \cref{ch:18}. Ensuite, la forme de la dépendance détermine le remède : les tests de la \cref{sec:19:tests} sont construits pour la révéler. | |
| 67 | + | |
| 68 | +\section{Les matrices de pondération spatiale}\label{sec:19:poids} | |
| 69 | + | |
| 70 | +\subsection{Formaliser le voisinage} | |
| 71 | + | |
| 72 | +Toute l'économétrie spatiale repose sur une \motcle{matrice de pondération spatiale} $\mathbf{W}$, carrée d'ordre $n$, dont l'élément $w_{ij}$ mesure l'intensité du lien entre les observations $i$ et $j$ ; sa diagonale est nulle, une observation n'étant pas sa propre voisine. Trois constructions dominent pour les microdonnées immobilières, où chaque vente est un point. La \emph{contiguïté}, naturelle pour des polygones (municipalités, secteurs de recensement), s'applique aux points par l'intermédiaire d'une triangulation ou des lots cadastraux ; elle est rarement disponible. Les \emph{$k$ plus proches voisins} attribuent un poids de un aux $k$ ventes les plus proches de $i$ et zéro aux autres : chaque observation a exactement $k$ voisines, ce qui évite les observations isolées, mais le rayon de voisinage varie avec la densité des ventes. La \emph{distance inverse} attribue un poids $d_{ij}^{-\gamma}$ à toutes les ventes situées à moins d'une portée $d_{\max}$ : le voisinage a une étendue fixe, mais le nombre de voisines varie, et une portée trop courte laisse des lignes vides. | |
| 73 | + | |
| 74 | +La matrice est presque toujours \motcle{standardisée en ligne}, | |
| 75 | +\begin{equation}\label{eq:19:standardisation} | |
| 76 | +w_{ij} = \frac{w^{*}_{ij}}{\sum_{l=1}^{n} w^{*}_{il}}, \qquad \sum_{j} w_{ij} = 1 , | |
| 77 | +\end{equation} | |
| 78 | +de sorte que le \motcle{décalage spatial} $(\mathbf{W}\mathbf{y})_i = \sum_j w_{ij} y_j$ est une moyenne pondérée des valeurs voisines, comparable d'une observation à l'autre. La standardisation a deux conséquences mathématiques utiles. La plus grande valeur propre de $\mathbf{W}$ vaut alors exactement un, de sorte que le paramètre autorégressif $\rho$ des modèles de la \cref{sec:19:modeles} est contraint à l'intervalle $(1/\omega_{\min}, 1)$, où $\omega_{\min}$ est la plus petite valeur propre réelle ; et le vecteur unité est un vecteur propre de $\mathbf{W}$, $\mathbf{W}\mathbf{1} = \mathbf{1}$, ce qui simplifie le calcul des effets totaux (\cref{prop:effet-total}). En revanche, une matrice standardisée en ligne n'est plus symétrique, et les valeurs propres d'une matrice de $k$ plus proches voisins peuvent être complexes ; le déterminant jacobien qui intervient dans la vraisemblance se calcule alors avec les modules, $\abs{\det(\mathbf{I} - \rho\mathbf{W})} = \prod_i \abs{1 - \rho\,\omega_i}$. | |
| 79 | + | |
| 80 | +Les coordonnées des ventes doivent être exprimées dans un système métrique avant tout calcul de distance. Des latitudes et longitudes en degrés ne s'y prêtent pas, un degré de longitude valant à Gatineau environ 78 kilomètres contre 111 pour un degré de latitude. Une projection équirectangulaire locale, $x = (\lambda - \lambda_0)\,111{,}32\cos\varphi_0$ et $y = (\varphi - \varphi_0)\,110{,}57$ en kilomètres autour d'un point de référence $(\varphi_0, \lambda_0)$, suffit à l'échelle d'une agglomération ; au-delà, on utilise la projection officielle du Québec (\cref{ch:38}). Rappelons que les coordonnées du cas 1 sont synthétiques et que les cartes de ce chapitre ne représentent aucun lieu réel ; les questions de géocodage et de confidentialité des adresses sont traitées au \cref{ch:14}. | |
| 81 | + | |
| 82 | +\subsection{La matrice spatio-temporelle de Dubé et Legros} | |
| 83 | + | |
| 84 | +Les ventes immobilières ne sont pas seulement localisées, elles sont datées, et une matrice purement spatiale fait parler une vente de 2026 pour expliquer une vente de 2023, ce qui est absurde pour le troisième mécanisme de la \cref{sec:19:dependance}. \textcite{dubeLegros2013} ont proposé de combiner une pondération spatiale $s_{ij}$ et une pondération temporelle $t_{ij}$ en une matrice \motcle{spatio-temporelle} | |
| 85 | +\begin{equation}\label{eq:19:spatio-temporelle} | |
| 86 | +w^{*}_{ij} = s_{ij}\, t_{ij}, \qquad s_{ij} = d_{ij}^{-\gamma}\,\mathbb{1}\{0 < d_{ij} \leq d_{\max}\}, \qquad t_{ij} = \mathbb{1}\{0 < t_i - t_j \leq T\}, | |
| 87 | +\end{equation} | |
| 88 | +où $t_i - t_j$ est le délai, en mois, entre la vente $j$ et la vente $i$. Le facteur temporel est \emph{unidirectionnel} : seules les ventes conclues avant $i$, et depuis moins de $T$ mois, pèsent sur $i$. On peut lui substituer une fonction décroissante du délai, et \textcite{dubeLegros2014} discutent plusieurs choix ; la forme indicatrice suffit ici. La matrice est ensuite standardisée en ligne par l'\cref{eq:19:standardisation}. Deux particularités en découlent. Les premières ventes de la période, et les ventes isolées, n'ont aucune voisine antérieure : leur ligne est nulle et leur décalage vaut zéro, ce qui oblige soit à les exclure de l'estimation du modèle à décalage, soit à les conserver seulement comme voisines des ventes suivantes. Surtout, si les observations sont ordonnées par date, la matrice est strictement triangulaire inférieure. | |
| 89 | + | |
| 90 | +\begin{proposition}{Jacobien d'une matrice spatio-temporelle unidirectionnelle}{jacobien-st} | |
| 91 | +Si $\mathbf{W}$ est construite selon l'\cref{eq:19:spatio-temporelle} avec un facteur temporel unidirectionnel, alors toutes ses valeurs propres sont nulles et $\det(\mathbf{I} - \rho\mathbf{W}) = 1$ pour tout $\rho$. | |
| 92 | +\end{proposition} | |
| 93 | +\begin{proof} | |
| 94 | +Ordonnons les observations par date de vente croissante. Pour $j \geq i$, la vente $j$ n'est pas antérieure à $i$, donc $t_{ij} = 0$ et $w_{ij} = 0$ : la matrice est strictement triangulaire inférieure. La standardisation en ligne ne modifie pas la position des zéros. Les valeurs propres d'une matrice triangulaire sont ses éléments diagonaux, tous nuls, et celles de $\mathbf{I} - \rho\mathbf{W}$ valent toutes un ; le déterminant, produit des valeurs propres, vaut un. Le résultat ne dépend pas de l'ordre choisi, le déterminant étant invariant par permutation simultanée des lignes et des colonnes. | |
| 95 | +\end{proof} | |
| 96 | + | |
| 97 | +La proposition a une conséquence pratique importante : le terme jacobien de la vraisemblance disparaît, et le modèle à décalage spatio-temporel s'estime par moindres carrés ordinaires sur $[\mathbf{W}\mathbf{y}, \mathbf{X}]$, le décalage étant \emph{prédéterminé} au sens des séries chronologiques. Cette simplicité a un prix : l'estimateur n'est convergent que si les erreurs ne sont pas elles-mêmes autocorrélées dans l'espace, car la vente antérieure voisine porte le même résidu de quartier que le sujet. L'exemple de la \cref{sec:19:exemple} montrera que, dans le jeu du cas 1, le décalage spatio-temporel capte une partie seulement de la dépendance résiduelle. | |
| 98 | + | |
| 99 | +\subsection{Choisir la matrice} | |
| 100 | + | |
| 101 | +Le choix de $\mathbf{W}$ est la décision la plus discutée et la moins théorisée de l'économétrie spatiale. Aucun test ne désigne la « bonne » matrice ; le critère d'information et la vraisemblance permettent de comparer des matrices entre elles pour un même modèle, mais la comparaison est fragile et les paramètres autorégressifs ne sont pas comparables d'une matrice à l'autre, un fait que la \cref{sec:19:sensibilite} illustrera. En pratique, on construit la matrice à partir de ce que l'on sait du mécanisme : quelques plus proches voisins si l'on pense aux comparables qu'un acheteur regarde, une portée fixe si l'on pense à une externalité physique dont l'effet s'éteint avec la distance, une pondération spatio-temporelle si l'on pense à l'ancrage sur les ventes récentes. Et l'on vérifie que les conclusions ne dépendent pas de choix raisonnables différents. Dans ce chapitre, la matrice de référence est celle des huit plus proches voisins, standardisée en ligne ; les variantes à quatre, six, douze et seize voisins et les matrices de distance inverse à deux, trois et quatre kilomètres servent à l'analyse de sensibilité. | |
| 102 | + | |
| 103 | +\section{Tester la dépendance spatiale}\label{sec:19:tests} | |
| 104 | + | |
| 105 | +\subsection{La statistique \texorpdfstring{$I$}{I} de Moran} | |
| 106 | + | |
| 107 | +La statistique de Moran est au spatial ce que le coefficient d'autocorrélation d'ordre un est aux séries chronologiques. Pour une variable $z_i = x_i - \bar{x}$ centrée et une matrice $\mathbf{W}$ de somme $S_0 = \sum_i\sum_j w_{ij}$, | |
| 108 | +\begin{equation}\label{eq:19:moran} | |
| 109 | +I = \frac{n}{S_0}\,\frac{\mathbf{z}\T\mathbf{W}\mathbf{z}}{\mathbf{z}\T\mathbf{z}} = \frac{n}{S_0}\,\frac{\sum_i\sum_j w_{ij}\,z_i z_j}{\sum_i z_i^2} . | |
| 110 | +\end{equation} | |
| 111 | +Avec une matrice standardisée en ligne, $S_0 = n$ et $I$ est la pente de la régression du décalage spatial $\mathbf{W}\mathbf{z}$ sur $\mathbf{z}$ : c'est la lecture graphique du « diagramme de Moran ». Sous l'hypothèse nulle d'absence d'autocorrélation et de normalité des $z_i$, l'espérance et la variance sont connues \parencite{anselin1988} : | |
| 112 | +\begin{equation}\label{eq:19:moran-moments} | |
| 113 | +\E[I] = -\frac{1}{n-1}, \qquad \Var[I] = \frac{n^2 S_1 - n S_2 + 3 S_0^2}{(n^2 - 1)\, S_0^2} - \E[I]^2 , | |
| 114 | +\end{equation} | |
| 115 | +avec $S_1 = \tfrac12\sum_i\sum_j (w_{ij} + w_{ji})^2$ et $S_2 = \sum_i\big(\sum_j w_{ij} + \sum_j w_{ji}\big)^2$. La statistique centrée réduite $z_I = (I - \E[I])/\sqrt{\Var[I]}$ est comparée à la loi normale. L'espérance sous l'hypothèse nulle est légèrement négative, $-1/599 \approx -0{,}0017$ pour le cas 1, ce qui rappelle qu'un $I$ nul n'est pas tout à fait l'absence de dépendance. Une inférence par permutations, qui recalcule $I$ après avoir mélangé aléatoirement les valeurs entre les positions, dispense de l'hypothèse de normalité ; les deux approches donnent des résultats très voisins pour 600 observations. | |
| 116 | + | |
| 117 | +Appliquée aux résidus d'une régression, la statistique de Moran garde son interprétation descriptive, mais ses moments sous l'hypothèse nulle sont modifiés par le fait que les résidus MCO sont des combinaisons linéaires des erreurs, contraintes par les régresseurs ; \textcite{anselin1988} en donne la version corrigée. L'écart est faible lorsque $n$ est grand par rapport au nombre de régresseurs, et la version simple de l'\cref{eq:19:moran-moments} est celle qu'utilise le script de ce chapitre ; la version corrigée donne, pour les résidus du cas 1 sans indicatrices de secteur, une statistique de 17,3 contre 17,1. | |
| 118 | + | |
| 119 | +Le \motcle{corrélogramme} spatial raffine le diagnostic en calculant $I$ pour une suite de matrices binaires, chacune ne retenant que les paires dont la distance tombe dans une bande donnée. Il montre à quelle distance la dépendance s'éteint, et il révèle des structures que la statistique globale masque : une dépendance forte jusqu'à quelques kilomètres puis négative au-delà signale des sous-marchés de taille finie plutôt qu'une décroissance continue, comme on le verra à la \cref{fig:19:correlogramme}. | |
| 120 | + | |
| 121 | +\subsection{Les tests du multiplicateur de Lagrange} | |
| 122 | + | |
| 123 | +La statistique de Moran détecte la dépendance mais ne dit pas si elle vient du terme d'erreur ou de la variable dépendante. Les tests du \motcle{multiplicateur de Lagrange} (LM), calculés à partir des seuls résidus MCO $\mathbf{e}$, répondent à cette question \parencite{anselin1988}. Avec $\hat\sigma^2 = \mathbf{e}\T\mathbf{e}/n$, $T = \operatorname{tr}[(\mathbf{W}\T + \mathbf{W})\mathbf{W}]$, $\mathbf{M} = \mathbf{I} - \mathbf{X}(\mathbf{X}\T\mathbf{X})^{-1}\mathbf{X}\T$ et $R_J = (\mathbf{W}\mathbf{X}\hat{\boldsymbol\beta})\T\mathbf{M}(\mathbf{W}\mathbf{X}\hat{\boldsymbol\beta})/\hat\sigma^2 + T$, le test de l'hypothèse $\lambda = 0$ dans le modèle à erreur spatiale et le test de $\rho = 0$ dans le modèle à décalage s'écrivent | |
| 124 | +\begin{equation}\label{eq:19:lm} | |
| 125 | +\mathrm{LM}_{\text{err}} = \frac{\left(\mathbf{e}\T\mathbf{W}\mathbf{e}/\hat\sigma^2\right)^2}{T}, \qquad | |
| 126 | +\mathrm{LM}_{\text{lag}} = \frac{\left(\mathbf{e}\T\mathbf{W}\mathbf{y}/\hat\sigma^2\right)^2}{R_J} , | |
| 127 | +\end{equation} | |
| 128 | +chacun suivant asymptotiquement une loi du $\chi^2$ à un degré de liberté sous son hypothèse nulle. Le numérateur du premier est, à un facteur près, le carré de la statistique de Moran des résidus ; celui du second mesure la corrélation entre les résidus et le décalage de la variable dépendante. Le problème est que chaque test rejette aussi en présence de l'autre forme de dépendance. Les versions \emph{robustes}, dues à Anselin et à ses coauteurs et présentées dans \textcite{elhorst2014}, corrigent chaque statistique de la présence éventuelle de l'autre forme : | |
| 129 | +\begin{equation}\label{eq:19:rlm} | |
| 130 | +\mathrm{RLM}_{\text{lag}} = \frac{\left(d_\rho - d_\lambda\right)^2}{R_J - T}, \qquad | |
| 131 | +\mathrm{RLM}_{\text{err}} = \frac{\left(d_\lambda - T R_J^{-1} d_\rho\right)^2}{T\left(1 - T R_J^{-1}\right)} , | |
| 132 | +\end{equation} | |
| 133 | +avec $d_\rho = \mathbf{e}\T\mathbf{W}\mathbf{y}/\hat\sigma^2$ et $d_\lambda = \mathbf{e}\T\mathbf{W}\mathbf{e}/\hat\sigma^2$. La règle de décision usuelle est séquentielle : si aucun test simple n'est significatif, le modèle MCO suffit ; si les deux le sont, on regarde les versions robustes et l'on retient la forme dont la version robuste reste significative ; si les deux versions robustes sont significatives, on envisage un modèle combinant les deux formes ou, plus souvent, on soupçonne une spécification déficiente (variables omises, forme fonctionnelle) dont la dépendance spatiale n'est que le symptôme. L'exemple du cas 1 est, à cet égard, un cas d'école. | |
| 134 | + | |
| 135 | +\begin{pratique} | |
| 136 | +Le diagnostic spatial d'un modèle hédonique tient en trois gestes, qui devraient figurer au dossier de tout modèle utilisé pour dériver des ajustements ou des indications de valeur : la carte des résidus sur les coordonnées des ventes, qui montre à l'œil les grappes de sur- et de sous-évaluation ; la statistique de Moran des résidus avec sa valeur $z$, pour une matrice de voisinage déclarée ; et les quatre tests LM, qui orientent vers un remède. Un modèle dont les résidus sont spatialement corrélés n'est pas inutilisable, mais ses erreurs-types sont trompeuses et sa prédiction pour un sujet donné peut être améliorée par l'information des ventes voisines, ce que la \cref{sec:19:krigeage} montre. | |
| 137 | +\end{pratique} | |
modified
sources/registry.csv
+10 −0
@@ -290,6 +290,16 @@ SRC-E-065,jeu_de_donnees,Profil financier des municipalités locales 2024-2025 ( | ||
| 290 | 290 | SRC-E-066,rapport,Rapport mensuel sur le marché immobilier au Québec — août 2026 (RMR Ottawa-Gatineau),JLR (Equifax),2026-09,https://solutions.jlr.ca/hubfs/Etudes_et_rapports/2026-08_Rapport_Mensuel.pdf?hsLang=fr,2026-10-31,verifie,ch. 13; 45,"Industrie (actes publiés au Registre foncier). Unifamiliale 522 741 $ (août 2026), 12 mois 495 000 $ ; 2-5 log. 545 750 $." |
| 291 | 291 | SRC-E-067,article,Banque du Canada — décision du 28 octobre 2026 (analyse),Desjardins Études économiques,2026-10-28,https://www.desjardins.com/fr/epargne-placements/etudes-economiques/canada-bdc-28-octobre-2026.html,2026-10-31,verifie,ch. 3,"Secondaire (institution financière) ; confirme +25 pb à 2,50 %, première hausse depuis juillet 2023." |
| 292 | 292 | SRC-O-001,loi,Loi sur l'intérêt (L.R.C. 1985 ch. I-15) art. 6,Canada (ministère de la Justice),texte consolidé consulté le 2026-10-31,https://laws-lois.justice.gc.ca/fra/lois/i-15/page-1.html,2026-10-31,verifie,29;30;31;annexe conventions,"art. 6 lu : taux « calculé annuellement ou semestriellement, mais non d'avance » ; fonde la composition semestrielle de traite.finance.Hypotheque" |
| 293 | +SRC-APPB-001,page_web,Lignes directrices sur le radon,Santé Canada (Canada.ca),Page modifiée le 2025-10-15,https://www.canada.ca/fr/sante-canada/services/securite-et-risque-pour-sante/radiation/radon/lignes-directrices-gouvernement-canada.html,2026-10-31,verifie,appB,"Ligne directrice 200 Bq/m3 (moyenne annuelle, aires normalement occupées) ; mesures correctives recommandées dans l'année ; pas de durée de test précisée sur la page" | |
| 294 | +SRC-APPB-002,communique,Santé Canada avise les Canadiens que l'isolant de vermiculite peut contenir de l'amiante et peut être nocif,Santé Canada (archives Canada.ca),2004-04-03,https://www.canada.ca/fr/nouvelles/archive/2004/04/sante-canada-avise-canadiens-isolant-vermiculite-peut-contenir-amiante-peut-etre-nocif.html,2026-10-31,partiellement_verifie,appB,"Extrait de recherche lu (titre, date, première phrase) ; page non ouverte intégralement" | |
| 295 | +SRC-APPB-003,guide,Emprisonnons la chaleur — Section 3 : Les matériaux (produits interdits et réglementés),Ressources naturelles Canada,Page modifiée le 2025-01-31,https://ressources-naturelles.canada.ca/efficacite-energetique/efficacite-energetique-maison/emprisonnons-chaleur-section-3,2026-10-31,verifie,appB,MIUF injectée sur place interdite au Canada depuis 1980 ; produits de consommation contenant de l'amiante interdits depuis 2018 ; vermiculite : renvoi à un avertissement de sécurité (partie 1.4 non lue) | |
| 296 | +SRC-APPB-004,page_web,La pyrite,Association des consommateurs pour la qualité dans la construction (ACQC),Sans date de mise à jour affichée,https://www.acqc.ca/fr/info/pyrite,2026-10-31,verifie,appB,"Mécanisme, signes (dalle du garage d'abord, fissures en étoile, poudre blanche), délais 10-25+ ans, CTQ-M100/M200, remblai DB, programme SHQ 2001-2011 (fin 26 oct. 2011) ; source d'association, non gouvernementale" | |
| 297 | +SRC-APPB-005,norme,BNQ 2560-500/2022 — Indice pétrographique du potentiel de gonflement sulfatique (IPPG) : méthode d'essai (sommaire),Bureau de normalisation du Québec,2022,https://bnq.qc.ca/images/pdf/Sommaire_SOD/SOD_2560-500_FR_2022.pdf,2026-10-31,partiellement_verifie,appB,"Sommaire public seulement (norme protégée, non reproduite) ; BNQ 2560-510/2022 (remblais) également listée" | |
| 298 | +SRC-APPB-006,page_web,Dépôt d'ocre,Régie du bâtiment du Québec,Sans date de mise à jour (date d'impression 2026-10-30),https://www.rbq.gouv.qc.ca/vous-etes/citoyen/problemes-avec-votre-residence/depot-docre,2026-10-31,verifie,appB,"Signes, origine chimique/biologique, conséquences, solutions avant/après construction ; ne mentionne pas la norme BNQ" | |
| 299 | +SRC-APPB-007,page_web,Ocre ferreuse,ACQC,Sans date,https://acqc.ca/fr/ocre-ferreuse,2026-10-31,partiellement_verifie,appB,Extrait de recherche : norme BNQ 3661-500 publiée en février 2012 ; page non ouverte intégralement | |
| 300 | +SRC-APPB-008,programme,Programme pour les résidences endommagées par la pyrrhotite (+ volet I ; normes du programme SHQ),Société d'habitation du Québec / Québec.ca,Page mise à jour le 2026-10-08 ; programme terminé le 2026-09-30,https://www.quebec.ca/habitation-territoire/construction-renovation/renovation-residentielle/programme-residences-endommagees-pyrrhotite,2026-10-31,verifie,appB,"Programme mis en œuvre en 2011 ; volet I : taux minimal de 0,23 % de pyrrhotite ; normes : https://cdn-contenu.quebec.ca/cdn-contenu/adm/org/shq/Documents/SHQ/acces/prise-de-decision/normes-programme-pyrrhotite.pdf" | |
| 301 | +SRC-APPB-009,page_web,Chauffage au mazout résidentiel,Gouvernement du Québec (Québec.ca),Dernière mise à jour : 8 juin 2026,https://www.quebec.ca/habitation-territoire/chauffage-consommation-energie/chauffage-mazout,2026-10-31,verifie,appB,"Règlement sur les appareils de chauffage au mazout : interdictions (neuf ; remplacement par combustible fossile dans l'existant ≤ 600 m2 et ≤ 3 étages), entretien < 20 ans (chauffe-eau < 10 ans), déclaration des travaux et du retrait du réservoir ; texte réglementaire non relu" | |
| 302 | +SRC-APPB-010,communique,Québec adopte un règlement afin d'éliminer le recours au mazout pour le chauffage résidentiel,Cabinet du ministre de l'Environnement (Québec.ca),2021,https://www.quebec.ca/nouvelles/actualites/details/quebec-adopte-un-reglement-afin-deliminer-le-recours-au-mazout-pour-le-chauffage-residentiel-36186,2026-10-31,verifie,appB,Interdictions à compter du 31 déc. 2021 (neuf) et du 31 déc. 2023 (remplacement dans l'existant) ; réparation interdite des appareils de plus de 20 ans et chauffe-eau de plus de 10 ans | |
| 293 | 303 | SRC-3-001,article,"The Geographic Determinants of Housing Supply (QJE 125(3), 1253-1296)",Saiz — Oxford University Press (QJE),2010 (DOI 10.1162/qjec.2010.125.3.1253),https://academic.oup.com/qje/article/125/3/1253/1903664,2026-10-31,partiellement_verifie,3,"Notice et résumé lus (accès au texte intégral fermé ; aucune copie ouverte trouvée : IZA dp3900/dp3842 ne sont pas cet article, Semantic Scholar openAccessPdf = CLOSED). Clé lib-D saiz2010 (DOI vérifié par le lot D). Seuls les énoncés qualitatifs sont cités au ch. 3 ; les élasticités par MSA ne sont pas reproduites (claims CLM-3-19)." |
| 294 | 304 | SRC-04-001,article,Jour du déménagement au Québec (J. Patterson),L'Encyclopédie canadienne (Historica Canada),2024-04-23,https://thecanadianencyclopedia.ca/fr/article/jour-du-demenagement-au-quebec,2026-10-31,partiellement_verifie,4,Lu le 2026-10-31 : 1er mai sous le régime seigneurial ; loi de 1973 ; baux prolongés au 30 juin 1974 ; Jérôme Choquette. Source secondaire : le texte de la loi de 1973 n'a pas été consulté. |
| 295 | 305 | SRC-04-002,donnees,"Base de données toponymiques du Canada — service geonames (coordonnées des villes de Montréal, Québec, Gatineau, Sherbrooke, Trois-Rivières, Saguenay)",Ressources naturelles Canada,2026-10-31,https://geogratis.gc.ca/services/geoname/fr/geonames.json,2026-10-31,verifie,4,"Requêtes concise=CITY, province=24 ; coordonnées reproduites dans analysis/python/figures/ch04_carte.py ; licence du gouvernement ouvert du Canada" |
modified
tools/merge_sources.py
+5 −5
@@ -67,15 +67,15 @@ def merge_csv(pattern, target, keycol): | ||
| 67 | 67 | with tgt.open("w", encoding="utf-8", newline="") as fh: |
| 68 | 68 | w = csv.DictWriter(fh, fieldnames=cols); w.writeheader(); w.writerows(rows) |
| 69 | 69 | print(f"{target} : {len(rows)} lignes (+{n})") |
| 70 | −merge_csv("ch*-figures.csv", "verification/figures.csv", "fichier") | |
| 71 | −merge_csv("ch*-calculations.csv", "verification/calculations.csv", "id") | |
| 72 | −merge_csv("ch*-claims.csv", "verification/claims.csv", "id") | |
| 70 | +merge_csv("*-figures.csv", "verification/figures.csv", "fichier") | |
| 71 | +merge_csv("*-calculations.csv", "verification/calculations.csv", "id") | |
| 72 | +merge_csv("*-claims.csv", "verification/claims.csv", "id") | |
| 73 | 73 | # registre par chapitre → registry.csv (append sans doublon d'id) |
| 74 | 74 | reg = ROOT / "sources/registry.csv" |
| 75 | 75 | with reg.open(encoding="utf-8") as fh: |
| 76 | 76 | r = csv.DictReader(fh); rrows = list(r) |
| 77 | 77 | ids = {x["id"] for x in rrows}; n = 0 |
| 78 | −for f in sorted((ROOT / "verification/chapters").glob("ch*-registry.csv")): | |
| 78 | +for f in sorted((ROOT / "verification/chapters").glob("*-registry.csv")): | |
| 79 | 79 | with f.open(encoding="utf-8") as fh: |
| 80 | 80 | for row in csv.DictReader(fh): |
| 81 | 81 | row = {c: (row.get(c) or "").strip() for c in hdr} |
@@ -85,7 +85,7 @@ with reg.open("w", encoding="utf-8", newline="") as fh: | ||
| 85 | 85 | print(f"registry.csv (+chapitres) : {len(rrows)} (+{n})") |
| 86 | 86 | # bib par chapitre |
| 87 | 87 | txt = lib.read_text(encoding="utf-8"); have = set(keys(txt)); added = [] |
| 88 | −for f in sorted((ROOT / "references").glob("lib-ch*.bib")): | |
| 88 | +for f in sorted((ROOT / "references").glob("lib-ch*.bib")) + sorted((ROOT / "references").glob("lib-app*.bib")): | |
| 89 | 89 | for p in re.split(r"(?m)^(?=@\w+\s*\{)", f.read_text(encoding="utf-8")): |
| 90 | 90 | m = re.match(r"@\w+\s*\{\s*([^,\s]+)\s*,", p) |
| 91 | 91 | if m and m.group(1) not in have: have.add(m.group(1)); added.append(p.strip() + "\n") |
modified
verification/chapters/appB-claims.csv
+25 −25
@@ -1,25 +1,25 @@ | ||
| 1 | −id,chapitre,affirmation,source_id,verification,date,statut,notes | |
| 2 | −CLM-APPB-01,appB,Inspection obligatoire de tout immeuble évalué sauf circonstances exceptionnelles expliquées au rapport (règle 2.4) ; minimum : décrire l'immeuble ; fonder l'usage le meilleur ; soutenir les comparaisons,SRC-C-001,NPP règle 2.4 (notes-C §2.1),2026-10-31,verifie,paraphrase | |
| 3 | −CLM-APPB-02,appB,Code de déontologie art. 41 : rapport conforme aux normes faisant état de la méthodologie et de l'étendue des recherches ; art. 32 : interdiction d'exclure sa responsabilité civile,SRC-C-003,LégisQuébec C-26 r. 123 à jour au 2026-07-01 (notes-C §2.2),2026-10-31,verifie,id source approximatif (Code de déontologie) : voir registry fusionné | |
| 4 | −CLM-APPB-03,appB,Règle 2.1 c) et règle 2.3 élément 7 : divulguer toute hypothèse ou réserve extraordinaire avec son incidence sur la valeur ; rappel à chaque énoncé de la conclusion,SRC-C-001,NPP (notes-C §2.1),2026-10-31,verifie, | |
| 5 | −CLM-APPB-04,appB,Art. 1726 al. 2 C.c.Q. : est apparent le vice qu'un acheteur prudent et diligent peut constater sans recourir à un expert,SRC-A-001,texte lu (notes-A §2.1),2026-10-31,verifie, | |
| 6 | −CLM-APPB-05,appB,Formulaire DV OACIQ obligatoire depuis juillet 2012 pour la vente par une personne physique d'un immeuble résidentiel de moins de 5 logements ; recueille sinistres et infiltrations et loyers et dossiers TAL,SRC-A-026;SRC-A-028,notes-A §2.4,2026-10-31,verifie,la liste des rubriques précises (pyrite ocre amiante mazout) n'a pas été relue dans le formulaire V24 : formulée au conditionnel ("renseigne sur") | |
| 7 | −CLM-APPB-06,appB,Cartographie des zones inondables et de mobilité en vigueur : classes d'intensité (cadre depuis le 1er mars 2026),SRC-B-050;SRC-B-045,notes-B §43.2,2026-10-31,verifie, | |
| 8 | −CLM-APPB-07,appB,Art. 1931 C.c.Q. : préavis du locateur avant d'entrer dans le logement (accès aux logements pour l'inspection),SRC-A-001,article non relu dans les notes-A (liste des articles lus : 1892-1914 et 1936-1979),2026-10-31,partiellement_verifie,à confirmer par l'orchestrateur (préavis de 24 heures art. 1931) | |
| 9 | −CLM-APPB-08,appB,Règlement sur la tenue des dossiers (C-26 r. 133) art. 6 : conservation au moins 5 ans après le dernier service,SRC-C-004,notes-C §2.3,2026-10-31,verifie, | |
| 10 | −CLM-APPB-09,appB,Harding Rosenthal Sirmans 2007 : dépréciation nette d'entretien ≈ 2,5 %/an ; l'entretien compense ≈ 1 point/an,SRC-D-ch11,notes-D ch. 11 (clé hardingRosenthalSirmans2007),2026-10-31,verifie,source scientifique du domaine D | |
| 11 | −CLM-APPB-10,appB,Baum 1991 : typologie des désuétudes (physique fonctionnelle esthétique externe),SRC-D-ch11,notes-D ch. 11 (clé baum1991),2026-10-31,verifie, | |
| 12 | −CLM-APPB-11,appB,Loi sur le bâtiment art. 29 : exclusion du chapitre Bâtiment pour la maison unifamiliale et le bâtiment totalement résidentiel de moins de trois étages ou de moins de neuf logements,SRC-B-052,notes-B §44.1,2026-10-31,verifie, | |
| 13 | −CLM-APPB-12,appB,Pyrite : gonflement du remblai sous dalle ; apparition ≈ 10 ans (parfois 15 à 25+) ; dalle du garage d'abord ; norme CTQ-M200 ; remblai certifié DB ; programme SHQ 2001-2011 (fin le 26 oct. 2011) ; régions : Montérégie et vallée du Saint-Laurent,SRC-APPB-004,page ACQC lue le 2026-10-31 (sans date de mise à jour),2026-10-31,verifie,source d'association de consommateurs ; la norme BNQ 2560-500/2022 (IPPG) vérifiée par son sommaire (SRC-APPB-005) | |
| 14 | −CLM-APPB-13,appB,Ocre ferreuse : signes (eau rougeâtre ; boue ocre ; dépôts rougeâtres avec odeur de soufre ; humidité) ; origine chimique ou bactérienne ; impossibilité d'empêcher la formation ; solutions d'aménagement,SRC-APPB-006,page RBQ lue le 2026-10-31,2026-10-31,verifie, | |
| 15 | −CLM-APPB-14,appB,Norme BNQ 3661-500 (drains en sols propices aux dépôts d'ocre) publiée en 2012,SRC-APPB-007,page ACQC « Ocre ferreuse » (extrait de recherche),2026-10-31,partiellement_verifie,sommaire BNQ non ouvert | |
| 16 | −CLM-APPB-15,appB,Pyrrhotite : programme SHQ de 2011 à 2026 ; fin le 30 septembre 2026 ; volet travaux : taux minimal de 0,23 % de pyrrhotite ; régions Mauricie et voisines,SRC-APPB-008,pages Québec.ca (màj 2026-10-08) et normes du programme (PDF SHQ),2026-10-31,verifie,« années 1990 et 2000 » : repère issu des pages consultées (programme mis en œuvre en 2011) non quantifié | |
| 17 | −CLM-APPB-16,appB,Vermiculite : avis Santé Canada du 3 avril 2004 (isolant pouvant contenir de l'amiante ; marque Zonolite) ; produits de consommation contenant de l'amiante interdits depuis 2018,SRC-APPB-002;SRC-APPB-003,extrait d'archive Canada.ca + RNCan Emprisonnons la chaleur §3,2026-10-31,verifie,la mention « vendu jusqu'au début des années 1990 » du tableau n'a pas été vérifiée sur la page (snippet seulement) : marquée non_verifie dans le texte ? -> formulée « posé jusqu'au début des années 1990 » : NON VÉRIFIÉE | |
| 18 | −CLM-APPB-17,appB,MIUF (UFFI) interdite au Canada depuis 1980,SRC-APPB-003,RNCan Emprisonnons la chaleur §3 (page modifiée 2025-01-31),2026-10-31,verifie, | |
| 19 | −CLM-APPB-18,appB,Radon : ligne directrice canadienne 200 Bq/m3 (moyenne annuelle aires occupées) ; agir dans l'année si dépassement,SRC-APPB-001,page Santé Canada modifiée 2025-10-15,2026-10-31,verifie, | |
| 20 | −CLM-APPB-19,appB,Câblage en aluminium : résidences câblées fin des années 1960 et début des années 1970 ; risque aux connexions ; marquage AL ; dispositifs CO/ALR,,aucune source officielle trouvée (RBQ : seulement bâtiments de ferme 2017),2026-10-31,non_verifie,déclaré comme pratique courante non vérifiée dans le texte et dans limites | |
| 21 | −CLM-APPB-20,appB,Plomberie galvanisée courante avant les années 1960 ; signes (pression faible ; eau colorée ; corrosion aux raccords cuivre-galvanisé),,pratique du bâtiment,2026-10-31,non_verifie,déclaré comme tel dans le texte | |
| 22 | −CLM-APPB-21,appB,Règlement sur les appareils de chauffage au mazout : interdiction d'installer au mazout dans le neuf ; interdiction de remplacer par combustible fossile dans l'existant ≤ 600 m2 et ≤ 3 étages ; entretien permis si < 20 ans (chauffe-eau < 10 ans) ; déclaration du retrait du réservoir ; dates 31 déc. 2021 et 31 déc. 2023,SRC-APPB-009;SRC-APPB-010,page Québec.ca (màj 2026-06-08) et communiqué d'édiction,2026-10-31,partiellement_verifie,texte réglementaire non relu (seuils lus sur la page gouvernementale) — signalé dans le texte | |
| 23 | −CLM-APPB-22,appB,Plan de garantie (B-1.1 r. 8) : couverture 1 an malfaçons non apparentes ; 3 ans vices cachés ; 5 ans vices de conception/construction/sol ; plafonds 300 000 $ unifamiliale et 200 000 $ par logement multifamilial ; garantie transférable,SRC-B-057;SRC-B-058,notes-B §44.1,2026-10-31,verifie, | |
| 24 | −CLM-APPB-23,appB,Code de construction chapitre I = CNB 2020 modifié Québec en vigueur le 17 avril 2025 ; transition pour travaux débutés avant le 17 octobre 2027,SRC-B-053,notes-B §44.1,2026-10-31,verifie, | |
| 25 | −CLM-APPB-24,appB,Exemple rempli du cas 1 : adresse fictive ; 1998 ; 165 m2 ; 620 m2 ; qualité 3 ; garage 1 ; sous-sol fini ; 4 ch ; 2 sdb,SYNTH-cas1,data/synthetic/cas1_gatineau_sujet.json,2026-10-31,verifie,toutes les observations de l'exemple sont fictives et identifiées (encadré synthetique) ; aucun montant | |
| 1 | +id,chapitre,affirmation,source_id,verification,date,statut,notes | |
| 2 | +CLM-APPB-01,appB,Inspection obligatoire de tout immeuble évalué sauf circonstances exceptionnelles expliquées au rapport (règle 2.4) ; minimum : décrire l'immeuble ; fonder l'usage le meilleur ; soutenir les comparaisons,SRC-C-001,NPP règle 2.4 (notes-C §2.1),2026-10-31,verifie,paraphrase | |
| 3 | +CLM-APPB-02,appB,Code de déontologie art. 41 : rapport conforme aux normes faisant état de la méthodologie et de l'étendue des recherches ; art. 32 : interdiction d'exclure sa responsabilité civile,SRC-C-003,LégisQuébec C-26 r. 123 à jour au 2026-07-01 (notes-C §2.2),2026-10-31,verifie,id source approximatif (Code de déontologie) : voir registry fusionné | |
| 4 | +CLM-APPB-03,appB,Règle 2.1 c) et règle 2.3 élément 7 : divulguer toute hypothèse ou réserve extraordinaire avec son incidence sur la valeur ; rappel à chaque énoncé de la conclusion,SRC-C-001,NPP (notes-C §2.1),2026-10-31,verifie, | |
| 5 | +CLM-APPB-04,appB,Art. 1726 al. 2 C.c.Q. : est apparent le vice qu'un acheteur prudent et diligent peut constater sans recourir à un expert,SRC-A-001,texte lu (notes-A §2.1),2026-10-31,verifie, | |
| 6 | +CLM-APPB-05,appB,Formulaire DV OACIQ obligatoire depuis juillet 2012 pour la vente par une personne physique d'un immeuble résidentiel de moins de 5 logements ; recueille sinistres et infiltrations et loyers et dossiers TAL,SRC-A-026;SRC-A-028,notes-A §2.4,2026-10-31,verifie,"la liste des rubriques précises (pyrite ocre amiante mazout) n'a pas été relue dans le formulaire V24 : formulée au conditionnel (""renseigne sur"")" | |
| 7 | +CLM-APPB-06,appB,Cartographie des zones inondables et de mobilité en vigueur : classes d'intensité (cadre depuis le 1er mars 2026),SRC-B-050;SRC-B-045,notes-B §43.2,2026-10-31,verifie, | |
| 8 | +CLM-APPB-07,appB,Art. 1931 C.c.Q. : préavis du locateur avant d'entrer dans le logement (accès aux logements pour l'inspection),SRC-A-001,article non relu dans les notes-A (liste des articles lus : 1892-1914 et 1936-1979),2026-10-31,partiellement_verifie,à confirmer par l'orchestrateur (préavis de 24 heures art. 1931) | |
| 9 | +CLM-APPB-08,appB,Règlement sur la tenue des dossiers (C-26 r. 133) art. 6 : conservation au moins 5 ans après le dernier service,SRC-C-004,notes-C §2.3,2026-10-31,verifie, | |
| 10 | +CLM-APPB-09,appB,"Harding Rosenthal Sirmans 2007 : dépréciation nette d'entretien ≈ 2,5 %/an ; l'entretien compense ≈ 1 point/an",SRC-D-ch11,notes-D ch. 11 (clé hardingRosenthalSirmans2007),2026-10-31,verifie,source scientifique du domaine D | |
| 11 | +CLM-APPB-10,appB,Baum 1991 : typologie des désuétudes (physique fonctionnelle esthétique externe),SRC-D-ch11,notes-D ch. 11 (clé baum1991),2026-10-31,verifie, | |
| 12 | +CLM-APPB-11,appB,Loi sur le bâtiment art. 29 : exclusion du chapitre Bâtiment pour la maison unifamiliale et le bâtiment totalement résidentiel de moins de trois étages ou de moins de neuf logements,SRC-B-052,notes-B §44.1,2026-10-31,verifie, | |
| 13 | +CLM-APPB-12,appB,Pyrite : gonflement du remblai sous dalle ; apparition ≈ 10 ans (parfois 15 à 25+) ; dalle du garage d'abord ; norme CTQ-M200 ; remblai certifié DB ; programme SHQ 2001-2011 (fin le 26 oct. 2011) ; régions : Montérégie et vallée du Saint-Laurent,SRC-APPB-004,page ACQC lue le 2026-10-31 (sans date de mise à jour),2026-10-31,verifie,source d'association de consommateurs ; la norme BNQ 2560-500/2022 (IPPG) vérifiée par son sommaire (SRC-APPB-005) | |
| 14 | +CLM-APPB-13,appB,Ocre ferreuse : signes (eau rougeâtre ; boue ocre ; dépôts rougeâtres avec odeur de soufre ; humidité) ; origine chimique ou bactérienne ; impossibilité d'empêcher la formation ; solutions d'aménagement,SRC-APPB-006,page RBQ lue le 2026-10-31,2026-10-31,verifie, | |
| 15 | +CLM-APPB-14,appB,Norme BNQ 3661-500 (drains en sols propices aux dépôts d'ocre) publiée en 2012,SRC-APPB-007,page ACQC « Ocre ferreuse » (extrait de recherche),2026-10-31,partiellement_verifie,sommaire BNQ non ouvert | |
| 16 | +CLM-APPB-15,appB,"Pyrrhotite : programme SHQ de 2011 à 2026 ; fin le 30 septembre 2026 ; volet travaux : taux minimal de 0,23 % de pyrrhotite ; régions Mauricie et voisines",SRC-APPB-008,pages Québec.ca (màj 2026-10-08) et normes du programme (PDF SHQ),2026-10-31,verifie,« années 1990 et 2000 » : repère issu des pages consultées (programme mis en œuvre en 2011) non quantifié | |
| 17 | +CLM-APPB-16,appB,Vermiculite : avis Santé Canada du 3 avril 2004 (isolant pouvant contenir de l'amiante ; marque Zonolite) ; produits de consommation contenant de l'amiante interdits depuis 2018,SRC-APPB-002;SRC-APPB-003,extrait d'archive Canada.ca + RNCan Emprisonnons la chaleur §3,2026-10-31,verifie,la mention « posé jusqu'au début des années 1990 » du tableau des pathologies n'a pas été vérifiée sur une source primaire (voir appB.md point 4) | |
| 18 | +CLM-APPB-17,appB,MIUF (UFFI) interdite au Canada depuis 1980,SRC-APPB-003,RNCan Emprisonnons la chaleur §3 (page modifiée 2025-01-31),2026-10-31,verifie, | |
| 19 | +CLM-APPB-18,appB,Radon : ligne directrice canadienne 200 Bq/m3 (moyenne annuelle aires occupées) ; agir dans l'année si dépassement,SRC-APPB-001,page Santé Canada modifiée 2025-10-15,2026-10-31,verifie, | |
| 20 | +CLM-APPB-19,appB,Câblage en aluminium : résidences câblées fin des années 1960 et début des années 1970 ; risque aux connexions ; marquage AL ; dispositifs CO/ALR,,aucune source officielle trouvée (RBQ : seulement bâtiments de ferme 2017),2026-10-31,non_verifie,déclaré comme pratique courante non vérifiée dans le texte et dans limites | |
| 21 | +CLM-APPB-20,appB,Plomberie galvanisée courante avant les années 1960 ; signes (pression faible ; eau colorée ; corrosion aux raccords cuivre-galvanisé),,pratique du bâtiment,2026-10-31,non_verifie,déclaré comme tel dans le texte | |
| 22 | +CLM-APPB-21,appB,Règlement sur les appareils de chauffage au mazout : interdiction d'installer au mazout dans le neuf ; interdiction de remplacer par combustible fossile dans l'existant ≤ 600 m2 et ≤ 3 étages ; entretien permis si < 20 ans (chauffe-eau < 10 ans) ; déclaration du retrait du réservoir ; dates 31 déc. 2021 et 31 déc. 2023,SRC-APPB-009;SRC-APPB-010,page Québec.ca (màj 2026-06-08) et communiqué d'édiction,2026-10-31,partiellement_verifie,texte réglementaire non relu (seuils lus sur la page gouvernementale) — signalé dans le texte | |
| 23 | +CLM-APPB-22,appB,Plan de garantie (B-1.1 r. 8) : couverture 1 an malfaçons non apparentes ; 3 ans vices cachés ; 5 ans vices de conception/construction/sol ; plafonds 300 000 $ unifamiliale et 200 000 $ par logement multifamilial ; garantie transférable,SRC-B-057;SRC-B-058,notes-B §44.1,2026-10-31,verifie, | |
| 24 | +CLM-APPB-23,appB,Code de construction chapitre I = CNB 2020 modifié Québec en vigueur le 17 avril 2025 ; transition pour travaux débutés avant le 17 octobre 2027,SRC-B-053,notes-B §44.1,2026-10-31,verifie, | |
| 25 | +CLM-APPB-24,appB,Exemple rempli du cas 1 : adresse fictive ; 1998 ; 165 m2 ; 620 m2 ; qualité 3 ; garage 1 ; sous-sol fini ; 4 ch ; 2 sdb,SYNTH-cas1,data/synthetic/cas1_gatineau_sujet.json,2026-10-31,verifie,toutes les observations de l'exemple sont fictives et identifiées (encadré synthetique) ; aucun montant | |
added
verification/chapters/appC.md
+24 −0
@@ -0,0 +1,24 @@ | ||
| 1 | +# Annexe C — Modèles de rapport d'évaluation — bilan de rédaction (2026-10-31) | |
| 2 | + | |
| 3 | +## Livrables | |
| 4 | +- `manuscript/appendices/modeles-rapport.tex` (remplace le stub ; première ligne `\chapter{Modèles de rapport d'évaluation}\label{app:modeles-rapport}` conservée) : ≈ 5 500 mots de prose hors tableaux (≈ 8 500 mots au compteur brut), 6 sections : (1) cadre (trois formes, douze éléments, déontologie, comparaison CUSPAP/IVS/RICS) ; (2) modèle 1 — plan commenté du rapport narratif complet (longtable section → élément 2.3 → contenu attendu → pièges) + commentaires de rédaction ; (3) modèle 2 — rapport multilogement (longtable des sections supplémentaires : état locatif, cadre juridique des loyers, revenus, dépenses normalisées, RNE, taux, capitalisation, DCF, comparaison par unité, réconciliation, financement et stress test en consultation) + 4 identités numérotées (C.1)-(C.4) ; (4) modèle 3 — rapport abrégé / formulaire court (longtable de 21 rubriques numérotées par élément) ; (5) textes-types : 14 clauses d'hypothèses de base et conditions limitatives (formulées par l'auteur), gabarit d'hypothèse extraordinaire + 3 exemples rédigés (permis ; désordre non expertisé ; logements non visités), 2 textes-types d'avertissement (modèle statistique ; données construites ou simulées), structure paraphrasée de l'attestation ; (6) liste de contrôle de révision (longtable de 36 vérifications en 6 blocs : conformité, chiffres, renvois, sources et dates, langage, forme). Encadrés `attention`, `pratique` ×5, `quebec`, `erreurs`, `limites`, `synthese`. **4 longtables, 4 équations numérotées**, aucun tableau flottant, aucune figure. | |
| 5 | +- Textes normatifs **paraphrasés, non reproduits** : annexe E des NPP, formule d'attestation (seule l'incipit déjà cité au ch. 10/47 est mentionné), annexe B (trois formes). | |
| 6 | +- `verification/chapters/appC-claims.csv` (15 lignes) ; `appC-registry.csv` (en-tête seulement : aucune nouvelle source, toutes les clés citées existent dans `library.bib` : oeaqNormes, deontOEAQ, cuspap2026, ivs2025, rics2025, ccq, lfm, codeProfessions, tenuedossiersoeaq, appraisal2026). | |
| 7 | + | |
| 8 | +## Contrôles | |
| 9 | +- `tools/compile_chapter.sh modeles-rapport` → `OK app-modeles-rapport : 21 pages, 0 references/citations non definies, 0 overfull hbox`. | |
| 10 | +- Inspection visuelle `pdftoppm -r 50` : 21 pages ; pages 4, 9, 13, 17 vérifiées (plan commenté, formulaire court, exemples d'hypothèses extraordinaires, liste de contrôle + encadré québécois) : aucun débordement. | |
| 11 | +- 6 messages « Infinite glue shrinkage … ignored » dans les longtables (sans effet visible ; voir appB.md). | |
| 12 | + | |
| 13 | +## Points non vérifiés / à harmoniser | |
| 14 | +1. **LFM art. 46 — date de référence du rôle** : formulée sans chiffre (« antérieure à l'entrée en vigueur du rôle ») parce que le ch. 47 écrit « deux ans » alors que la règle usuelle est 18 mois ; à trancher par l'orchestrateur sur le texte de l'art. 46 (CLM-APPC-09). | |
| 15 | +2. **RICS VPS 6** : page RICS lue, PDF non téléchargé (notes-C) — `partiellement_verifie` (CLM-APPC-08). | |
| 16 | +3. La correspondance sections ↔ éléments de la règle 2.3 (colonne « Élém. » des tableaux C.1 et C.3) est une interprétation de l'auteur, déclarée dans les `\tabsource`. | |
| 17 | +4. Les 14 clauses d'hypothèses de base n'ont pas fait l'objet d'une revue juridique (prévue avant publication ; signalé dans `limites`). | |
| 18 | +5. `source_id` « SRC-C-003 », « SRC-C-0xx (cuspap2026 / rics2025) » à aligner sur le registre fusionné. | |
| 19 | +6. Les champs des textes-types sont rendus par `\champ{…}` = ⟨*texte*⟩ (`\langle`/`\rangle` en math, car `\textlangle` n'est pas fiable sous fontspec/TU) ; ce ne sont pas des placeholders de rédaction mais des champs de gabarit. | |
| 20 | + | |
| 21 | +## Suggestions | |
| 22 | +- Harmoniser avec le ch. 10 : les tableaux 10.3 (formes) et 10.4 (éléments) et le tableau C.1 doivent rester identiques dans la numérotation des éléments ; le ch. 10 §10.6 (« Rédiger le rapport ») pourrait renvoyer à `app:modeles-rapport` pour les textes-types. | |
| 23 | +- Le ch. 47 §47.14 pourrait renvoyer au modèle 2 (`sec:appC:multilog`) et à la liste de contrôle (`tab:appC:controle`) ; les labels de section d'annexe ne sont toutefois pas générés par `make_labels.py` (seuls `app:*` le sont) : utiliser `annexe~\ref{app:modeles-rapport}` depuis les chapitres. | |
| 24 | +- Lors de la modernisation annoncée des NPP, revérifier la numérotation des règles 2.1-2.5 et la liste des douze éléments avant publication. | |
modified
verification/claims.csv
+39 −0
@@ -408,3 +408,42 @@ CLM-18-13,18,"Cropper, Deck & McConnell (1988) : formes simples (linéaire, semi | ||
| 408 | 408 | CLM-18-14,18,"Court (1939) a forgé le terme « hédonique » (indices automobiles GM) ; Griliches (1961, 1971) a relancé la méthode",SRC-D (lib-D),notes-D.md §ch. 18 ; goodman1998,2026-10-31,partiellement_verifie,Pagination de Court (1939) issue de la littérature secondaire (notes-D) |
| 409 | 409 | CLM-18-15,18,Version studentisée de Koenker du test de Breusch–Pagan (LM = n·R² de e² sur X),—,Formule standard (Koenker 1981,Journal of Econometrics) non ajoutée à la bibliographie,2026-10-31,non_verifie |
| 410 | 410 | CLM-18-16,18,Seuil 4/n pour la distance de Cook : convention d'usage,—,Convention répandue ; aucune norme,2026-10-31,non_verifie,Déclaré comme convention dans le texte et dans limites |
| 411 | +CLM-APPB-01,appB,Inspection obligatoire de tout immeuble évalué sauf circonstances exceptionnelles expliquées au rapport (règle 2.4) ; minimum : décrire l'immeuble ; fonder l'usage le meilleur ; soutenir les comparaisons,SRC-C-001,NPP règle 2.4 (notes-C §2.1),2026-10-31,verifie,paraphrase | |
| 412 | +CLM-APPB-02,appB,Code de déontologie art. 41 : rapport conforme aux normes faisant état de la méthodologie et de l'étendue des recherches ; art. 32 : interdiction d'exclure sa responsabilité civile,SRC-C-003,LégisQuébec C-26 r. 123 à jour au 2026-07-01 (notes-C §2.2),2026-10-31,verifie,id source approximatif (Code de déontologie) : voir registry fusionné | |
| 413 | +CLM-APPB-03,appB,Règle 2.1 c) et règle 2.3 élément 7 : divulguer toute hypothèse ou réserve extraordinaire avec son incidence sur la valeur ; rappel à chaque énoncé de la conclusion,SRC-C-001,NPP (notes-C §2.1),2026-10-31,verifie, | |
| 414 | +CLM-APPB-04,appB,Art. 1726 al. 2 C.c.Q. : est apparent le vice qu'un acheteur prudent et diligent peut constater sans recourir à un expert,SRC-A-001,texte lu (notes-A §2.1),2026-10-31,verifie, | |
| 415 | +CLM-APPB-05,appB,Formulaire DV OACIQ obligatoire depuis juillet 2012 pour la vente par une personne physique d'un immeuble résidentiel de moins de 5 logements ; recueille sinistres et infiltrations et loyers et dossiers TAL,SRC-A-026;SRC-A-028,notes-A §2.4,2026-10-31,verifie,"la liste des rubriques précises (pyrite ocre amiante mazout) n'a pas été relue dans le formulaire V24 : formulée au conditionnel (""renseigne sur"")" | |
| 416 | +CLM-APPB-06,appB,Cartographie des zones inondables et de mobilité en vigueur : classes d'intensité (cadre depuis le 1er mars 2026),SRC-B-050;SRC-B-045,notes-B §43.2,2026-10-31,verifie, | |
| 417 | +CLM-APPB-07,appB,Art. 1931 C.c.Q. : préavis du locateur avant d'entrer dans le logement (accès aux logements pour l'inspection),SRC-A-001,article non relu dans les notes-A (liste des articles lus : 1892-1914 et 1936-1979),2026-10-31,partiellement_verifie,à confirmer par l'orchestrateur (préavis de 24 heures art. 1931) | |
| 418 | +CLM-APPB-08,appB,Règlement sur la tenue des dossiers (C-26 r. 133) art. 6 : conservation au moins 5 ans après le dernier service,SRC-C-004,notes-C §2.3,2026-10-31,verifie, | |
| 419 | +CLM-APPB-09,appB,"Harding Rosenthal Sirmans 2007 : dépréciation nette d'entretien ≈ 2,5 %/an ; l'entretien compense ≈ 1 point/an",SRC-D-ch11,notes-D ch. 11 (clé hardingRosenthalSirmans2007),2026-10-31,verifie,source scientifique du domaine D | |
| 420 | +CLM-APPB-10,appB,Baum 1991 : typologie des désuétudes (physique fonctionnelle esthétique externe),SRC-D-ch11,notes-D ch. 11 (clé baum1991),2026-10-31,verifie, | |
| 421 | +CLM-APPB-11,appB,Loi sur le bâtiment art. 29 : exclusion du chapitre Bâtiment pour la maison unifamiliale et le bâtiment totalement résidentiel de moins de trois étages ou de moins de neuf logements,SRC-B-052,notes-B §44.1,2026-10-31,verifie, | |
| 422 | +CLM-APPB-12,appB,Pyrite : gonflement du remblai sous dalle ; apparition ≈ 10 ans (parfois 15 à 25+) ; dalle du garage d'abord ; norme CTQ-M200 ; remblai certifié DB ; programme SHQ 2001-2011 (fin le 26 oct. 2011) ; régions : Montérégie et vallée du Saint-Laurent,SRC-APPB-004,page ACQC lue le 2026-10-31 (sans date de mise à jour),2026-10-31,verifie,source d'association de consommateurs ; la norme BNQ 2560-500/2022 (IPPG) vérifiée par son sommaire (SRC-APPB-005) | |
| 423 | +CLM-APPB-13,appB,Ocre ferreuse : signes (eau rougeâtre ; boue ocre ; dépôts rougeâtres avec odeur de soufre ; humidité) ; origine chimique ou bactérienne ; impossibilité d'empêcher la formation ; solutions d'aménagement,SRC-APPB-006,page RBQ lue le 2026-10-31,2026-10-31,verifie, | |
| 424 | +CLM-APPB-14,appB,Norme BNQ 3661-500 (drains en sols propices aux dépôts d'ocre) publiée en 2012,SRC-APPB-007,page ACQC « Ocre ferreuse » (extrait de recherche),2026-10-31,partiellement_verifie,sommaire BNQ non ouvert | |
| 425 | +CLM-APPB-15,appB,"Pyrrhotite : programme SHQ de 2011 à 2026 ; fin le 30 septembre 2026 ; volet travaux : taux minimal de 0,23 % de pyrrhotite ; régions Mauricie et voisines",SRC-APPB-008,pages Québec.ca (màj 2026-10-08) et normes du programme (PDF SHQ),2026-10-31,verifie,« années 1990 et 2000 » : repère issu des pages consultées (programme mis en œuvre en 2011) non quantifié | |
| 426 | +CLM-APPB-16,appB,Vermiculite : avis Santé Canada du 3 avril 2004 (isolant pouvant contenir de l'amiante ; marque Zonolite) ; produits de consommation contenant de l'amiante interdits depuis 2018,SRC-APPB-002;SRC-APPB-003,extrait d'archive Canada.ca + RNCan Emprisonnons la chaleur §3,2026-10-31,verifie,la mention « posé jusqu'au début des années 1990 » du tableau des pathologies n'a pas été vérifiée sur une source primaire (voir appB.md point 4) | |
| 427 | +CLM-APPB-17,appB,MIUF (UFFI) interdite au Canada depuis 1980,SRC-APPB-003,RNCan Emprisonnons la chaleur §3 (page modifiée 2025-01-31),2026-10-31,verifie, | |
| 428 | +CLM-APPB-18,appB,Radon : ligne directrice canadienne 200 Bq/m3 (moyenne annuelle aires occupées) ; agir dans l'année si dépassement,SRC-APPB-001,page Santé Canada modifiée 2025-10-15,2026-10-31,verifie, | |
| 429 | +CLM-APPB-19,appB,Câblage en aluminium : résidences câblées fin des années 1960 et début des années 1970 ; risque aux connexions ; marquage AL ; dispositifs CO/ALR,,aucune source officielle trouvée (RBQ : seulement bâtiments de ferme 2017),2026-10-31,non_verifie,déclaré comme pratique courante non vérifiée dans le texte et dans limites | |
| 430 | +CLM-APPB-20,appB,Plomberie galvanisée courante avant les années 1960 ; signes (pression faible ; eau colorée ; corrosion aux raccords cuivre-galvanisé),,pratique du bâtiment,2026-10-31,non_verifie,déclaré comme tel dans le texte | |
| 431 | +CLM-APPB-21,appB,Règlement sur les appareils de chauffage au mazout : interdiction d'installer au mazout dans le neuf ; interdiction de remplacer par combustible fossile dans l'existant ≤ 600 m2 et ≤ 3 étages ; entretien permis si < 20 ans (chauffe-eau < 10 ans) ; déclaration du retrait du réservoir ; dates 31 déc. 2021 et 31 déc. 2023,SRC-APPB-009;SRC-APPB-010,page Québec.ca (màj 2026-06-08) et communiqué d'édiction,2026-10-31,partiellement_verifie,texte réglementaire non relu (seuils lus sur la page gouvernementale) — signalé dans le texte | |
| 432 | +CLM-APPB-22,appB,Plan de garantie (B-1.1 r. 8) : couverture 1 an malfaçons non apparentes ; 3 ans vices cachés ; 5 ans vices de conception/construction/sol ; plafonds 300 000 $ unifamiliale et 200 000 $ par logement multifamilial ; garantie transférable,SRC-B-057;SRC-B-058,notes-B §44.1,2026-10-31,verifie, | |
| 433 | +CLM-APPB-23,appB,Code de construction chapitre I = CNB 2020 modifié Québec en vigueur le 17 avril 2025 ; transition pour travaux débutés avant le 17 octobre 2027,SRC-B-053,notes-B §44.1,2026-10-31,verifie, | |
| 434 | +CLM-APPB-24,appB,Exemple rempli du cas 1 : adresse fictive ; 1998 ; 165 m2 ; 620 m2 ; qualité 3 ; garage 1 ; sous-sol fini ; 4 ch ; 2 sdb,SYNTH-cas1,data/synthetic/cas1_gatineau_sujet.json,2026-10-31,verifie,toutes les observations de l'exemple sont fictives et identifiées (encadré synthetique) ; aucun montant | |
| 435 | +CLM-APPC-01,appC,Trois formes de rapport écrit (narratif complet ; abrégé ; mise à jour) avec le même contenu minimal ; verbes décrire / résumer / mentionner (règles 2.2-2.3 et annexe B des NPP),SRC-C-001,NPP (notes-C §2.1) ; cohérent avec ch. 10 tab. 10.3,2026-10-31,verifie,paraphrase ; annexe B non reproduite | |
| 436 | +CLM-APPC-02,appC,Douze éléments du contenu minimal du rapport écrit (règle 2.3) : identification ; droits ; but et fin ; définition ; date ; étendue ; réserves et hypothèses ; traitement et raisonnement ; usage le meilleur ; rejet d'une méthode ; conformité ; attestation,SRC-C-001,NPP (notes-C §2.1),2026-10-31,verifie,liste paraphrasée ; correspondance sections-éléments = interprétation de l'auteur (déclarée) | |
| 437 | +CLM-APPC-03,appC,Code de déontologie : art. 34 signer et indiquer sa qualité ; art. 41 méthodologie et étendue des recherches ; art. 32 pas d'exclusion de responsabilité ; art. 24.1 honoraires conditionnels interdits ; art. 33 supervision du travail délégué,SRC-C-003,LégisQuébec C-26 r. 123 à jour au 2026-07-01 (notes-C §2.2),2026-10-31,verifie,id source approximatif : voir registry fusionné | |
| 438 | +CLM-APPC-04,appC,Règle 2.5 : le signataire accepte l'entière responsabilité du contenu et des conclusions,SRC-C-001,NPP (notes-C §2.1),2026-10-31,verifie, | |
| 439 | +CLM-APPC-05,appC,Tenue des dossiers (C-26 r. 133) art. 6 : conservation au moins 5 ans,SRC-C-004,notes-C §2.3,2026-10-31,verifie, | |
| 440 | +CLM-APPC-06,appC,CUSPAP 2026 : norme distincte « Reporting » ; définitions Extraordinary Assumption / Hypothetical Condition / Extraordinary Limiting Condition (s. 3),SRC-C-0xx (cuspap2026),notes-C §2.5,2026-10-31,verifie,id source à compléter par l'orchestrateur | |
| 441 | +CLM-APPC-07,appC,IVS 2025 : documentation des données et intrants et des modèles (IVS 104-105),SRC-C-014,notes-C §1.2 et ch. 10 §10.5.3,2026-10-31,verifie,numérotation 104/105 reprise du ch. 10 (édition 2025) | |
| 442 | +CLM-APPC-08,appC,RICS Red Book 2025 : VPS 6 Valuation reports,SRC-C-0xx (rics2025),notes-C §2.5,2026-10-31,partiellement_verifie,page RICS lue et PDF non téléchargé (notes-C point 5) | |
| 443 | +CLM-APPC-09,appC,LFM art. 43-46 : valeur réelle ; date de référence antérieure à l'entrée en vigueur du rôle ; immeuble supposé libre de baux (art. 45.1 cité au ch. 47),SRC-B-001,notes-B et ch. 47,2026-10-31,verifie,formulation volontairement non chiffrée (« antérieure ») ; le délai exact (18 mois) non vérifié dans les notes | |
| 444 | +CLM-APPC-10,appC,Art. 1936-1937 C.c.Q. : reconduction de plein droit ; l'aliénation ne résilie pas les baux,SRC-A-001,texte lu (notes-A ; articles 1936-1979 lus),2026-10-31,verifie, | |
| 445 | +CLM-APPC-11,appC,Code des professions art. 36 j) et 37 j) : titre réservé d'évaluateur agréé et champ d'exercice,SRC-C-009,notes-C §1.4,2026-10-31,verifie, | |
| 446 | +CLM-APPC-12,appC,Règle 2.3 élément 7 : réserves et hypothèses de base ; annexe E = modèle non contraignant quant à la forme ; élément 12 : attestation à formule imposée commençant par « J'atteste par la présente au mieux de ma connaissance et de ma conviction » (non reproduite au-delà de la paraphrase),SRC-C-001,NPP (notes-C §2.1),2026-10-31,verifie,texte de l'attestation et de l'annexe E non reproduits (paraphrase de la structure) | |
| 447 | +CLM-APPC-13,appC,Règle 2.3 élément 10 : justifier le rejet d'une méthode traditionnelle ; élément 11 : conformité à la substance et écarts aux directives,SRC-C-001,NPP (notes-C §3.1),2026-10-31,verifie, | |
| 448 | +CLM-APPC-14,appC,Convention de l'ouvrage : réserve incluse dans le RNE de la capitalisation directe ; CAPEX explicites dans le DCF ; composition semestrielle des hypothèques,CONV,manuscript/appendices/conventions-calcul.tex,2026-10-31,verifie,cohérence interne de l'ouvrage (pas une source externe) | |
| 449 | +CLM-APPC-15,appC,Identités (C.1)-(C.4) : RBE = RBP(1−v)+autres ; RNE = RBE−DE−RR ; V = RNE/R ; RCD = RNE/SD ; RPV = M0/V,CONV,CONVENTIONS.md §4 et traite/finance,2026-10-31,verifie,notation de l'ouvrage | |
modified
verification/review_log.md
+1 −0
@@ -5,3 +5,4 @@ | ||
| 5 | 5 | | 2026-10-31 | 0 | Création du dépôt, audit de l'environnement, style `traite-qc.sty`, Makefile, outils de vérification | Claude (orchestrateur) | |
| 6 | 6 | | 2026-10-31 | 1 | Cinq lots de sources vérifiés et fusionnés (registre, matrice des normes, bibliographie 422 clés, calibration 204 paramètres, données brutes 315 Mo) ; générateur synthétique recalibré | Claude + 5 agents | |
| 7 | 7 | | 2026-10-31 | 3 | Prototype : ch. 16, 21, 29, 30, 47 rédigés et testés ; PDF pilote 338 p. ; modules cas1-4 | Claude + 4 agents | |
| 8 | +- 2026-10-31 (ch. 3) : SRC-E-019 « RMR Montréal 2025 : 21 138 mises en chantier » ne couvre que 9 mois (34-10-0143 : juillet–septembre 2025 absents) → ne pas citer comme total annuel ; V39079 Valet commence en 2009 (SOURCES.md dit 2000). | |
| 8 | 9 | |