#!/usr/bin/env python3 """Figure ch22 — répartition du devis du sujet du cas 1 par corps de métier et par type d'unité de mesure (mesuré en m³/m²/m lin., dénombré en unités/appareils/semaines, forfait). DONNÉES SYNTHÉTIQUES, graine 20261031. Mêmes sept blocs et même ordre que la figure 21 (ch21_decomposition.py). Sortie : assets/figures/ch22/ch22-fig-repartition-corps.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 matplotlib.pyplot as plt from traite.style import setup, palette, savefig, LARGEUR, fmt_dollars import ch21_cout_neuf as c21 import ch22_metre as cas def main(): setup() devis, _ = c21.charger() rep = cas.repartition_par_type(devis) types = [c for c in ("mesuré", "dénombré", "forfait") if c in rep.columns] etiquettes = {"mesuré": "Mesuré (m³, m², m lin.)", "dénombré": "Dénombré (unités, appareils, semaines)", "forfait": "Forfait"} couleurs = {"mesuré": palette.cat[0], "dénombré": palette.cat[2], "forfait": palette.cat[1]} fig, ax = plt.subplots(figsize=(LARGEUR, 3.3)) ordre = list(rep.index)[::-1] # premier bloc en haut y = list(range(len(ordre))) gauche = [0.0] * len(ordre) for t in types: vals = [float(rep.loc[c, t]) for c in ordre] ax.barh(y, vals, left=gauche, color=couleurs[t], height=0.66, label=etiquettes[t]) gauche = [g + v for g, v in zip(gauche, vals)] tot_di = float(rep.to_numpy().sum()) for k, c in enumerate(ordre): tot = float(rep.loc[c].sum()) ax.text(tot + 900, k, f"{fmt_dollars(tot)} ({tot/tot_di*100:.1f} %)", va="center", ha="left", fontsize=8, color=palette.ink) ax.set_yticks(y); ax.set_yticklabels(ordre) ax.set_xlim(0, max(gauche) * 1.45) ax.xaxis.set_major_formatter(plt.FuncFormatter(fmt_dollars)) ax.set_xlabel("Montant du devis (dollars de 2026) ; part du sous-total directs + indirects") ax.grid(axis="y", visible=False) parts = rep.sum() / tot_di * 100 ax.set_title("Sous-total D + I = " + fmt_dollars(tot_di) + " : " + ", ".join(f"{etiquettes[t].split(' (')[0].lower()} {parts[t]:.1f} %" for t in types), loc="left", fontsize=8.5, color=palette.ink) ax.legend(loc="upper center", bbox_to_anchor=(0.45, -0.2), ncol=3, fontsize=7.5) out = savefig(fig, "ch22/ch22-fig-repartition-corps.pdf"); print("OK", out) if __name__ == "__main__": main()