#!/usr/bin/env python3 """Ch. 30, figure « tornade » : variation de la valeur DCF du cas 3 (DONNÉES SYNTHÉTIQUES) pour un choc bas/haut de chaque intrant, les autres étant fixés à leur valeur de base. Sortie : ch30-fig-tornade.pdf.""" from __future__ import annotations import sys, pathlib sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1])) import numpy as np, matplotlib.pyplot as plt from matplotlib.ticker import FuncFormatter from traite.style import setup, palette, savefig, LARGEUR, fmt_milliers from cases import ch30_dcf as c def main(): setup() r = c.base(); t = c.tornade(r); V0 = r["dcf"]["valeur"] t = t.iloc[::-1].reset_index(drop=True) # plus grande amplitude en haut fig, ax = plt.subplots(figsize=(LARGEUR, 3.6)) y = np.arange(len(t)) ax.barh(y, t["delta_bas"] / 1000, color=palette.cat[0], label="Borne basse de l'intrant") ax.barh(y, t["delta_haut"] / 1000, color=palette.cat[1], label="Borne haute de l'intrant") ax.axvline(0, color=palette.ink, lw=0.9) ax.set_yticks(y); ax.set_yticklabels(t["intrant"], fontsize=7.8) for yi, (db, dh) in enumerate(zip(t["delta_bas"], t["delta_haut"])): for dv in (db, dh): ax.text(dv / 1000 + (6 if dv >= 0 else -6), yi, f"{dv/1000:+,.0f}".replace(",", " "), va="center", ha="left" if dv >= 0 else "right", fontsize=7, color=palette.ink2) ax.set_xlabel(f"Variation de la valeur par rapport à la base ({fmt_milliers(V0)} \\$), en milliers de \\$") lim = max(abs(t["delta_bas"].min()), abs(t["delta_haut"].max())) / 1000 * 1.25 ax.set_xlim(-lim, lim) ax.xaxis.set_major_formatter(FuncFormatter(lambda v, q: f"{v:+,.0f}".replace(",", " "))) ax.legend(loc="lower right") savefig(fig, "ch30/ch30-fig-tornade.pdf") if __name__ == "__main__": main()