#!/usr/bin/env python3 """Figures du chapitre 41 (DONNÉES SYNTHÉTIQUES du cas 1 ; zonage FICTIF ; hypothèses déclarées dans ch41_phmu.py) : ch41-fig-residuel-densite.pdf — valeur résiduelle du terrain selon le nombre de logements et le niveau des prix ; ch41-fig-surface-prix-cout.pdf — surface de la valeur résiduelle du triplex (prix des unités × coût de construction) ; ch41-fig-frontiere-option.pdf — prime de l'option de redéveloppement et frontière d'exercice selon la volatilité. Usage : python3 analysis/python/figures/ch41_figures.py""" import sys, pathlib ROOT = pathlib.Path(__file__).resolve().parents[3] sys.path.insert(0, str(ROOT / "analysis/python")); sys.path.insert(0, str(ROOT / "analysis/python/cases")) import numpy as np, matplotlib.pyplot as plt from matplotlib.ticker import FuncFormatter from traite.style import setup, palette, savefig, LARGEUR import ch41_phmu as cas K = FuncFormatter(lambda x, p: f"{x/1000:.0f}") def fig_densite(R): df = cas.courbe_densite(R, R["couts"]) fig, ax = plt.subplots(figsize=(LARGEUR, 3.1)) for k, f in enumerate(sorted(df.facteur_prix.unique())): d = df[df.facteur_prix == f].sort_values("n") lab = {0.85: "prix des unités −15 %", 1.0: "prix de base (modèle hédonique)", 1.15: "prix des unités +15 %"}[round(f, 2)] ax.plot(d.n, d.v_l, marker="o", color=palette.cat[k], label=lab) np_ = d[~d.permis] ax.plot(np_.n, np_.v_l, marker="o", linestyle="none", markerfacecolor="white", color=palette.cat[k]) ax.axhline(R["v_l_marche"], color=palette.ink2, linestyle="--", linewidth=1) ax.text(2.55, R["v_l_marche"] + 6000, "valeur du terrain par comparaison (ch. 25) : 277 000 $", fontsize=7.5, color=palette.ink2) occ = R["usages"]["U"]["occupant"]["valeur_residuelle"] ax.plot([1], [occ], marker="s", color=palette.ink, linestyle="none") ax.annotate("maison neuve, convention\n« construction sur commande »\n(266 187 $)", (1, occ), xytext=(1.3, occ - 60000), fontsize=7.5, arrowprops=dict(arrowstyle="-", color=palette.ink2, lw=0.8)) ax.axvspan(3.5, 4.5, color=palette.grid, alpha=0.6, lw=0) ax.text(4, 20000, "non permis\n(COS 0,60)", ha="center", fontsize=7.5, color=palette.ink2) ax.set_xticks([1, 2, 3, 4]); ax.set_xticklabels(["1 logement\n(maison neuve)", "2 logements\n(duplex)", "3 logements\n(triplex)", "4 logements\n(quadruplex)"]) ax.set_xlim(0.7, 4.5); ax.set_ylim(0, None) ax.yaxis.set_major_formatter(K); ax.set_ylabel("Valeur résiduelle du terrain (milliers de $)") ax.legend(loc="lower right", fontsize=7.5, bbox_to_anchor=(0.78, 0.02)) fig.tight_layout(); savefig(fig, "ch41/ch41-fig-residuel-densite.pdf") def fig_surface(R): fp, fc, Z = cas.surface_prix_cout(R["usages"]["T"], R["couts"], fp=np.linspace(0.80, 1.55, 76)) X, Y = fp * 100 - 100, fc * 100 - 100 fig, ax = plt.subplots(figsize=(LARGEUR, 3.4)) cf = ax.contourf(X, Y, np.clip(Z, 0, None) / 1000, levels=24, cmap=palette.cmap_seq()) cb = fig.colorbar(cf, ax=ax, pad=0.02); cb.set_label("Valeur résiduelle du terrain (milliers de $)") cs = ax.contour(X, Y, Z, levels=[0, R["v_l_marche"], R["v_act"]], colors=[palette.ink2, palette.ink, palette.ink], linewidths=[0.9, 1.0, 1.4], linestyles=["dotted", "dashed", "solid"]) ax.clabel(cs, fmt={0: "résidu nul", R["v_l_marche"]: "277 000 $ : terrain au prix du marché", R["v_act"]: "600 000 $ : démolition rentable"}, fontsize=7, inline=True, inline_spacing=6) ax.plot([0], [0], marker="o", color=palette.ink, markersize=5) ax.annotate("cas de base : 229 265 $", (0, 0), xytext=(-18, -8), fontsize=7.5, color=palette.ink, arrowprops=dict(arrowstyle="-", color=palette.ink, lw=0.7)) ax.set_xlabel("Variation du prix des unités neuves (%)"); ax.set_ylabel("Variation du coût de construction (%)") ax.grid(False) fig.tight_layout(); savefig(fig, "ch41/ch41-fig-surface-prix-cout.pdf") def fig_frontiere(R): O = cas.OPTION; rT = R["usages"]["T"]["promoteur"]; gdv0 = rT["gdv"] sigmas = np.linspace(0.03, 0.30, 55) fig, (a, b) = plt.subplots(1, 2, figsize=(LARGEUR, 3.1)) for k, v_act in enumerate((600_000.0, 450_000.0, 300_000.0)): aa, bb = cas.coefficients_option(rT, v_act) prem = [cas.option_redeveloppement(gdv0, aa, bb, s, O["dt"], O["r"], O["y"], O["n"])["prime_americaine"] for s in sigmas] a.plot(sigmas * 100, np.array(prem) / 1000, color=palette.cat[k], label=f"valeur en usage {v_act/1000:.0f} k$") a.set_xlabel("Volatilité annuelle du prix des unités σ (%)"); a.set_ylabel("Prime de l'option (milliers de $)") a.set_title("(a) valeur de l'attente", loc="left"); a.legend(fontsize=7.5) aa, bb = cas.coefficients_option(rT, R["v_act"]) seuil_van = bb / aa / gdv0 front = [cas.frontiere_exercice_t1(aa, bb, s, O["dt"], O["r"], O["y"]) / gdv0 for s in sigmas] b.plot(sigmas * 100, np.array(front), color=palette.cat[0], label="frontière d'exercice (attente optimale au-dessous)") b.axhline(seuil_van, color=palette.cat[1], linestyle="--", label="seuil de la règle de la VAN (résidu = valeur en usage)") b.axhline(1.0, color=palette.ink2, linestyle=":", linewidth=1); b.text(3.5, 1.02, "produit de vente actuel", fontsize=7.5, color=palette.ink2) b.fill_between(sigmas * 100, seuil_van, np.array(front), color=palette.cat[0], alpha=0.12, lw=0) b.text(16, (seuil_van + 1.9) / 2, "zone d'attente :\nVAN > 0 mais attendre vaut plus", fontsize=7.5, ha="center", color=palette.ink) b.set_xlabel("Volatilité annuelle du prix des unités σ (%)"); b.set_ylabel("Produit de vente / produit actuel") b.set_ylim(0.9, 2.6); b.set_title("(b) frontière d'exercice au nœud t = 1", loc="left"); b.legend(fontsize=7, loc="upper left") fig.tight_layout(); savefig(fig, "ch41/ch41-fig-frontiere-option.pdf") def main(): setup(); R = cas.exemple_complet() fig_densite(R); fig_surface(R); fig_frontiere(R) if __name__ == "__main__": main()