SPB Git
2.6 KB · 44 lines python
1#!/usr/bin/env python32"""Figure ch42 — grille de comparaison de la fraction F4 : prix observé, prix ajusté et décomposition des ajustements des cinq comparables3fictifs ; valeur pondérée (DONNÉES SYNTHÉTIQUES).4Sortie : assets/figures/ch42/ch42-fig-grille-fraction.pdf"""5import sys, pathlib6ROOT = pathlib.Path(__file__).resolve().parents[3]7sys.path.insert(0, str(ROOT / "analysis/python")); sys.path.insert(0, str(ROOT / "analysis/python/cases"))8import numpy as np, matplotlib.pyplot as plt9from traite.style import setup, palette, savefig, LARGEUR, fmt_dollars10import ch42_copro as cas1112ORDRE = ["marché", "étage", "orientation", "stationnement", "superficie", "charges", "syndicat"]13COUL = {k: c for k, c in zip(ORDRE, palette.cat[:6] + [palette.ink2])}1415def main():16    setup()17    g = cas.grille_fraction(); comps, aj, w = g["comparables"], g["ajustes"], g["poids"]; rec = g["reconciliation"]18    y = np.arange(len(aj))19    fig, ax = plt.subplots(figsize=(LARGEUR, 3.6))20    for yi, a, c in zip(y, aj, comps):21        pos = a.prix; neg = a.prix22        for k in ORDRE:23            v = a.ajustements[k]24            if v >= 0:25                ax.barh(yi, v, left=pos, height=0.5, color=COUL[k]); pos += v26            else:27                neg += v; ax.barh(yi, -v, left=neg, height=0.5, color=COUL[k], alpha=0.55, label=None)28        ax.plot([a.prix], [yi], marker="o", color=palette.ink, ms=5, zorder=5)29        ax.plot([a.prix_ajuste], [yi], marker="D", color=palette.ink, ms=5, zorder=5)30        ax.text(max(pos, a.prix_ajuste) + 3000, yi, f"{a.prix_ajuste:,.0f} $  (w = {wi:.2f})".replace(",", " ").replace("0.", "0,") if (wi := w[yi]) is not None else "", va="center", fontsize=7.2)31    ax.axvline(rec["valeur_ponderee"], color=palette.ink2, ls="--", lw=1.1)32    ax.text(rec["valeur_ponderee"], -0.75, f"valeur pondérée {rec['valeur_ponderee']:,.0f} $".replace(",", " "), ha="center", fontsize=7.6, color=palette.ink)33    ax.set_yticks(y); ax.set_yticklabels([f"{c['nom']} — {c['immeuble']}" for c in comps]); ax.invert_yaxis()34    ax.xaxis.set_major_formatter(plt.FuncFormatter(fmt_dollars))35    ax.set_xlim(300_000, 450_000); ax.set_xlabel("Prix observé (cercle) → prix ajusté (losange) ; ajustements négatifs en teinte pâle")36    ax.grid(axis="y", visible=False)37    from matplotlib.patches import Patch38    handles = [Patch(color=COUL[k], label=k) for k in ORDRE]39    ax.legend(handles=handles, loc="upper center", bbox_to_anchor=(0.45, -0.22), ncol=4, fontsize=7.2, title="Ajustements (du point de vue du sujet)", title_fontsize=7.4)40    out = savefig(fig, "ch42/ch42-fig-grille-fraction.pdf"); print("OK", out)4142if __name__ == "__main__":43    main()44
Files · main
Menu

Theme