#!/usr/bin/env python3 """Figure ch07 — ventes appariées : prix sans et avec servitude de passage pour 12 paires (DONNÉES SYNTHÉTIQUES, graine 20261031). Sortie : assets/figures/ch07/ch07-fig-paires-servitude.pdf""" 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 traite.style import setup, palette, savefig, LARGEUR, fmt_dollars import ch07_servitudes_copro as cas def main(): setup() p = cas.paires_servitude() ordre = np.argsort(p["sans"]) sans, avec = p["sans"][ordre], p["avec"][ordre] y = np.arange(len(sans)) fig, ax = plt.subplots(figsize=(LARGEUR, 3.6)) for yi, s, a in zip(y, sans, avec): ax.plot([a, s], [yi, yi], color=palette.grid, lw=2.2, zorder=1) ax.scatter(sans, y, color=palette.cat[0], s=34, zorder=3, label="Lot sans servitude (fonds libre)") ax.scatter(avec, y, color=palette.cat[1], s=34, zorder=3, marker="D", label="Lot grevé d'une servitude de passage") for yi, s, a in zip(y, sans, avec): ax.text(s + 4000, yi, f"{a - s:+,.0f} $".replace(",", " "), va="center", ha="left", fontsize=7.5, color=palette.ink2) ax.set_yticks(y); ax.set_yticklabels([f"paire {int(i) + 1}" for i in ordre]) ax.xaxis.set_major_formatter(plt.FuncFormatter(fmt_dollars)) ax.set_xlim(avec.min() - 15000, sans.max() + 60000) ax.set_xlabel("Prix de vente (dollars de 2026, données synthétiques)") ax.grid(axis="y", visible=False) ax.set_title((f"Différence moyenne {p['moyenne']:,.0f} \\$ (IC 95 % : {p['ic95'][0]:,.0f} à {p['ic95'][1]:,.0f} \\$)@ soit {p['effet_relatif_moyen']*100:.1f} % du prix").replace(",", " ").replace(".", ",").replace("@", ","), loc="left", fontsize=8.5) ax.legend(loc="lower right", fontsize=7.5) out = savefig(fig, "ch07/ch07-fig-paires-servitude.pdf"); print("OK", out) if __name__ == "__main__": main()