SPB Git
6.7 KB · 102 lines python
1#!/usr/bin/env python32"""Figure ch03 — Modèle des quatre quadrants (DiPasquale–Wheaton) tracé à partir des paramètres pédagogiques du marché type3(analysis/python/cases/ch03_cycles.py, SYNTHÉTIQUE). Équilibre de référence (trait plein) et nouvel équilibre après une baisse4du taux de capitalisation de 5 % à 4 % (tirets), avec la réaction de court terme (stock fixe) en pointillé.5Les quatre demi-axes ont des échelles propres (S en milliers de logements, P en k$/logement, R en k$/logement/an,6C en milliers de logements/an) ; les graduations sont dessinées explicitement.7Sortie : assets/figures/ch03/ch03-fig-quatre-quadrants.pdf"""8import sys, pathlib9ROOT = pathlib.Path(__file__).resolve().parents[3]10sys.path.insert(0, str(ROOT / "analysis/python")); sys.path.insert(0, str(ROOT / "analysis/python/cases"))11import numpy as np, matplotlib.pyplot as plt12from matplotlib.lines import Line2D13from traite.style import setup, palette, savefig, LARGEUR14import ch03_cycles as cas1516# facteurs d'affichage : unités de données → unités du tracé17FS = 1.0 / 1000.0          # S : logements → milliers (axe x positif, 0..150)18FP = 1.0 / 3000.0          # P : $ → k$/3 (axe x négatif ; 420 k$ → 140 unités)19FR = 1.0 / 1000.0          # R : $/an → k$ (axe y positif, 0..24)20FC = 4.0 / 1000.0          # C : logements/an → milliers × 4 (axe y négatif ; 3 000/an → 12 unités)212223def main():24    setup()25    p = cas.Parametres4Q()26    base = cas.equilibre_4q(p); i2 = 0.0427    nouv = cas.equilibre_4q(p, i=i2); ct = cas.court_terme_4q(p, i=i2)28    c0, c1 = palette.cat[0], palette.cat[1]29    fig, ax = plt.subplots(figsize=(LARGEUR, LARGEUR * 0.9))30    XP, XS, YR, YC = -150, 150, 24, -13.531    ax.set_xlim(XP - 2, XS + 2); ax.set_ylim(YC - 1, YR + 1)32    ax.axhline(0, color=palette.ink, lw=0.9); ax.axvline(0, color=palette.ink, lw=0.9)33    ax.grid(False); ax.set_xticks([]); ax.set_yticks([])34    for s in ax.spines.values():35        s.set_visible(False)36    # graduations manuelles37    for v in (50, 100, 150):38        ax.plot([v, v], [0, -0.35], color=palette.ink, lw=0.7); ax.text(v, -0.6, f"{v}", ha="center", va="top", fontsize=7, color=palette.ink2)39    for v in (150, 300, 450):40        ax.plot([-v / 3, -v / 3], [0, -0.35], color=palette.ink, lw=0.7); ax.text(-v / 3, -0.6, f"{v}", ha="center", va="top", fontsize=7, color=palette.ink2)41    for v in (6, 12, 18, 24):42        ax.plot([0, -1.5], [v, v], color=palette.ink, lw=0.7); ax.text(-2.5, v, f"{v}", ha="right", va="center", fontsize=7, color=palette.ink2)43    for v in (1, 2, 3):44        ax.plot([0, -1.5], [-4 * v, -4 * v], color=palette.ink, lw=0.7); ax.text(-2.5, -4 * v, f"{v}", ha="right", va="center", fontsize=7, color=palette.ink2)45    # --- NE : marché de l'espace  R = (a − S)/b46    S = np.linspace(50_000, 150_000, 50)47    ax.plot(S * FS, (p.a - S) / p.b * FR, color=c0, lw=1.8)48    # --- NW : marché des actifs  P = R/i49    R = np.linspace(0, YR / FR, 50)50    ax.plot(-R / p.i * FP, R * FR, color=c0, lw=1.8)51    ax.plot(-R / i2 * FP, R * FR, color=c1, lw=1.8, ls="--")52    # --- SW : construction  C = (P − c0)/c153    P = np.linspace(p.c0, 450_000, 50)54    ax.plot(-P * FP, -(P - p.c0) / p.c1 * FC, color=c0, lw=1.8)55    # --- SE : stock stationnaire  S = C/δ56    C = np.linspace(0, 1_500, 50)57    ax.plot(C / p.delta * FS, -C * FC, color=c0, lw=1.8)5859    def rectangle(eq, color, ls, lw=1.0):60        Sx, Rx, Px, Cx = eq["S"] * FS, eq["R"] * FR, eq["P"] * FP, eq["C"] * FC61        ax.plot([Sx, Sx], [-Cx, Rx], color=color, lw=lw, ls=ls)62        ax.plot([-Px, Sx], [Rx, Rx], color=color, lw=lw, ls=ls)63        ax.plot([-Px, -Px], [Rx, -Cx], color=color, lw=lw, ls=ls)64        ax.plot([-Px, Sx], [-Cx, -Cx], color=color, lw=lw, ls=ls)65        for x, y in ((Sx, Rx), (-Px, Rx), (-Px, -Cx), (Sx, -Cx)):66            ax.plot(x, y, "o", color=color, ms=3.8)6768    rectangle(base, c0, "-")69    rectangle(nouv, c1, "--")70    # court terme : stock fixe, P bondit à R0/i2, C bondit, mais S n'a pas encore bougé71    Pct, Cct = ct["P"] * FP, ct["C"] * FC72    ax.plot([-Pct, base["S"] * FS], [base["R"] * FR, base["R"] * FR], color=c1, lw=1.0, ls=":")73    ax.plot([-Pct, -Pct], [base["R"] * FR, -Cct], color=c1, lw=1.0, ls=":")74    ax.plot([-Pct, base["S"] * FS], [-Cct, -Cct], color=c1, lw=1.0, ls=":")75    ax.plot(-Pct, base["R"] * FR, "o", color=c1, ms=3.8, mfc="white"); ax.plot(-Pct, -Cct, "o", color=c1, ms=3.8, mfc="white")76    # titres des quadrants (coins) et noms d'axes77    ax.text(XS, YR, "Marché de l'espace\n$D(R) = S$", ha="right", va="top", fontsize=8, color=palette.ink2)78    ax.text(XP, 9.0, "Marché des actifs\n$P = R/i$", ha="left", va="center", fontsize=8, color=palette.ink2)79    ax.text(XP, YC + 4.0, "Construction\n$C = (P - c_0)/c_1$", ha="left", va="bottom", fontsize=8, color=palette.ink2)80    ax.text(XS, YC, "Ajustement du stock\n$C = \\delta S$", ha="right", va="bottom", fontsize=8, color=palette.ink2)81    ax.text(XS, 1.0, "Stock $S$\n(milliers de logements)", ha="right", va="bottom", fontsize=7.5)82    ax.text(XP, 1.0, "Prix $P$\n(k\\$ par logement)", ha="left", va="bottom", fontsize=7.5)83    ax.text(-2.5, YR, "Loyer $R$\n(k\\$/logement/an)", ha="right", va="top", fontsize=7.5)84    ax.text(-2.5, YC, "Construction $C$\n(milliers de logements/an)", ha="right", va="bottom", fontsize=7.5)85    # annotations numériques86    ax.text(base["S"] * FS + 1.5, base["R"] * FR + 0.4, f"$S^*$ = {base['S']/1000:.0f} ; $R^*$ = {base['R']/1000:.1f}", fontsize=7.5, color=c0, ha="left", va="bottom")87    ax.text(nouv["S"] * FS + 1.5, nouv["R"] * FR - 0.4, f"{nouv['S']/1000:.1f} ; {nouv['R']/1000:.1f}", fontsize=7.5, color=c1, ha="left", va="top")88    ax.text(-base["P"] * FP + 1.5, base["R"] * FR + 0.4, f"$P^*$ = {base['P']/1000:.0f}", fontsize=7.5, color=c0, ha="left", va="bottom")89    ax.text(-nouv["P"] * FP - 1.5, nouv["R"] * FR - 0.4, f"{nouv['P']/1000:.1f}", fontsize=7.5, color=c1, ha="right", va="top")90    ax.text(-Pct, base["R"] * FR + 1.6, f"court terme :\n$P$ = {ct['P']/1000:.0f}", fontsize=7.5, color=c1, ha="center", va="bottom")91    ax.text(-Pct + 1.5, -Cct - 0.3, f"$C$ = {ct['C']/1000:.1f}", fontsize=7.5, color=c1, ha="left", va="top")92    ax.text(base["S"] * FS + 1.5, -base["C"] * FC - 0.3, f"$C^*$ = {base['C']/1000:.1f}", fontsize=7.5, color=c0, ha="left", va="top")93    h = [Line2D([], [], color=c0, lw=1.8, label="équilibre de référence ($i$ = 5 %)"),94         Line2D([], [], color=c1, lw=1.8, ls="--", label="long terme après la baisse à $i$ = 4 %"),95         Line2D([], [], color=c1, lw=1.0, ls=":", label="court terme (stock fixe)")]96    ax.legend(handles=h, loc="upper center", bbox_to_anchor=(0.5, -0.01), ncol=3, fontsize=7.5, frameon=False)97    out = savefig(fig, "ch03/ch03-fig-quatre-quadrants.pdf"); print("OK", out)9899100if __name__ == "__main__":101    main()102
Files · 2a26d365cc23544c8343a62de5e56cfc4c06cb46
Menu

Theme