praca_magisterska/pytania/generate_q31_diagrams.py

621 lines
25 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
Generate diagrams for PYTANIE 31: Interaktywne wspomaganie decyzji w warunkach ryzyka.
Diagrams:
1. Payoff matrix + all criteria results comparison (bar chart)
2. Regret matrix construction step-by-step
3. Hurwicz α interpolation between maximax and maximin
4. Decision criteria mnemonic map
5. Expected value criterion with probability-weighted bars
6. Decision conditions spectrum (pewność → ryzyko → niepewność)
All: A4-compatible, B&W, 300 DPI, laser-printer-friendly.
"""
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.patches import FancyBboxPatch
import numpy as np
import os
DPI = 300
BG = 'white'
LN = 'black'
FS = 8
FS_TITLE = 11
OUTPUT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'img')
os.makedirs(OUTPUT_DIR, exist_ok=True)
GRAY1 = '#E8E8E8'
GRAY2 = '#D0D0D0'
GRAY3 = '#B8B8B8'
GRAY4 = '#F5F5F5'
GRAY5 = '#C0C0C0'
def draw_box(ax, x, y, w, h, text, fill='white', lw=1.2, fontsize=FS,
fontweight='normal', ha='center', va='center', rounded=True):
if rounded:
rect = FancyBboxPatch((x, y), w, h, boxstyle="round,pad=0.05",
lw=lw, edgecolor=LN, facecolor=fill)
else:
rect = mpatches.Rectangle((x, y), w, h, lw=lw, edgecolor=LN, facecolor=fill)
ax.add_patch(rect)
ax.text(x + w/2, y + h/2, text, ha=ha, va=va, fontsize=fontsize,
fontweight=fontweight, wrap=True)
def draw_arrow(ax, x1, y1, x2, y2, lw=1.2, style='->', color=LN):
ax.annotate("", xy=(x2, y2), xytext=(x1, y1),
arrowprops=dict(arrowstyle=style, color=color, lw=lw))
# ============================================================
# 1. PAYOFF MATRIX + ALL CRITERIA BAR CHART
# ============================================================
def draw_criteria_comparison():
fig, axes = plt.subplots(1, 2, figsize=(8.27, 4.5), gridspec_kw={'width_ratios': [1.2, 1]})
# -- Left: Payoff matrix as styled table --
ax = axes[0]
ax.axis('off')
ax.set_xlim(0, 6)
ax.set_ylim(0, 6)
ax.set_title('Macierz wypłat (tys. zł)', fontsize=FS_TITLE, fontweight='bold', pad=8)
# Headers
headers_col = ['', 'S₁\n(dobra)', 'S₂\n(średnia)', 'S₃\n(zła)']
rows = [
['A₁ (fabryka)', '200', '50', '100'],
['A₂ (sklep)', '80', '70', '40'],
['A₃ (obligacje)', '30', '30', '30'],
]
col_w = [1.8, 1.2, 1.2, 1.2]
row_h = 0.7
start_y = 4.5
start_x = 0.2
# Draw header row
x = start_x
for j, h in enumerate(headers_col):
fill = GRAY2 if j > 0 else GRAY3
rect = mpatches.Rectangle((x, start_y), col_w[j], row_h, lw=1, edgecolor=LN, facecolor=fill)
ax.add_patch(rect)
ax.text(x + col_w[j]/2, start_y + row_h/2, h, ha='center', va='center',
fontsize=FS, fontweight='bold')
x += col_w[j]
# Draw data rows
for i, row in enumerate(rows):
x = start_x
y = start_y - (i+1) * row_h
for j, val in enumerate(row):
fill = GRAY4 if j == 0 else ('white' if i % 2 == 0 else GRAY1)
# Highlight negative
if val.startswith(''):
fill = '#D8D8D8'
rect = mpatches.Rectangle((x, y), col_w[j], row_h, lw=1, edgecolor=LN, facecolor=fill)
ax.add_patch(rect)
fw = 'bold' if j == 0 else 'normal'
ax.text(x + col_w[j]/2, y + row_h/2, val, ha='center', va='center',
fontsize=FS, fontweight=fw)
x += col_w[j]
# Probability row for EV
x = start_x
y = start_y - 4 * row_h
probs = ['p (dla E[X]):', '0.5', '0.3', '0.2']
for j, val in enumerate(probs):
fill = GRAY5 if j > 0 else GRAY3
rect = mpatches.Rectangle((x, y), col_w[j], row_h * 0.7, lw=1, edgecolor=LN, facecolor=fill)
ax.add_patch(rect)
ax.text(x + col_w[j]/2, y + row_h*0.35, val, ha='center', va='center',
fontsize=7, fontweight='bold', style='italic')
x += col_w[j]
# -- Right: Bar chart comparing criteria results --
ax2 = axes[1]
criteria = ['E[X]', 'Laplace', 'Maximax', 'Maximin', 'Hurwicz\nα=0.6', 'Savage']
# Recalculate with probabilities 0.5, 0.3, 0.2
# E[X]: A1=200*0.5+50*0.3+(-100)*0.2=100+15-20=95
# A2=80*0.5+70*0.3+40*0.2=40+21+8=69
# A3=30*0.5+30*0.3+30*0.2=15+9+6=30
ev = [95, 69, 30]
laplace = [50, 63.3, 30]
maximax = [200, 80, 30]
maximin = [-100, 40, 30]
hurwicz = [80, 64, 30] # α=0.6
savage_maxregret = [140, 120, 170] # lower = better
# Which alternative wins for each criterion?
winners = [0, 1, 0, 1, 0, 1] # index of winning alternative
winner_vals = [95, 63.3, 200, 40, 80, 120]
# Display as grouped bar chart - each criterion shows the 3 alternatives
x_pos = np.arange(len(criteria))
width = 0.22
hatches = ['///', '...', 'xxx']
labels = ['A₁ (fabryka)', 'A₂ (sklep)', 'A₃ (obligacje)']
all_vals = [
[ev[0], laplace[0], maximax[0], maximin[0], hurwicz[0], savage_maxregret[0]],
[ev[1], laplace[1], maximax[1], maximin[1], hurwicz[1], savage_maxregret[1]],
[ev[2], laplace[2], maximax[2], maximin[2], hurwicz[2], savage_maxregret[2]],
]
for i in range(3):
bars = ax2.bar(x_pos + (i - 1) * width, all_vals[i], width,
label=labels[i], color='white', edgecolor=LN, hatch=hatches[i], lw=0.8)
# Mark winners with star
for c_idx in range(len(criteria)):
w = winners[c_idx]
val = all_vals[w][c_idx]
ax2.text(x_pos[c_idx] + (w - 1) * width, val + 5, '',
ha='center', va='bottom', fontsize=10, fontweight='bold')
ax2.set_xticks(x_pos)
ax2.set_xticklabels(criteria, fontsize=7)
ax2.set_ylabel('Wartość kryterium', fontsize=8)
ax2.set_title('Porównanie kryteriów', fontsize=FS_TITLE, fontweight='bold', pad=8)
ax2.legend(fontsize=7, loc='upper right')
ax2.axhline(y=0, color=LN, lw=0.5, ls='-')
ax2.spines['top'].set_visible(False)
ax2.spines['right'].set_visible(False)
ax2.tick_params(labelsize=7)
# Note about Savage
ax2.text(5, -30, '(Savage: niżej\n= lepiej)', fontsize=6, ha='center',
va='top', style='italic')
plt.tight_layout()
outpath = os.path.join(OUTPUT_DIR, 'q31_criteria_comparison.png')
fig.savefig(outpath, dpi=DPI, bbox_inches='tight', facecolor=BG)
plt.close(fig)
print(f" Saved: {outpath}")
# ============================================================
# 2. REGRET MATRIX CONSTRUCTION
# ============================================================
def draw_regret_matrix():
fig, ax = plt.subplots(1, 1, figsize=(8.27, 5))
ax.axis('off')
ax.set_xlim(0, 10)
ax.set_ylim(0, 7)
ax.set_title('Kryterium Savage\'a — budowa macierzy żalu',
fontsize=FS_TITLE + 1, fontweight='bold', pad=10)
# --- Step 1: Original payoff matrix (left) ---
ax.text(2.2, 6.3, 'Krok 1: Macierz wypłat', fontsize=9, fontweight='bold',
ha='center', va='center')
col_w = 1.0
row_h = 0.55
headers = ['', 'S₁', 'S₂', 'S₃']
data = [
['A₁', '200', '50', '100'],
['A₂', '80', '70', '40'],
['A₃', '30', '30', '30'],
]
start_x = 0.3
start_y = 5.5
for j, h in enumerate(headers):
w = 0.7 if j == 0 else col_w
x = start_x + (0 if j == 0 else 0.7 + (j-1)*col_w)
rect = mpatches.Rectangle((x, start_y), w, row_h, lw=1, edgecolor=LN, facecolor=GRAY2)
ax.add_patch(rect)
ax.text(x + w/2, start_y + row_h/2, h, ha='center', va='center',
fontsize=FS, fontweight='bold')
for i, row in enumerate(data):
y = start_y - (i+1) * row_h
for j, val in enumerate(row):
w = 0.7 if j == 0 else col_w
x = start_x + (0 if j == 0 else 0.7 + (j-1)*col_w)
fill = GRAY4 if j == 0 else 'white'
rect = mpatches.Rectangle((x, y), w, row_h, lw=1, edgecolor=LN, facecolor=fill)
ax.add_patch(rect)
ax.text(x + w/2, y + row_h/2, val, ha='center', va='center', fontsize=FS)
# Max per column annotation
max_y = start_y - 3 * row_h - 0.1
ax.text(start_x + 0.7 + 0.5*col_w, max_y, 'max=200', fontsize=7,
ha='center', va='top', fontweight='bold', color='#333')
ax.text(start_x + 0.7 + 1.5*col_w, max_y, 'max=70', fontsize=7,
ha='center', va='top', fontweight='bold', color='#333')
ax.text(start_x + 0.7 + 2.5*col_w, max_y, 'max=40', fontsize=7,
ha='center', va='top', fontweight='bold', color='#333')
# Arrow
ax.annotate("", xy=(5.0, 4.8), xytext=(4.2, 4.8),
arrowprops=dict(arrowstyle='->', color=LN, lw=2))
ax.text(4.6, 5.0, 'rᵢⱼ = max aᵢⱼ', fontsize=8, ha='center', va='bottom',
fontweight='bold')
# --- Step 2: Regret matrix (right) ---
ax.text(7.5, 6.3, 'Krok 2: Macierz żalu', fontsize=9, fontweight='bold',
ha='center', va='center')
regret_data = [
['A₁', '0', '20', '140'],
['A₂', '120', '0', '0'],
['A₃', '170', '40', '10'],
]
headers2 = ['', 'S₁', 'S₂', 'S₃', 'max rᵢ']
start_x2 = 5.3
for j, h in enumerate(headers2):
w = 0.7 if j == 0 else (0.9 if j < 4 else 1.0)
x = start_x2
if j == 0:
x = start_x2
elif j <= 3:
x = start_x2 + 0.7 + (j-1)*0.9
else:
x = start_x2 + 0.7 + 3*0.9
rect = mpatches.Rectangle((x, start_y), w, row_h, lw=1, edgecolor=LN,
facecolor=GRAY2 if j < 4 else GRAY3)
ax.add_patch(rect)
ax.text(x + w/2, start_y + row_h/2, h, ha='center', va='center',
fontsize=FS, fontweight='bold')
max_regrets = [140, 120, 170]
for i, row in enumerate(regret_data):
y = start_y - (i+1) * row_h
for j, val in enumerate(row):
w = 0.7 if j == 0 else 0.9
x = start_x2 + (0 if j == 0 else 0.7 + (j-1)*0.9)
fill = GRAY4 if j == 0 else 'white'
# Highlight the max regret cell
if j > 0 and int(val) == max_regrets[i]:
fill = GRAY2
rect = mpatches.Rectangle((x, y), w, row_h, lw=1, edgecolor=LN, facecolor=fill)
ax.add_patch(rect)
fw = 'bold' if (j > 0 and int(val) == max_regrets[i]) else 'normal'
ax.text(x + w/2, y + row_h/2, val, ha='center', va='center',
fontsize=FS, fontweight=fw)
# Max regret column
x = start_x2 + 0.7 + 3*0.9
w = 1.0
fill = '#C8C8C8' if max_regrets[i] == min(max_regrets) else GRAY1
rect = mpatches.Rectangle((x, y), w, row_h, lw=1.5 if max_regrets[i]==min(max_regrets) else 1,
edgecolor=LN, facecolor=fill)
ax.add_patch(rect)
marker = '' if max_regrets[i] == min(max_regrets) else ''
ax.text(x + w/2, y + row_h/2, f'{max_regrets[i]}{marker}', ha='center', va='center',
fontsize=FS, fontweight='bold')
# Bottom conclusion
ax.text(5.0, 2.8, 'Krok 3: Wybierz min z max żalu → A₂ (max żal = 120)',
fontsize=10, ha='center', va='center', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor=GRAY1, edgecolor=LN, lw=1.5))
# Interpretatation examples
ax.text(5.0, 2.0, 'Interpretacja żalu: r₁₃ = 140 oznacza:\n'
'„Gdyby nastąpił S₃ (zła koniunktura), a wybrałbym A₁,\n'
'żałowałbym, bo najlepszą opcją byłoby A₂ z wynikiem 40 — traciłbym 140"',
fontsize=7.5, ha='center', va='center', style='italic',
bbox=dict(boxstyle='round,pad=0.3', facecolor=GRAY4, edgecolor=GRAY3, lw=0.8))
# Mnemonic
ax.text(5.0, 0.8, 'Mnemonik: Savage = „Żal jak nóż"\nMaksymalny żal to nóż '
'— wybierz opcję z NAJMNIEJSZYM nożem',
fontsize=8, ha='center', va='center', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor='white', edgecolor=LN, lw=1))
plt.tight_layout()
outpath = os.path.join(OUTPUT_DIR, 'q31_regret_matrix.png')
fig.savefig(outpath, dpi=DPI, bbox_inches='tight', facecolor=BG)
plt.close(fig)
print(f" Saved: {outpath}")
# ============================================================
# 3. HURWICZ α INTERPOLATION
# ============================================================
def draw_hurwicz_interpolation():
fig, ax = plt.subplots(1, 1, figsize=(8.27, 4))
ax.set_title('Kryterium Hurwicza — wpływ α na wybór alternatywy',
fontsize=FS_TITLE + 1, fontweight='bold', pad=10)
alphas = np.linspace(0, 1, 200)
# V(Ai) = α * max_i + (1-α) * min_i
# A1: max=200, min=-100
# A2: max=80, min=40
# A3: max=30, min=30
v1 = alphas * 200 + (1 - alphas) * (-100)
v2 = alphas * 80 + (1 - alphas) * 40
v3 = alphas * 30 + (1 - alphas) * 30
ax.plot(alphas, v1, 'k-', lw=2, label='A₁ (fabryka): V = 300α 100')
ax.plot(alphas, v2, 'k--', lw=2, label='A₂ (sklep): V = 40α + 40')
ax.plot(alphas, v3, 'k:', lw=2, label='A₃ (obligacje): V = 30')
# Find crossover points
# A2 = A1: 40α + 40 = 300α - 100 → 140 = 260αα = 140/260 ≈ 0.538
alpha_cross_12 = 140 / 260
v_cross_12 = 40 * alpha_cross_12 + 40
# A2 = A3: 40α + 40 = 30 → 40α = -10 → α = -0.25 (never — A2 always > A3)
# A1 = A3: 300α - 100 = 30 → 300α = 130 → α = 130/300 ≈ 0.433
alpha_cross_13 = 130 / 300
v_cross_13 = 30
ax.plot(alpha_cross_12, v_cross_12, 'ko', markersize=8, zorder=5)
ax.annotate(f'α{alpha_cross_12:.2f}\nA₁ = A₂', xy=(alpha_cross_12, v_cross_12),
xytext=(alpha_cross_12 + 0.12, v_cross_12 - 30),
fontsize=8, fontweight='bold',
arrowprops=dict(arrowstyle='->', color=LN, lw=1))
# Shade winning regions
ax.axvspan(0, alpha_cross_12, alpha=0.08, color='black', label='_')
ax.axvspan(alpha_cross_12, 1, alpha=0.15, color='black', label='_')
ax.text(alpha_cross_12 / 2, -60, 'A₂ wygrywa\n(pesymistycznie)',
fontsize=8, ha='center', va='center',
bbox=dict(boxstyle='round', facecolor='white', edgecolor=LN))
ax.text((alpha_cross_12 + 1) / 2, 160, 'A₁ wygrywa\n(optymistycznie)',
fontsize=8, ha='center', va='center',
bbox=dict(boxstyle='round', facecolor='white', edgecolor=LN))
# Special α values
ax.axvline(x=0, color=LN, lw=0.5, ls=':')
ax.axvline(x=1, color=LN, lw=0.5, ls=':')
ax.text(0, -115, 'α=0\nmaximin', fontsize=7, ha='center', va='top', fontweight='bold')
ax.text(1, -115, 'α=1\nmaximax', fontsize=7, ha='center', va='top', fontweight='bold')
ax.set_xlabel('Współczynnik optymizmu α', fontsize=9)
ax.set_ylabel('V(Aᵢ) = α·max + (1α)·min', fontsize=9)
ax.legend(fontsize=8, loc='upper left')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.set_xlim(-0.05, 1.05)
ax.axhline(y=0, color=LN, lw=0.3, ls='-')
ax.tick_params(labelsize=8)
plt.tight_layout()
outpath = os.path.join(OUTPUT_DIR, 'q31_hurwicz_alpha.png')
fig.savefig(outpath, dpi=DPI, bbox_inches='tight', facecolor=BG)
plt.close(fig)
print(f" Saved: {outpath}")
# ============================================================
# 4. DECISION CRITERIA MNEMONIC MAP
# ============================================================
def draw_criteria_mnemonic():
fig, ax = plt.subplots(1, 1, figsize=(8.27, 6))
ax.set_xlim(0, 10)
ax.set_ylim(0, 8)
ax.set_aspect('equal')
ax.axis('off')
ax.set_title('Mapa mnemoniczna — 6 kryteriów decyzyjnych',
fontsize=FS_TITLE + 2, fontweight='bold', pad=10)
# Central node
draw_box(ax, 3.5, 3.5, 3, 1, 'MACIERZ\nWYPŁAT', fill=GRAY2, lw=2,
fontsize=11, fontweight='bold')
# Criteria boxes around the center
criteria = [
# (x, y, w, h, title, mnemonic, formula)
(0, 6.5, 3, 1.2, 'WARTOŚĆ OCZEKIWANA', '„Mam prawdopodobieństwa"', 'E[Aᵢ] = Σ pⱼ·aᵢⱼ'),
(3.5, 6.5, 3, 1.2, 'LAPLACE', '„Wszystko po równo"', 'V = Σaᵢⱼ / n'),
(7, 6.5, 3, 1.2, 'MAXIMAX', '„Optymista: max z max"', 'max maxⱼ aᵢⱼ'),
(0, 0.5, 3, 1.2, 'MAXIMIN (Wald)', '„Pesymista: max z min"', 'max minⱼ aᵢⱼ'),
(3.5, 0.5, 3, 1.2, 'HURWICZ', '„α pomiędzy"', 'α·max + (1α)·min'),
(7, 0.5, 3, 1.2, 'SAVAGE', '„Min max żalu"', 'min maxⱼ rᵢⱼ'),
]
fills = [GRAY3, GRAY1, 'white', 'white', GRAY1, GRAY3]
for i, (x, y, w, h, title, mnem, formula) in enumerate(criteria):
rect = FancyBboxPatch((x, y), w, h, boxstyle="round,pad=0.08",
lw=1.5, edgecolor=LN, facecolor=fills[i])
ax.add_patch(rect)
ax.text(x + w/2, y + h*0.78, title, ha='center', va='center',
fontsize=8, fontweight='bold')
ax.text(x + w/2, y + h*0.45, mnem, ha='center', va='center',
fontsize=7, style='italic')
ax.text(x + w/2, y + h*0.15, formula, ha='center', va='center',
fontsize=7, fontweight='bold', family='monospace')
# Arrows from center to each box
cx, cy = 5, 4 # center of macierz
bx, by = x + w/2, y + h/2
if by > cy:
ax.annotate("", xy=(bx, y), xytext=(cx, 4.5),
arrowprops=dict(arrowstyle='->', color=LN, lw=1,
connectionstyle='arc3,rad=0'))
else:
ax.annotate("", xy=(bx, y + h), xytext=(cx, 3.5),
arrowprops=dict(arrowstyle='->', color=LN, lw=1,
connectionstyle='arc3,rad=0'))
# Labels on arrows
ax.text(1.2, 5.6, 'znane p', fontsize=7, ha='center', va='center',
bbox=dict(boxstyle='round,pad=0.15', facecolor='white', edgecolor=GRAY3, lw=0.5))
ax.text(5, 5.6, 'p = 1/n', fontsize=7, ha='center', va='center',
bbox=dict(boxstyle='round,pad=0.15', facecolor='white', edgecolor=GRAY3, lw=0.5))
ax.text(8.7, 5.6, 'max ↑', fontsize=7, ha='center', va='center',
bbox=dict(boxstyle='round,pad=0.15', facecolor='white', edgecolor=GRAY3, lw=0.5))
ax.text(1.2, 2.5, 'min ↑', fontsize=7, ha='center', va='center',
bbox=dict(boxstyle='round,pad=0.15', facecolor='white', edgecolor=GRAY3, lw=0.5))
ax.text(5, 2.5, 'podaj α', fontsize=7, ha='center', va='center',
bbox=dict(boxstyle='round,pad=0.15', facecolor='white', edgecolor=GRAY3, lw=0.5))
ax.text(8.7, 2.5, 'macierz\nżalu', fontsize=7, ha='center', va='center',
bbox=dict(boxstyle='round,pad=0.15', facecolor='white', edgecolor=GRAY3, lw=0.5))
plt.tight_layout()
outpath = os.path.join(OUTPUT_DIR, 'q31_criteria_mnemonic.png')
fig.savefig(outpath, dpi=DPI, bbox_inches='tight', facecolor=BG)
plt.close(fig)
print(f" Saved: {outpath}")
# ============================================================
# 5. EXPECTED VALUE CRITERION WITH PROBABILITY BARS
# ============================================================
def draw_expected_value():
fig, axes = plt.subplots(1, 3, figsize=(8.27, 3.5), sharey=True)
fig.suptitle('Kryterium wartości oczekiwanej E[X] — rozkład wyników per alternatywa',
fontsize=FS_TITLE, fontweight='bold', y=1.02)
# Probabilities: p1=0.5, p2=0.3, p3=0.2
probs = [0.5, 0.3, 0.2]
states = ['S₁\n(dobra)\np=0.5', 'S₂\n(średnia)\np=0.3', 'S₃\n(zła)\np=0.2']
alts = [
('A₁ (fabryka)', [200, 50, -100], 95),
('A₂ (sklep)', [80, 70, 40], 69),
('A₃ (obligacje)', [30, 30, 30], 30),
]
hatches = ['///', '...', 'xxx']
for idx, (ax, (name, vals, ev)) in enumerate(zip(axes, alts)):
# Bar: height = payoff, width proportional to probability
x_positions = [0, 0.6, 1.0]
widths = [p * 0.9 for p in probs]
for i, (v, p, h) in enumerate(zip(vals, probs, hatches)):
color = 'white' if v >= 0 else GRAY2
bar = ax.bar(x_positions[i], v, width=widths[i], color=color,
edgecolor=LN, hatch=h, lw=0.8, align='edge')
# Value label
offset = 8 if v >= 0 else -12
ax.text(x_positions[i] + widths[i]/2, v + offset, f'{v}',
ha='center', va='center', fontsize=8, fontweight='bold')
# Probability contribution
contrib = v * p
ax.text(x_positions[i] + widths[i]/2, v/2,
f'{v}×{p}\n={contrib:.0f}', ha='center', va='center',
fontsize=6, style='italic')
# Expected value line
ax.axhline(y=ev, color=LN, lw=2, ls='--')
ax.text(1.35, ev, f'E[X]={ev}', fontsize=8, fontweight='bold',
va='center', ha='left',
bbox=dict(boxstyle='round,pad=0.15', facecolor=GRAY1, edgecolor=LN))
ax.set_title(name, fontsize=9, fontweight='bold')
ax.set_xticks([0.225, 0.735, 1.09])
ax.set_xticklabels(['S₁', 'S₂', 'S₃'], fontsize=7)
ax.axhline(y=0, color=LN, lw=0.5)
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.tick_params(labelsize=7)
# Star on winner
if ev == 95:
ax.text(0.7, ev + 20, '★ MAX', fontsize=9, fontweight='bold',
ha='center', va='bottom')
axes[0].set_ylabel('Wypłata (tys. zł)', fontsize=8)
plt.tight_layout()
outpath = os.path.join(OUTPUT_DIR, 'q31_expected_value.png')
fig.savefig(outpath, dpi=DPI, bbox_inches='tight', facecolor=BG)
plt.close(fig)
print(f" Saved: {outpath}")
# ============================================================
# 6. DECISION CONDITIONS SPECTRUM
# ============================================================
def draw_conditions_spectrum():
fig, ax = plt.subplots(1, 1, figsize=(8.27, 3.5))
ax.set_xlim(0, 10)
ax.set_ylim(0, 5)
ax.set_aspect('equal')
ax.axis('off')
ax.set_title('Warunki decyzyjne — spektrum wiedzy decydenta',
fontsize=FS_TITLE + 1, fontweight='bold', pad=10)
# Three zones
zones = [
(0.3, 1.5, 2.8, 2.5, 'PEWNOŚĆ', 'white', [
'Znamy dokładny wynik',
'Przykład: lokata 5%',
'Metoda: po prostu wybierz',
'najlepszy wynik'
]),
(3.5, 1.5, 2.8, 2.5, 'RYZYKO', GRAY1, [
'Znamy wyniki I prawdop.',
'Przykład: gra w kości',
'Metoda: wartość',
'oczekiwana E[X]'
]),
(6.7, 1.5, 2.8, 2.5, 'NIEPEWNOŚĆ', GRAY3, [
'Znamy wyniki, ale',
'NIE znamy prawdop.',
'Metody: Laplace, maximax,',
'maximin, Hurwicz, Savage'
]),
]
for x, y, w, h, title, fill, lines in zones:
rect = FancyBboxPatch((x, y), w, h, boxstyle="round,pad=0.1",
lw=2, edgecolor=LN, facecolor=fill)
ax.add_patch(rect)
ax.text(x + w/2, y + h - 0.3, title, ha='center', va='center',
fontsize=11, fontweight='bold')
for i, line in enumerate(lines):
ax.text(x + w/2, y + h - 0.7 - i*0.4, line, ha='center', va='center',
fontsize=7)
# Arrows between zones
ax.annotate("", xy=(3.4, 2.75), xytext=(3.15, 2.75),
arrowprops=dict(arrowstyle='->', color=LN, lw=2))
ax.annotate("", xy=(6.6, 2.75), xytext=(6.35, 2.75),
arrowprops=dict(arrowstyle='->', color=LN, lw=2))
# Bottom: knowledge gradient bar
gradient_y = 0.5
gradient_h = 0.5
n_steps = 50
for i in range(n_steps):
x = 0.3 + i * (9.2 / n_steps)
w = 9.2 / n_steps + 0.01
gray_val = 1 - (i / n_steps) * 0.7
rect = mpatches.Rectangle((x, gradient_y), w, gradient_h, lw=0,
facecolor=str(gray_val))
ax.add_patch(rect)
rect = mpatches.Rectangle((0.3, gradient_y), 9.2, gradient_h, lw=1.5,
edgecolor=LN, facecolor='none')
ax.add_patch(rect)
ax.text(0.3, gradient_y - 0.15, 'Dużo wiedzy', fontsize=7, ha='left', va='top')
ax.text(9.5, gradient_y - 0.15, 'Mało wiedzy', fontsize=7, ha='right', va='top')
ax.text(4.95, gradient_y + gradient_h / 2, 'POZIOM WIEDZY DECYDENTA',
fontsize=8, fontweight='bold', ha='center', va='center', color='white')
plt.tight_layout()
outpath = os.path.join(OUTPUT_DIR, 'q31_conditions_spectrum.png')
fig.savefig(outpath, dpi=DPI, bbox_inches='tight', facecolor=BG)
plt.close(fig)
print(f" Saved: {outpath}")
# ============================================================
# MAIN
# ============================================================
if __name__ == '__main__':
print("Generating PYTANIE 31 diagrams...")
draw_criteria_comparison()
draw_regret_matrix()
draw_hurwicz_interpolation()
draw_criteria_mnemonic()
draw_expected_value()
draw_conditions_spectrum()
print("Done! All Q31 diagrams saved to:", OUTPUT_DIR)