Zirok05's picture
Update app/simulation/visualization/plots.py
f24a12e verified
Raw
History Blame Contribute Delete
11.6 kB
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
def minutes_to_time(minutes, start_time="00:00"):
"""Преобразует минуты от старта в строку времени ЧЧ:ММ"""
start_hour, start_min = map(int, start_time.split(':'))
total_minutes = start_hour * 60 + start_min + minutes
hour = (total_minutes // 60) % 24
minute = total_minutes % 60
return f"{hour:02d}:{minute:02d}"
def plot_queue_dynamics(queue_history, business_queue_history=None, start_time="00:00"):
"""
Два отдельных графика для очередей с временной шкалой ЧЧ:ММ
"""
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6))
# Создаем метки времени для каждого часа
total_minutes = len(queue_history)
hours = range(0, total_minutes, 60) # каждый час
hour_labels = [minutes_to_time(m, start_time) for m in hours]
# График 1: Очередь моделей
ax1.plot(range(total_minutes), queue_history, 'b-', linewidth=1.5)
ax1.set_xticks(hours)
ax1.set_xticklabels(hour_labels, rotation=45)
ax1.set_xlabel('Время')
ax1.set_ylabel('Размер очереди')
ax1.set_title('Очередь моделей')
ax1.grid(True, alpha=0.3)
# График 2: Очередь бизнес-правил
if business_queue_history and len(business_queue_history) > 0:
ax2.plot(range(total_minutes), business_queue_history, 'orange', linewidth=1.5)
ax2.set_xticks(hours)
ax2.set_xticklabels(hour_labels, rotation=45)
ax2.set_xlabel('Время')
ax2.set_ylabel('Размер очереди')
ax2.set_title('Очередь бизнес-правил')
ax2.grid(True, alpha=0.3)
else:
ax2.text(0.5, 0.5, 'Нет данных', ha='center', va='center', transform=ax2.transAxes)
ax2.set_title('Очередь бизнес-правил')
ax2.set_xlabel('Время')
plt.tight_layout()
return plt
def plot_specialist_load(specialist_busy_history, specialists_count, start_time="00:00"):
"""График загрузки специалистов с временной шкалой ЧЧ:ММ"""
load_percent = [busy / specialists_count * 100 for busy in specialist_busy_history]
fig, ax = plt.subplots(figsize=(10, 4))
total_minutes = len(load_percent)
hours = range(0, total_minutes, 60) # каждый час
hour_labels = [minutes_to_time(m, start_time) for m in hours]
ax.plot(range(total_minutes), load_percent, 'g-', linewidth=1.5)
ax.axhline(y=100, color='r', linestyle='--', alpha=0.5, label='Максимум')
ax.axhline(y=80, color='b', linestyle='--', alpha=0.5, label='Цель 80%')
ax.set_xticks(hours)
ax.set_xticklabels(hour_labels, rotation=45)
ax.set_xlabel('Время')
ax.set_ylabel('Загрузка (%)')
ax.set_title('Загрузка специалистов')
ax.legend()
ax.grid(True, alpha=0.3)
ax.set_ylim(0, 110)
plt.tight_layout()
return plt
def plot_inflow(minute_counts, start_time="00:00"):
"""
График входящего потока заявок с заливкой под кривой
"""
fig, ax = plt.subplots(figsize=(14, 5))
total_minutes = len(minute_counts)
minutes = range(total_minutes)
# Заливка под кривой (area plot)
ax.fill_between(minutes, minute_counts, alpha=0.3, color='blue', label='Общий поток')
# Основной график (линия поверх заливки)
ax.plot(minutes, minute_counts, 'b-', linewidth=1.5, alpha=0.8)
# Скользящее среднее
window = 30
if total_minutes > window:
smoothed = np.convolve(minute_counts, np.ones(window) / window, mode='valid')
ax.plot(range(window - 1, total_minutes), smoothed,
'r-', linewidth=2.5, label=f'Среднее за 30 мин')
# Метки времени
hours = range(0, total_minutes, 60)
hour_labels = [minutes_to_time(m, start_time) for m in hours]
ax.set_xticks(hours)
ax.set_xticklabels(hour_labels, rotation=45)
ax.set_xlabel('Время')
ax.set_ylabel('Количество заявок')
ax.set_title('Входящий поток заявок')
ax.legend()
ax.grid(True, alpha=0.3)
# Горизонтальная линия среднего
mean_value = np.mean(minute_counts)
ax.axhline(y=mean_value, color='gray', linestyle='--', alpha=0.7,
label=f'Среднее: {mean_value:.1f}')
plt.tight_layout()
return plt
def plot_detailed_decisions(batch_stats, second_model_name="XGBoost", start_time="00:00"):
"""
Набор графиков для каждого типа решений отдельно с временной шкалой ЧЧ:ММ
"""
if not batch_stats:
return None
fig, axes = plt.subplots(3, 2, figsize=(14, 10))
times = [stat['time'] for stat in batch_stats] # минуты
total_minutes = max(times) if times else 0
# Метки времени каждый час
hours = range(0, total_minutes + 60, 60)
hour_labels = [minutes_to_time(m, start_time) for m in hours]
# 1. Бизнес-правила (ручной разбор)
axes[0, 0].plot(times, [stat['business_manual'] for stat in batch_stats],
'r-', linewidth=1.5)
axes[0, 0].fill_between(times, 0, [stat['business_manual'] for stat in batch_stats],
alpha=0.2, color='red')
axes[0, 0].set_title('Ручной разбор: бизнес-правила', fontweight='bold')
axes[0, 0].set_xticks(hours)
axes[0, 0].set_xticklabels(hour_labels, rotation=45)
axes[0, 0].set_xlabel('Время')
axes[0, 0].set_ylabel('Заявок')
axes[0, 0].grid(True, alpha=0.3)
# 2. Бизнес-правила (авто отказ)
axes[0, 1].plot(times, [stat['business_auto'] for stat in batch_stats],
'darkred', linewidth=1.5)
axes[0, 1].fill_between(times, 0, [stat['business_auto'] for stat in batch_stats],
alpha=0.2, color='darkred')
axes[0, 1].set_title('Авто отказ: бизнес-правила', fontweight='bold')
axes[0, 1].set_xticks(hours)
axes[0, 1].set_xticklabels(hour_labels, rotation=45)
axes[0, 1].set_xlabel('Время')
axes[0, 1].set_ylabel('Заявок')
axes[0, 1].grid(True, alpha=0.3)
# 3. LR уверенные решения
axes[1, 0].plot(times, [stat['lr_confident'] for stat in batch_stats],
'blue', linewidth=1.5)
axes[1, 0].fill_between(times, 0, [stat['lr_confident'] for stat in batch_stats],
alpha=0.2, color='blue')
axes[1, 0].set_title('Уверенные решения: Logistic Regression', fontweight='bold')
axes[1, 0].set_xticks(hours)
axes[1, 0].set_xticklabels(hour_labels, rotation=45)
axes[1, 0].set_xlabel('Время')
axes[1, 0].set_ylabel('Заявок')
axes[1, 0].grid(True, alpha=0.3)
# 4. Вторая модель уверенные решения
axes[1, 1].plot(times, [stat['second_confident'] for stat in batch_stats],
'green', linewidth=1.5)
axes[1, 1].fill_between(times, 0, [stat['second_confident'] for stat in batch_stats],
alpha=0.2, color='green')
axes[1, 1].set_title(f'Уверенные решения: {second_model_name}', fontweight='bold')
axes[1, 1].set_xticks(hours)
axes[1, 1].set_xticklabels(hour_labels, rotation=45)
axes[1, 1].set_xlabel('Время')
axes[1, 1].set_ylabel('Заявок')
axes[1, 1].grid(True, alpha=0.3)
# 5. Ручной разбор от моделей
axes[2, 0].plot(times, [stat['second_uncertain'] for stat in batch_stats],
'orange', linewidth=1.5)
axes[2, 0].fill_between(times, 0, [stat['second_uncertain'] for stat in batch_stats],
alpha=0.2, color='orange')
axes[2, 0].set_title('Ручной разбор: модели неуверенны', fontweight='bold')
axes[2, 0].set_xticks(hours)
axes[2, 0].set_xticklabels(hour_labels, rotation=45)
axes[2, 0].set_xlabel('Время')
axes[2, 0].set_ylabel('Заявок')
axes[2, 0].grid(True, alpha=0.3)
# 6. Сравнительный график
axes[2, 1].plot(times, [stat['business_manual'] for stat in batch_stats],
'r-', linewidth=1.5, label='Бизнес-правила', alpha=0.7)
axes[2, 1].plot(times, [stat['second_uncertain'] for stat in batch_stats],
'orange', linewidth=1.5, label='Модели неуверенны', alpha=0.7)
axes[2, 1].set_title('Сравнение источников ручного разбора', fontweight='bold')
axes[2, 1].set_xticks(hours)
axes[2, 1].set_xticklabels(hour_labels, rotation=45)
axes[2, 1].set_xlabel('Время')
axes[2, 1].set_ylabel('Заявок')
axes[2, 1].legend()
axes[2, 1].grid(True, alpha=0.3)
plt.suptitle('Детальный анализ решений', fontsize=14, fontweight='bold')
plt.tight_layout()
return plt
def plot_parameters_history(pid_history, second_model_name="XGBoost", start_time="00:00"):
"""График изменения параметров регулятора"""
if pid_history is None or pid_history.empty:
return None
fig, axes = plt.subplots(3, 1, figsize=(12, 12))
total_minutes = len(pid_history)
times = range(total_minutes)
# Метки времени
hours = range(0, total_minutes, 60)
hour_labels = [minutes_to_time(m, start_time) for m in hours]
# 1. Отступы LR
axes[0].plot(times, pid_history['lr_low'], 'g-', linewidth=2, label='LR Low')
axes[0].plot(times, pid_history['lr_high'], 'r-', linewidth=2, label='LR High')
axes[0].set_ylabel('Отступ')
axes[0].set_title('Отступы Logistic Regression')
axes[0].legend()
axes[0].grid(True, alpha=0.3)
axes[0].set_xticks(hours)
axes[0].set_xticklabels(hour_labels, rotation=45)
# 2. Отступы второй модели (с именем из параметра)
axes[1].plot(times, pid_history['second_low'], 'g-', linewidth=2, label=f'{second_model_name} Low')
axes[1].plot(times, pid_history['second_high'], 'r-', linewidth=2, label=f'{second_model_name} High')
axes[1].set_ylabel('Отступ')
axes[1].set_title(f'Отступы {second_model_name}')
axes[1].legend()
axes[1].grid(True, alpha=0.3)
axes[1].set_xticks(hours)
axes[1].set_xticklabels(hour_labels, rotation=45)
# 3. Ошибка загрузки и выход регулятора
axes[2].plot(times, pid_history['error_load'], 'b-', label='Error load', alpha=0.7, linewidth=1.5)
axes[2].plot(times, pid_history['output'], 'r-', label='Output', linewidth=2, alpha=0.7)
axes[2].axhline(y=0, color='black', linestyle='-', linewidth=0.5)
axes[2].set_xlabel('Время')
axes[2].set_ylabel('Значение')
axes[2].set_title('Ошибка загрузки и выход регулятора')
axes[2].legend()
axes[2].grid(True, alpha=0.3)
axes[2].set_xticks(hours)
axes[2].set_xticklabels(hour_labels, rotation=45)
plt.tight_layout()
return plt