spb/traite-evaluation-qc
Public
1#!/usr/bin/env python32"""Figure ch43 — municipalité fictive de Rivière-Fictive (DONNÉES SYNTHÉTIQUES) : 625 lots, classes d'exposition le long de la3rivière, lots riverains du lac (aménité sans exposition) ; (b) prix par m² habitable des ventes selon la distance à la rivière,4avant et après l'inondation de 2019 (moyennes par bande de 40 m).5Sortie : assets/figures/ch43/ch43-fig-carte-riverain.pdf"""6import sys, pathlib7ROOT = pathlib.Path(__file__).resolve().parents[3]8sys.path.insert(0, str(ROOT / "analysis/python")); sys.path.insert(0, str(ROOT / "analysis/python/cases"))9import numpy as np, pandas as pd, matplotlib.pyplot as plt10from matplotlib.patches import Rectangle11from traite.style import setup, palette, savefig, LARGEUR12import ch43_risques as cas131415def main():16 setup()17 lots, ventes = cas.charger_ou_generer(ecrire=False)18 fig, (a, b) = plt.subplots(1, 2, figsize=(LARGEUR, 3.4), gridspec_kw={"width_ratios": [1.15, 1]})19 couleurs = {"très élevée": palette.seq[-1], "élevée": palette.seq[-2], "modérée": palette.seq[-3], "faible": palette.seq[-4], "hors zone": "#e9e7e2"}20 for _, L in lots.iterrows():21 a.add_patch(Rectangle((L["x"] * cas.PAS, L["y"] * cas.PAS), cas.PAS, cas.PAS, facecolor=couleurs[L["classe"]], edgecolor="white", linewidth=0.3))22 if L["rive"] and L["classe"] == "hors zone":23 a.add_patch(Rectangle((L["x"] * cas.PAS + 6, L["y"] * cas.PAS + 6), cas.PAS - 12, cas.PAS - 12, facecolor="none", edgecolor=palette.cat[1], linewidth=0.7))24 a.add_patch(Rectangle((0, -60), cas.NCOL * cas.PAS, 60, facecolor=palette.seq[2], edgecolor="none"))25 a.add_patch(Rectangle((cas.NCOL * cas.PAS, -60), 70, cas.NLIG * cas.PAS + 60, facecolor=palette.seq[2], edgecolor="none"))26 a.text(cas.NCOL * cas.PAS / 2, -32, "rivière", ha="center", va="center", fontsize=7.5, color="white")27 a.text(cas.NCOL * cas.PAS + 35, cas.NLIG * cas.PAS / 2, "lac", rotation=90, ha="center", va="center", fontsize=7.5, color="white")28 a.set_xlim(-10, cas.NCOL * cas.PAS + 80); a.set_ylim(-70, cas.NLIG * cas.PAS + 10); a.set_aspect("equal"); a.grid(False)29 a.set_xticks([]); a.set_yticks([]); a.set_title("(a) Lots et classes d'exposition", loc="left")30 for i, (c, col) in enumerate(couleurs.items()):31 a.add_patch(Rectangle((cas.NCOL * cas.PAS + 90 + 0, 0), 0, 0, facecolor=col))32 handles = [Rectangle((0, 0), 1, 1, facecolor=couleurs[c], edgecolor="white") for c in couleurs] + [Rectangle((0, 0), 1, 1, facecolor="none", edgecolor=palette.cat[1])]33 a.legend(handles, list(couleurs) + ["vue sur l'eau hors zone (lac)"], loc="upper left", fontsize=6.8, ncol=2, frameon=True, framealpha=0.9, bbox_to_anchor=(0.0, 1.0))34 # (b) prix/m² par bande de distance, avant / après, lots non riverains du lac seulement35 v = ventes[~ventes["id_lot"].str[-2:].astype(int).isin(cas.COLS_LAC)].copy()36 v["pm2"] = v["prix_vente"] / v["sup_habitable_m2"]37 t = (pd.to_datetime(v["date_vente"]) - pd.Timestamp(cas.DATE_REF)).dt.days / 365.2538 v["pm2_t"] = v["pm2"] / (1 + cas.TAUX_MARCHE) ** t # ramené à la date de référence39 v["bande"] = (v["dist_riviere_m"] // 40).clip(upper=12)40 for post, col, lab in [(0, palette.cat[2], "avant mai 2019"), (1, palette.cat[0], "après mai 2019")]:41 g = v[v["post"] == post].groupby("bande")["pm2_t"].mean()42 b.plot(g.index * 40 + 20, g.values, marker="o", markersize=3.5, color=col, linewidth=1.3, label=lab)43 for xb, lab in [(80, "TE | E"), (160, "E | M"), (240, "M | F"), (280, "F | hors zone")]:44 b.axvline(xb if xb != 280 else 280, color=palette.ink2, linewidth=0.6, linestyle=":")45 b.set_xlabel("Distance à la rivière (m) — bandes de 40 m, lots du lac exclus"); b.set_ylabel("Prix par m² habitable, à la date de référence ($/m²)")46 b.set_title("(b) Prix unitaire et distance à la rivière", loc="left"); b.legend(loc="lower right", fontsize=7)47 fig.tight_layout()48 savefig(fig, "ch43/ch43-fig-carte-riverain.pdf")495051if __name__ == "__main__":52 main()53