/
CW3
/
The_Project
Обзор
Документация
Войти
/
CW3
/
The_Project
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/visualization/activity_charts.py
208 строк
9 KB
CW3
Initial project setup with structure and CI/CD
28 дек 2025, 12:40
28 дек 2025, 12:40
5cacb7a
Код
Авторство
О чём код?
""" Визуализация активности студентов """ import matplotlib # Используем агрессивный backend без GUI для тестов matplotlib.use('Agg') # Важно: используем non-interactive backend import matplotlib.pyplot as plt import pandas as pd import numpy as np from typing import Dict, List, Optional import os class ActivityVisualizer: """Визуализатор активности студентов""" def __init__(self, style: str = None): """ Инициализация визуализатора Args: style: Стиль графиков (опционально) """ # Устанавливаем агрессивный backend для избежания GUI plt.switch_backend('Agg') if style and style in plt.style.available: plt.style.use(style) else: plt.style.use('default') def plot_event_distribution(self, event_counts: Dict, save_path: Optional[str] = None): """ Визуализация распределения событий Args: event_counts: Словарь с количеством событий по типам save_path: Путь для сохранения (опционально) """ try: fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6)) # Столбчатая диаграмма event_types = list(event_counts.keys()) counts = list(event_counts.values()) # Используем палитру matplotlib colors = plt.cm.Set3(np.linspace(0, 1, len(event_types))) bars = ax1.bar(range(len(event_types)), counts, color=colors, alpha=0.7) ax1.set_xlabel('Тип события') ax1.set_ylabel('Количество') ax1.set_title('Распределение событий по типам') ax1.set_xticks(range(len(event_types))) ax1.set_xticklabels(event_types, rotation=45, ha='right') # Добавляем значения на столбцы for bar, count in zip(bars, counts): height = bar.get_height() ax1.text(bar.get_x() + bar.get_width()/2., height, f'{count}', ha='center', va='bottom') # Круговая диаграмма ax2.pie(counts, labels=event_types, autopct='%1.1f%%', startangle=90, colors=colors) ax2.set_title('Доля событий по типам') ax2.axis('equal') plt.tight_layout() if save_path: plt.savefig(save_path, dpi=150, bbox_inches='tight') print(f"График сохранен: {save_path}") plt.close(fig) return save_path else: # Для тестов - не показываем, просто закрываем plt.close(fig) return None except Exception as e: print(f"⚠️ Ошибка при создании графика: {e}") plt.close('all') # Закрываем все фигуры return None def plot_student_activity_timeline(self, logs_df: pd.DataFrame, student_id: int, save_path: Optional[str] = None): """ Упрощенная визуализация временной линии активности студента Args: logs_df: DataFrame с логами student_id: ID студента save_path: Путь для сохранения (опционально) """ student_data = logs_df[logs_df['student_id'] == student_id].copy() if len(student_data) == 0: print(f"Нет данных для студента {student_id}") return # Создаем фигуру с несколькими подграфиками fig, axes = plt.subplots(2, 2, figsize=(12, 10)) # Преобразуем временные метки student_data['timestamp'] = pd.to_datetime(student_data['timestamp']) student_data['date'] = student_data['timestamp'].dt.date student_data['hour'] = student_data['timestamp'].dt.hour # 1. Активность по дням daily_activity = student_data.groupby('date').size() if not daily_activity.empty: axes[0, 0].plot(range(len(daily_activity)), daily_activity.values, marker='o', linestyle='-', color='green', linewidth=2) axes[0, 0].set_xlabel('Дни') axes[0, 0].set_ylabel('Событий в день') axes[0, 0].set_title('Активность по дням') axes[0, 0].grid(True, alpha=0.3) # 2. Распределение по часам hour_counts = student_data['hour'].value_counts().sort_index() if not hour_counts.empty: axes[0, 1].bar(hour_counts.index, hour_counts.values, color='skyblue') axes[0, 1].set_xlabel('Час дня') axes[0, 1].set_ylabel('Количество событий') axes[0, 1].set_title('Активность по времени суток') axes[0, 1].set_xticks(range(0, 24, 3)) # 3. Типы активности event_counts = student_data['event_type'].value_counts() if not event_counts.empty: axes[1, 0].bar(event_counts.index, event_counts.values, color=['skyblue', 'lightcoral', 'lightgreen', 'gold', 'violet']) axes[1, 0].set_xlabel('Тип события') axes[1, 0].set_ylabel('Количество') axes[1, 0].set_title('Типы активности') axes[1, 0].tick_params(axis='x', rotation=45) # 4. Активность по курсам course_counts = student_data['course_id'].value_counts() if not course_counts.empty: axes[1, 1].bar(course_counts.index, course_counts.values, color='orange') axes[1, 1].set_xlabel('Курс') axes[1, 1].set_ylabel('Количество') axes[1, 1].set_title('Активность по курсам') axes[1, 1].tick_params(axis='x', rotation=45) plt.suptitle(f'Анализ активности студента {student_id}', fontsize=16, fontweight='bold') plt.tight_layout() if save_path: plt.savefig(save_path, dpi=150, bbox_inches='tight') print(f"График сохранен: {save_path}") plt.show() plt.close(fig) def plot_correlation_heatmap(self, correlation_data: pd.DataFrame, save_path: Optional[str] = None): """ Упрощенная визуализация тепловой карты корреляций Args: correlation_data: DataFrame с корреляциями save_path: Путь для сохранения (опционально) """ # Выбираем числовые колонки numeric_cols = correlation_data.select_dtypes(include=[np.number]).columns if len(numeric_cols) < 2: print("Недостаточно числовых данных для корреляции") return # Расчет матрицы корреляций corr_matrix = correlation_data[numeric_cols].corr() fig, ax = plt.subplots(figsize=(10, 8)) # Тепловая карта im = ax.imshow(corr_matrix, cmap='coolwarm', vmin=-1, vmax=1) # Добавляем значения for i in range(len(corr_matrix)): for j in range(len(corr_matrix)): text = ax.text(j, i, f'{corr_matrix.iloc[i, j]:.2f}', ha="center", va="center", color="black", fontsize=8) # Настройка осей ax.set_xticks(range(len(corr_matrix.columns))) ax.set_yticks(range(len(corr_matrix.columns))) ax.set_xticklabels(corr_matrix.columns, rotation=45, ha='right', fontsize=9) ax.set_yticklabels(corr_matrix.columns, fontsize=9) # Цветовая шкала cbar = ax.figure.colorbar(im, ax=ax) cbar.ax.set_ylabel("Корреляция", rotation=-90, va="bottom") ax.set_title("Тепловая карта корреляций активности и успеваемости", fontsize=14, fontweight='bold') plt.tight_layout() if save_path: plt.savefig(save_path, dpi=150, bbox_inches='tight') print(f"Тепловая карта сохранена: {save_path}") plt.show() plt.close(fig)