spb/traite-evaluation-qc
Public
1#!/usr/bin/env python32"""Chapitre 10 — figures Matplotlib :3 ch10-fig-chronologie-cadres.pdf chronologie documentaire des cadres normatifs (dates du registre C) ;4 ch10-fig-matrice-normes.pdf statistiques descriptives de la matrice des dispositions (sources/normes-C.csv) ;5 ch10-fig-grille-controle.pdf grille de détection des douze éléments dans quatre extraits FICTIFS.6Usage (racine du dépôt) : PYTHONPATH=analysis/python python3 analysis/python/figures/ch10_figures.py"""7from __future__ import annotations8import sys, pathlib, datetime as dt9import matplotlib.pyplot as plt10import matplotlib.dates as mdates11from matplotlib.lines import Line2D12from matplotlib.patches import Patch1314sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1]))15sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1] / "cases"))16from traite.style import setup, palette, savefig, LARGEUR17import ch10_matrice_normes as mn18import ch10_controle_rapport as cr1920def fig_chronologie(ch):21 fig, ax = plt.subplots(figsize=(LARGEUR, 4.0))22 ch = ch.copy()23 ch["ordre"] = ch["effet"].rank(method="first")24 ch = ch.sort_values("effet")25 col = {"en_vigueur": palette.cat[0], "remplace": palette.ink2, "projet": palette.cat[1], "reference": palette.cat[2]}26 fin_axe = dt.date(2028, 6, 30)27 labels = []28 for i, (_, r) in enumerate(ch.iterrows()):29 c = col[r["statut"]]30 fin = r["fin"] if r["fin"] is not None else (r["effet"] if r["statut"] == "projet" else fin_axe)31 ls = ":" if r["statut"] == "projet" else "-"32 if r["statut"] != "projet":33 ax.plot([r["effet"], fin], [i, i], color=c, lw=2.2, solid_capstyle="butt", alpha=0.9 if r["statut"] != "remplace" else 0.6)34 if r["publication"] is not None:35 ax.plot([r["publication"], r["effet"]], [i, i], color=c, lw=1.0, ls=ls)36 ax.plot(r["publication"], i, marker="o", mfc="white", mec=c, ms=5.5, mew=1.3)37 ax.plot(r["effet"], i, marker="o", color=c, ms=5.5)38 if r["statut"] == "projet":39 ax.plot(r["effet"], i, marker="o", mfc="white", mec=c, ms=5.5, mew=1.3)40 if r["fin"] is not None:41 ax.plot(r["fin"], i, marker="|", color=c, ms=9, mew=1.6)42 labels.append(r["cadre"] + (" *" if r.get("verification") == "partielle" else ""))43 ax.axvline(mn.DATE_REF, color=palette.ink2, lw=0.8, ls="--")44 ax.text(mn.DATE_REF, len(ch) - 0.4, " 31 oct. 2026", fontsize=7.5, color=palette.ink2, va="bottom")45 ax.set_yticks(range(len(ch))); ax.set_yticklabels(labels, fontsize=8)46 ax.tick_params(axis="y", length=0)47 ax.set_ylim(-0.7, len(ch) - 0.3)48 ax.set_xlim(dt.date(2012, 6, 1), fin_axe)49 ax.xaxis.set_major_locator(mdates.YearLocator(2)); ax.xaxis.set_major_formatter(mdates.DateFormatter("%Y"))50 ax.grid(axis="y", visible=False)51 for s in ("left",): ax.spines[s].set_visible(False)52 h = [Line2D([0], [0], color=col["en_vigueur"], lw=2.2, label="en vigueur au 31 oct. 2026"),53 Line2D([0], [0], color=col["remplace"], lw=2.2, alpha=0.6, label="remplacé"),54 Line2D([0], [0], color=col["reference"], lw=2.2, label="norme de référence (IAAO)"),55 Line2D([0], [0], color=col["projet"], lw=1.0, ls=":", marker="o", mfc="white", label="projet (date proposée)"),56 Line2D([0], [0], color=palette.ink2, lw=0, marker="o", mfc="white", mec=palette.ink2, label="publication → entrée en vigueur")]57 ax.legend(handles=h, loc="upper center", bbox_to_anchor=(0.5, -0.14), fontsize=7.5, ncol=3, frameon=False)58 ax.set_xlabel("Année")59 savefig(fig, "ch10/ch10-fig-chronologie-cadres.pdf")6061def fig_matrice(st):62 fig, (a1, a2) = plt.subplots(1, 2, figsize=(LARGEUR, 2.9), gridspec_kw={"width_ratios": [1.15, 1]})63 fam = st["par_famille"].sort_values()64 couleurs = [palette.cat[0] if f.startswith("OEAQ") else palette.ink2 for f in fam.index]65 a1.barh(fam.index, fam.values, color=couleurs, height=0.62)66 for y, v in enumerate(fam.values):67 a1.text(v + 0.6, y, str(v), va="center", fontsize=8, color=palette.ink)68 a1.set_xlabel("Dispositions (n = %d)" % st["n"]); a1.set_xlim(0, fam.max() * 1.18)69 a1.set_title("Par famille de sources", loc="left", fontsize=9)70 a1.grid(axis="y", visible=False)71 top = st["par_chapitre"].head(10).sort_values()72 couleurs2 = [palette.cat[0] if c == "ch10" else palette.ink2 for c in top.index]73 a2.barh([c.replace("ch", "ch. ") for c in top.index], top.values, color=couleurs2, height=0.62)74 for y, v in enumerate(top.values):75 a2.text(v + 0.6, y, str(v), va="center", fontsize=8, color=palette.ink)76 a2.set_xlabel("Renvois vers le chapitre"); a2.set_xlim(0, top.max() * 1.18)77 a2.set_title("Dix chapitres les plus visés", loc="left", fontsize=9)78 a2.grid(axis="y", visible=False)79 fig.tight_layout(w_pad=2.0)80 savefig(fig, "ch10/ch10-fig-matrice-normes.pdf")8182LIBELLES_COURTS = {1: "Identification physique et légale", 2: "Droits de propriété évalués", 3: "But et fin de l'évaluation",83 4: "Définition de la valeur", 5: "Date d'évaluation", 6: "Étendue du travail", 7: "Réserves et hypothèses",84 8: "Information et raisonnement", 9: "Usage le meilleur et le plus profitable", 10: "Rejet d'une méthode",85 11: "Conformité et écarts aux directives", 12: "Attestation"}8687def fig_grille(out):88 extraits = list(cr.EXTRAITS.keys())89 fig, ax = plt.subplots(figsize=(LARGEUR, 3.4))90 for j, e in enumerate(cr.ELEMENTS):91 for i, nom in enumerate(extraits):92 strict = out["controle"][(nom, "strict")][e.cle][0]93 souple = out["controle"][(nom, "souple")][e.cle][0]94 if strict:95 ax.scatter(i, j, s=120, marker="s", color=palette.cat[0], zorder=3)96 elif souple:97 ax.scatter(i, j, s=120, marker="s", facecolor="white", edgecolor=palette.cat[1], linewidth=1.6, zorder=3)98 else:99 ax.scatter(i, j, s=40, marker="x", color=palette.ink2, linewidth=1.0, zorder=3)100 ax.set_xticks(range(len(extraits)))101 ax.set_xticklabels([f"Extrait {n}\n{out['taux'][(n,'strict')]*12:.0f}/12 (strict)" for n in extraits], fontsize=8)102 ax.set_yticks(range(len(cr.ELEMENTS)))103 ax.set_yticklabels([f"{e.no}. {LIBELLES_COURTS[e.no]}" for e in cr.ELEMENTS], fontsize=7.5)104 ax.invert_yaxis(); ax.set_xlim(-0.6, len(extraits) - 0.4)105 ax.grid(False)106 for s in ("left", "bottom"): ax.spines[s].set_visible(False)107 ax.tick_params(length=0)108 h = [Line2D([0], [0], marker="s", color=palette.cat[0], lw=0, ms=8, label="détecté (lexique strict)"),109 Line2D([0], [0], marker="s", mfc="white", mec=palette.cat[1], mew=1.6, lw=0, ms=8, label="détecté seulement par le lexique souple"),110 Line2D([0], [0], marker="x", color=palette.ink2, lw=0, ms=6, label="non détecté")]111 ax.legend(handles=h, loc="upper center", bbox_to_anchor=(0.5, -0.16), ncol=3, fontsize=7.5, frameon=False)112 savefig(fig, "ch10/ch10-fig-grille-controle.pdf")113114def main():115 setup()116 res = mn.main(verbose=False)117 out = cr.main(verbose=False)118 fig_chronologie(res["chronologie"])119 fig_matrice(res["stats"])120 fig_grille(out)121 print("figures du chapitre 10 écrites dans assets/figures/ch10/")122123if __name__ == "__main__":124 main()125