#!/usr/bin/env python """ Скрипт для построения специализированных графиков на основе макрометрик из Excel-файла. Строит несколько типов графиков: 1. Зависимость macro_text_recall от top_N для разных моделей при фиксированных параметрах чанкинга 2. Зависимость macro_text_recall от top_N для разных подходов к чанкингу при фиксированных моделях 3. Зависимость macro_text_recall от подхода к чанкингу для разных моделей при фиксированных top_N """ import os import matplotlib.pyplot as plt import pandas as pd import seaborn as sns # Константы EXCEL_FILE_PATH = "../../Белагропромбанк/test_vectors/combined_results.xlsx" PLOTS_DIR = "../../Белагропромбанк/test_vectors/plots" # Настройки для графиков plt.rcParams['font.family'] = 'DejaVu Sans' sns.set_style("whitegrid") FIGSIZE = (14, 10) DPI = 300 def setup_plots_directory(plots_dir: str) -> None: """ Создает директорию для сохранения графиков, если она не существует. Args: plots_dir: Путь к директории для графиков """ if not os.path.exists(plots_dir): os.makedirs(plots_dir) print(f"Создана директория для графиков: {plots_dir}") else: print(f"Использование существующей директории для графиков: {plots_dir}") def load_macro_metrics(excel_path: str) -> pd.DataFrame: """ Загружает макрометрики из Excel-файла. Args: excel_path: Путь к Excel-файлу с данными Returns: DataFrame с макрометриками """ try: df = pd.read_excel(excel_path, sheet_name="Macro метрики") print(f"Загружены данные из {excel_path}, лист 'Macro метрики'") print(f"Количество строк: {len(df)}") return df except Exception as e: print(f"Ошибка при загрузке данных: {e}") raise def plot_top_n_vs_recall_by_model(df: pd.DataFrame, plots_dir: str) -> None: """ Строит графики зависимости macro_text_recall от top_N для разных моделей при фиксированных параметрах чанкинга (50/25 и 200/75). Args: df: DataFrame с данными plots_dir: Директория для сохранения графиков """ # Фиксированные параметры чанкинга chunking_params = [ {"words": 50, "overlap": 25, "title": "Чанкинг 50/25"}, {"words": 200, "overlap": 75, "title": "Чанкинг 200/75"} ] # Создаем субплоты: 1 строка, 2 столбца fig, axes = plt.subplots(1, 2, figsize=FIGSIZE, sharey=True) for i, params in enumerate(chunking_params): # Фильтруем данные для текущих параметров чанкинга filtered_df = df[ (df['words_per_chunk'] == params['words']) & (df['overlap_words'] == params['overlap']) ] if len(filtered_df) == 0: print(f"Предупреждение: нет данных для чанкинга {params['words']}/{params['overlap']}") axes[i].text(0.5, 0.5, f"Нет данных для чанкинга {params['words']}/{params['overlap']}", ha='center', va='center', fontsize=12) axes[i].set_title(params['title']) continue # Находим уникальные модели models = filtered_df['model'].unique() # Создаем палитру цветов palette = sns.color_palette("viridis", len(models)) # Строим график для каждой модели for j, model in enumerate(models): model_df = filtered_df[filtered_df['model'] == model].sort_values('top_n') if len(model_df) <= 1: print(f"Предупреждение: недостаточно данных для модели {model} при чанкинге {params['words']}/{params['overlap']}") continue # Строим ломаную линию axes[i].plot(model_df['top_n'], model_df['macro_text_recall'], marker='o', linestyle='-', linewidth=2, label=model, color=palette[j]) # Настраиваем оси и заголовок axes[i].set_title(params['title'], fontsize=14) axes[i].set_xlabel('top_N', fontsize=12) if i == 0: axes[i].set_ylabel('macro_text_recall', fontsize=12) # Добавляем сетку axes[i].grid(True, linestyle='--', alpha=0.7) # Добавляем легенду axes[i].legend(title="Модель", fontsize=10, loc='best') # Общий заголовок plt.suptitle('Зависимость macro_text_recall от top_N для разных моделей', fontsize=16) # Настраиваем макет plt.tight_layout(rect=[0, 0, 1, 0.96]) # Сохраняем график file_path = os.path.join(plots_dir, "top_n_vs_recall_by_model.png") plt.savefig(file_path, dpi=DPI) plt.close() print(f"Создан график: {file_path}") def plot_top_n_vs_recall_by_chunking(df: pd.DataFrame, plots_dir: str) -> None: """ Строит графики зависимости macro_text_recall от top_N для разных параметров чанкинга при фиксированных моделях (bge и frida). Args: df: DataFrame с данными plots_dir: Директория для сохранения графиков """ # Фиксированные модели models = ["BAAI/bge", "frida"] # Создаем субплоты: 1 строка, 2 столбца fig, axes = plt.subplots(1, 2, figsize=FIGSIZE, sharey=True) for i, model_name in enumerate(models): # Находим все строки с моделями, содержащими указанное название model_df = df[df['model'].str.contains(model_name, case=False)] if len(model_df) == 0: print(f"Предупреждение: нет данных для модели {model_name}") axes[i].text(0.5, 0.5, f"Нет данных для модели {model_name}", ha='center', va='center', fontsize=12) axes[i].set_title(f"Модель: {model_name}") continue # Находим уникальные комбинации параметров чанкинга chunking_combinations = model_df.drop_duplicates(['words_per_chunk', 'overlap_words'])[['words_per_chunk', 'overlap_words']] # Ограничиваем количество комбинаций до 7 для читаемости if len(chunking_combinations) > 7: print(f"Предупреждение: слишком много комбинаций чанкинга для модели {model_name}, ограничиваем до 7") chunking_combinations = chunking_combinations.head(7) # Создаем палитру цветов palette = sns.color_palette("viridis", len(chunking_combinations)) # Строим график для каждой комбинации параметров чанкинга for j, (_, row) in enumerate(chunking_combinations.iterrows()): words = row['words_per_chunk'] overlap = row['overlap_words'] # Фильтруем данные для текущей модели и параметров чанкинга chunking_df = model_df[ (model_df['words_per_chunk'] == words) & (model_df['overlap_words'] == overlap) ].sort_values('top_n') if len(chunking_df) <= 1: print(f"Предупреждение: недостаточно данных для модели {model_name} с чанкингом {words}/{overlap}") continue # Строим ломаную линию axes[i].plot(chunking_df['top_n'], chunking_df['macro_text_recall'], marker='o', linestyle='-', linewidth=2, label=f"w={words}, o={overlap}", color=palette[j]) # Настраиваем оси и заголовок axes[i].set_title(f"Модель: {model_name}", fontsize=14) axes[i].set_xlabel('top_N', fontsize=12) if i == 0: axes[i].set_ylabel('macro_text_recall', fontsize=12) # Добавляем сетку axes[i].grid(True, linestyle='--', alpha=0.7) # Добавляем легенду axes[i].legend(title="Чанкинг", fontsize=10, loc='best') # Общий заголовок plt.suptitle('Зависимость macro_text_recall от top_N для разных параметров чанкинга', fontsize=16) # Настраиваем макет plt.tight_layout(rect=[0, 0, 1, 0.96]) # Сохраняем график file_path = os.path.join(plots_dir, "top_n_vs_recall_by_chunking.png") plt.savefig(file_path, dpi=DPI) plt.close() print(f"Создан график: {file_path}") def plot_chunking_vs_recall_by_model(df: pd.DataFrame, plots_dir: str) -> None: """ Строит графики зависимости macro_text_recall от подхода к чанкингу для разных моделей при фиксированных top_N (5, 20, 100). Args: df: DataFrame с данными plots_dir: Директория для сохранения графиков """ # Фиксированные значения top_N top_n_values = [5, 20, 100] # Создаем субплоты: 1 строка, 3 столбца fig, axes = plt.subplots(1, 3, figsize=FIGSIZE, sharey=True) # Создаем порядок чанкинга - сортируем по возрастанию размера и оверлапа chunking_order = df.drop_duplicates(['words_per_chunk', 'overlap_words'])[['words_per_chunk', 'overlap_words']] chunking_order = chunking_order.sort_values(['words_per_chunk', 'overlap_words']) # Создаем словарь для маппинга комбинаций чанкинга на индексы chunking_labels = [f"{row['words_per_chunk']}/{row['overlap_words']}" for _, row in chunking_order.iterrows()] chunking_map = {f"{row['words_per_chunk']}/{row['overlap_words']}": i for i, (_, row) in enumerate(chunking_order.iterrows())} for i, top_n in enumerate(top_n_values): # Фильтруем данные для текущего top_N top_n_df = df[df['top_n'] == top_n] if len(top_n_df) == 0: print(f"Предупреждение: нет данных для top_N={top_n}") axes[i].text(0.5, 0.5, f"Нет данных для top_N={top_n}", ha='center', va='center', fontsize=12) axes[i].set_title(f"top_N={top_n}") continue # Находим уникальные модели models = top_n_df['model'].unique() # Ограничиваем количество моделей до 5 для читаемости if len(models) > 5: print(f"Предупреждение: слишком много моделей для top_N={top_n}, ограничиваем до 5") models = models[:5] # Создаем палитру цветов palette = sns.color_palette("viridis", len(models)) # Строим график для каждой модели for j, model in enumerate(models): model_df = top_n_df[top_n_df['model'] == model].copy() if len(model_df) <= 1: print(f"Предупреждение: недостаточно данных для модели {model} при top_N={top_n}") continue # Создаем новую колонку с индексом чанкинга для сортировки model_df['chunking_index'] = model_df.apply( lambda row: chunking_map.get(f"{row['words_per_chunk']}/{row['overlap_words']}", -1), axis=1 ) # Отбрасываем строки с неизвестными комбинациями чанкинга model_df = model_df[model_df['chunking_index'] >= 0] if len(model_df) <= 1: continue # Сортируем по индексу чанкинга model_df = model_df.sort_values('chunking_index') # Создаем список индексов и значений для графика x_indices = model_df['chunking_index'].tolist() y_values = model_df['macro_text_recall'].tolist() # Строим ломаную линию axes[i].plot(x_indices, y_values, marker='o', linestyle='-', linewidth=2, label=model, color=palette[j]) # Настраиваем оси и заголовок axes[i].set_title(f"top_N={top_n}", fontsize=14) axes[i].set_xlabel('Подход к чанкингу', fontsize=12) if i == 0: axes[i].set_ylabel('macro_text_recall', fontsize=12) # Устанавливаем метки на оси X (подходы к чанкингу) axes[i].set_xticks(range(len(chunking_labels))) axes[i].set_xticklabels(chunking_labels, rotation=45, ha='right', fontsize=10) # Добавляем сетку axes[i].grid(True, linestyle='--', alpha=0.7) # Добавляем легенду axes[i].legend(title="Модель", fontsize=10, loc='best') # Общий заголовок plt.suptitle('Зависимость macro_text_recall от подхода к чанкингу для разных моделей', fontsize=16) # Настраиваем макет plt.tight_layout(rect=[0, 0, 1, 0.96]) # Сохраняем график file_path = os.path.join(plots_dir, "chunking_vs_recall_by_model.png") plt.savefig(file_path, dpi=DPI) plt.close() print(f"Создан график: {file_path}") def main(): """Основная функция скрипта.""" # Создаем директорию для графиков setup_plots_directory(PLOTS_DIR) # Загружаем данные try: macro_metrics = load_macro_metrics(EXCEL_FILE_PATH) except Exception as e: print(f"Критическая ошибка: {e}") return # Строим графики plot_top_n_vs_recall_by_model(macro_metrics, PLOTS_DIR) plot_top_n_vs_recall_by_chunking(macro_metrics, PLOTS_DIR) plot_chunking_vs_recall_by_model(macro_metrics, PLOTS_DIR) print("Готово! Все графики созданы.") if __name__ == "__main__": main()