#!/usr/bin/env python3 # erzeuge_zielgroessen_radar.py """ Erzeugt das Netzdiagramm zum Kapitel "Vom Management-Wunsch zum Modell": bilder_04/kap_modellierung_zielgroessen.svg Drei Saetze aus derselben Besprechung - "Umsatz maximieren", "Maschinen auslasten", "Deckungsbeitrag maximieren" - ergeben drei verschiedene Plaene aus denselben 14 Anfragen. Das Netzdiagramm legt sie uebereinander. Der Punkt, auf den es ankommt: Der Auslastungsplan sieht auf seiner eigenen Achse perfekt aus und faellt auf der Deckungsbeitragsachse durch. KEINE ZAHL IST HIER EINGETRAGEN. Das Skript importiert Vom_Wunsch_zum_Modell.py und loest dieselben Modelle mit demselben Solver. Weicht das Buchprogramm spaeter ab, aendert sich das Bild mit - nicht der Text neben einem alten Bild. Aufruf (aus dem Repository-Wurzelverzeichnis): python3 bilder_04/erzeuge_zielgroessen_radar.py Benoetigt: numpy, scipy, matplotlib """ from __future__ import annotations import os import sys import numpy as np HIER = os.path.dirname(os.path.abspath(__file__)) BASIS = os.path.dirname(HIER) sys.path.insert(0, HIER) # Das Buchprogramm ist die Quelle der Instanz - importiert, nicht kopiert. sys.path.insert(0, os.path.join( BASIS, "Operations_Research_mit_Python_Version_04_Programme")) from stil_04 import FARBEN, speichere # noqa: E402 import matplotlib.pyplot as plt # noqa: E402 from Vom_Wunsch_zum_Modell import (AUFTRAEGE, DECKUNGSBEITRAG, # noqa: E402 STUNDEN, UMSATZ, kennzahlen, waehle_aus) # Die fuenf Achsen des Netzes und wie sie aus kennzahlen() entstehen. Jede # Achse wird auf ihr eigenes Maximum ueber die drei Plaene skaliert - sonst # erschluege der Umsatz in Euro alle uebrigen Groessen. ACHSEN = [ ("Deckungsbeitrag", lambda k: k["db"]), ("Umsatz", lambda k: k["umsatz"]), ("Maschinen-\nauslastung", lambda k: k["auslastung"]), ("Stammkunden\nbedient", lambda k: len({a[1] for a in AUFTRAEGE if a[2]}) - k["kunden_leer"]), ("angenommene\nAufträge", lambda k: k["angenommen"]), ] PLAENE = [ ("„Umsatz maximieren“", UMSATZ, FARBEN["zweit"]), ("„Maschinen auslasten“", STUNDEN, FARBEN["warnung"]), ("„Deckungsbeitrag maximieren“", DECKUNGSBEITRAG, FARBEN["gut"]), ] def loese_alle() -> list[tuple[str, dict, str]]: ergebnis = [] for name, ziel, farbe in PLAENE: plan = waehle_aus(ziel) if plan is None: raise SystemExit(f"Radar: Fuer {name} liefert das Modell keinen Plan.") ergebnis.append((name, kennzahlen(plan), farbe)) return ergebnis def zeichne(plaene) -> None: werte = {name: [f(k) for _, f in ACHSEN] for name, k, _ in plaene} maxima = [max(werte[name][i] for name in werte) or 1.0 for i in range(len(ACHSEN))] winkel = np.linspace(0, 2 * np.pi, len(ACHSEN), endpoint=False) geschlossen = np.concatenate([winkel, winkel[:1]]) figur, achse = plt.subplots(figsize=(6.6, 5.8), subplot_kw={"projection": "polar"}) for name, _k, farbe in plaene: anteile = [w / m * 100 for w, m in zip(werte[name], maxima)] anteile += anteile[:1] achse.plot(geschlossen, anteile, "-", color=farbe, linewidth=2.0, label=name, zorder=3) achse.fill(geschlossen, anteile, color=farbe, alpha=0.12, zorder=2) achse.set_xticks(winkel) achse.set_xticklabels([bez for bez, _ in ACHSEN], fontsize=9, color=FARBEN["text"]) achse.set_yticks([25, 50, 75, 100]) achse.set_yticklabels(["25 %", "50 %", "75 %", "100 %"], fontsize=8, color=FARBEN["gedaempft"]) achse.set_ylim(0, 108) achse.grid(color=FARBEN["linie"], linewidth=0.7, alpha=0.6) achse.spines["polar"].set_color(FARBEN["linie"]) achse.set_title("Drei Sätze aus derselben Besprechung, drei Pläne\n" "je Achse auf den besten der drei Pläne bezogen", fontsize=10.5, color=FARBEN["text"], pad=24) achse.legend(fontsize=9, loc="lower center", bbox_to_anchor=(0.5, -0.20), frameon=False) speichere(figur, "kap_modellierung_zielgroessen") if __name__ == "__main__": plaene = loese_alle() for name, k, _ in plaene: print(f" {name:<32} Umsatz {k['umsatz']:>8,.0f} " f"DB {k['db']:>8,.0f} Auslastung {k['auslastung']:>3.0f} % " f"angenommen {k['angenommen']:>2}") zeichne(plaene)