115 lines
4.6 KiB
Python
115 lines
4.6 KiB
Python
|
|
#!/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)
|