SPB Git
4.3 KB · 64 lines python
1#!/usr/bin/env python32"""Figure ch22 — métré de contrôle du sujet du cas 1 : écart relatif entre la quantité recalculée depuis le plan coté3et la quantité du devis, par poste mesuré ou dénombré, avec les bandes de tolérance (±5 % conforme, ±10 % à préciser).4DONNÉES SYNTHÉTIQUES, graine 20261031. Sortie : assets/figures/ch22/ch22-fig-ecarts-metre.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 matplotlib.pyplot as plt9from matplotlib.lines import Line2D10from traite.style import setup, palette, savefig, LARGEUR11import ch21_cout_neuf as c2112import ch22_metre as cas1314COURT = {"Excavation et remblai": "Excavation et remblai (m³)", "Fondations béton (semelles, murs)": "Fondations béton (m³)",15         "Dalle de sous-sol": "Dalle de sous-sol (m²)", "Drain français et imperméabilisation": "Drain français (m lin.)",16         "Charpente murs et planchers": "Charpente murs et planchers (m²)", "Fermes de toit": "Fermes de toit (m²)",17         "Couverture bardeaux d'asphalte": "Couverture (m²)", "Revêtement extérieur (brique 40 % / fibrociment 60 %)": "Revêtement extérieur (m²)",18         "Isolation et pare-vapeur": "Isolation et pare-vapeur (m²)", "Fenêtres (PVC double vitrage)": "Fenêtres (unités)",19         "Portes extérieures": "Portes extérieures (unités)", "Porte de garage": "Porte de garage (unité)",20         "Gypse et tirage de joints": "Gypse (m²)", "Peinture intérieure": "Peinture (m²)", "Planchers bois franc": "Planchers bois franc (m²)",21         "Planchers céramique": "Planchers céramique (m²)", "Planchers flottants (sous-sol)": "Planchers flottants (m²)",22         "Escaliers": "Escaliers (unités)", "Portes intérieures et moulures": "Portes intérieures (unités)",23         "Plomberie brute et finition": "Plomberie (appareils)", "Gouttières et solins": "Gouttières et solins (m lin.)",24         "Balcon et perron": "Balcon et perron (m²)", "Entrée asphaltée": "Entrée asphaltée (m²)", "Supervision de chantier (salaires)": "Supervision (semaines)"}25COUL = {"conforme": palette.cat[0], "à préciser": palette.status["warning"], "à justifier": palette.status["critical"]}262728def main():29    setup()30    devis, _ = c21.charger()31    V = cas.verification(devis)32    V = V[V["classe"] != "forfait"].copy()33    V["ordre"] = V["corps"].map({c: i for i, c in enumerate(c21.ORDRE_CORPS)})34    V = V.sort_values(["ordre"], kind="stable").reset_index(drop=True)35    V = V.iloc[::-1].reset_index(drop=True)   # premier poste en haut36    y = list(range(len(V)))37    fig, ax = plt.subplots(figsize=(LARGEUR, 4.6))38    ax.axvspan(-10, -5, color=palette.status["warning"], alpha=0.10, lw=0); ax.axvspan(5, 10, color=palette.status["warning"], alpha=0.10, lw=0)39    ax.axvspan(-5, 5, color=palette.cat[0], alpha=0.07, lw=0)40    ax.axvline(0, color=palette.ink2, lw=0.8)41    for k, r in V.iterrows():42        ax.plot([0, r["ecart"] * 100], [k, k], color=palette.grid, lw=1.2, zorder=1)43        ax.plot(r["ecart"] * 100, k, "o", color=COUL[r["verdict"]], ms=5.5, zorder=3)44        if abs(r["ecart"]) > 0.0005:45            ax.text(r["ecart"] * 100 + (0.6 if r["ecart"] >= 0 else -0.6), k, f"{r['ecart']*100:+.1f} %", va="center",46                    ha="left" if r["ecart"] >= 0 else "right", fontsize=7.2, color=palette.ink)47    ax.set_yticks(y); ax.set_yticklabels([COURT.get(p, p) for p in V["poste"]], fontsize=7.6)48    ax.set_xlim(-22, 26); ax.set_xlabel("Écart du métré de contrôle par rapport au devis (%) : (quantité recalculée − quantité du devis) / devis")49    ax.grid(axis="y", visible=False)50    # séparateurs de corps51    prev = None52    for k, c in enumerate(V["corps"]):53        if prev is not None and c != prev:54            ax.axhline(k - 0.5, color=palette.grid, lw=0.6)55        prev = c56    nb = V["verdict"].value_counts()57    handles = [Line2D([], [], marker="o", color=COUL[v], ls="", ms=5.5, label=f"{v} ({int(nb.get(v, 0))})") for v in ("conforme", "à préciser", "à justifier")]58    ax.legend(handles=handles, loc="lower right", fontsize=7.5, title="Verdict (tolérances ±5 % et ±10 %)", title_fontsize=7.5)59    out = savefig(fig, "ch22/ch22-fig-ecarts-metre.pdf"); print("OK", out)606162if __name__ == "__main__":63    main()64
Files · d2b579d9e218b409826a327e9268215b1d11dd98
Menu

Theme