""" Скрипт для конвертации результатов экспериментов из CSV в LaTeX таблицы. Этот скрипт автоматически сканирует папку experiments/, находит все подпапки с файлами results.csv, парсит данные экспериментов и генерирует LaTeX код таблиц в формате, готовом для вставки в отчёт. Структура входных данных: - experiments/N/results.csv, где N - размер популяции - CSV содержит результаты экспериментов с различными параметрами Pc и Pm - Значения в формате "X.Y (Z)" где X.Y - время выполнения, Z - количество итераций - "—" для отсутствующих данных Выходной файл: tables.tex с готовым LaTeX кодом всех таблиц. Лучшие результаты по времени и фитнесу выделяются жирным (и цветом, если задан HIGHLIGHT_COLOR). """ import re from pathlib import Path # Настройка цвета для выделения лучших результатов # None - только жирным, строка (например "magenta") - жирным и цветом HIGHLIGHT_COLOR = "magenta" def parse_csv_file(csv_path: str) -> tuple[str, list[list[str]]]: """ Парсит CSV файл с результатами эксперимента. Args: csv_path: Путь к CSV файлу Returns: Tuple с заголовком и данными таблицы """ with open(csv_path, "r", encoding="utf-8") as file: lines = file.readlines() # Удаляем пустые строки и берём только строки с данными clean_lines = [line.strip() for line in lines if line.strip()] # Первая строка - заголовки header = clean_lines[0] # Остальные строки - данные data_lines = clean_lines[1:] # Парсим данные data_rows = [] for line in data_lines: parts = line.split(",") if len(parts) >= 6: # Pc + 5 значений Pm data_rows.append(parts) return header, data_rows def extract_time_value(value: str) -> float | None: """ Извлекает значение времени из строки формата "X.Y (Z)" или "X.Y (Z) W.V". Args: value: Строка с результатом Returns: Время выполнения как float или None если значение пустое """ value = value.strip() if value == "—" or value == "" or value == "–": return None # Ищем паттерн "число.число (число)" match = re.match(r"(\d+\.?\d*)\s*\(", value) if match: return float(match.group(1)) return None def extract_fitness_value(value: str) -> float | None: """ Извлекает значение фитнеса из строки формата "X.Y (Z) W.V". Args: value: Строка с результатом Returns: Значение фитнеса как float или None если значение пустое """ value = value.strip() if value == "—" or value == "" or value == "–": return None # Ищем паттерн "число.число (число) число.число" # Фитнес - это последнее число в строке match = re.search(r"\)\s+(\d+\.?\d*)\s*$", value) if match: return float(match.group(1)) return None def find_best_time(data_rows: list[list[str]]) -> float | None: """ Находит минимальное время выполнения среди всех значений в таблице. Args: data_rows: Строки данных таблицы Returns: Минимальное время или None если нет валидных значений """ min_time = None for row in data_rows: for i in range(1, min(6, len(row))): # Пропускаем первую колонку (Pc) time_value = extract_time_value(row[i]) if time_value is not None: if min_time is None or time_value < min_time: min_time = time_value return min_time def find_best_fitness(data_rows: list[list[str]]) -> float | None: """ Находит минимальное значение фитнеса среди всех значений в таблице. Args: data_rows: Строки данных таблицы Returns: Минимальное значение фитнеса или None если нет валидных значений """ min_fitness = None for row in data_rows: for i in range(1, min(6, len(row))): # Пропускаем первую колонку (Pc) fitness_value = extract_fitness_value(row[i]) if fitness_value is not None: if min_fitness is None or fitness_value < min_fitness: min_fitness = fitness_value return min_fitness def format_value( value: str, best_time: float | None = None, best_fitness: float | None = None ) -> str: """ Форматирует значение для LaTeX таблицы, выделяя лучшие результаты жирным. Args: value: Строковое значение из CSV best_time: Лучшее время в таблице для сравнения best_fitness: Лучший фитнес в таблице для сравнения Returns: Отформатированное значение для LaTeX """ value = value.strip() if value == "—" or value == "" or value == "–": return "—" # Проверяем есть ли фитнес в строке fitness_match = re.search(r"(\d+\.?\d*)\s*\((\d+)\)\s+(\d+\.?\d*)\s*$", value) if fitness_match: # Есть фитнес: "время (поколения) фитнес" time_str = fitness_match.group(1) generations_str = fitness_match.group(2) fitness_str = fitness_match.group(3) current_time = float(time_str) current_fitness = float(fitness_str) # Проверяем, является ли время лучшим time_part = f"{time_str} ({generations_str})" if best_time is not None and abs(current_time - best_time) < 0.001: if HIGHLIGHT_COLOR is not None: time_part = ( f"\\textcolor{{{HIGHLIGHT_COLOR}}}{{\\textbf{{{time_part}}}}}" ) else: time_part = f"\\textbf{{{time_part}}}" # Проверяем, является ли фитнес лучшим fitness_part = fitness_str if best_fitness is not None and abs(current_fitness - best_fitness) < 0.00001: if HIGHLIGHT_COLOR is not None: fitness_part = ( f"\\textcolor{{{HIGHLIGHT_COLOR}}}{{\\textbf{{{fitness_part}}}}}" ) else: fitness_part = f"\\textbf{{{fitness_part}}}" return f"{time_part} {fitness_part}" else: # Нет фитнеса: только "время (поколения)" time_match = re.match(r"(\d+\.?\d*)\s*\((\d+)\)", value) if time_match: current_time = float(time_match.group(1)) if best_time is not None and abs(current_time - best_time) < 0.001: if HIGHLIGHT_COLOR is not None: return f"\\textcolor{{{HIGHLIGHT_COLOR}}}{{\\textbf{{{value}}}}}" else: return f"\\textbf{{{value}}}" return value def generate_latex_table(n: str, header: str, data_rows: list[list[str]]) -> str: """ Генерирует LaTeX код таблицы. Args: n: Размер популяции header: Заголовок таблицы data_rows: Строки данных Returns: LaTeX код таблицы """ # Находим лучшее время и лучший фитнес в таблице best_time = find_best_time(data_rows) best_fitness = find_best_fitness(data_rows) # Извлекаем заголовки колонок из header header_parts = header.split(",") pm_values = header_parts[1:] # Пропускаем "Pc \ Pm" latex_code = f""" \\begin{{table}}[h!] \\centering \\small \\caption{{Результаты для $N = {n}$}} \\begin{{tabularx}}{{\\linewidth}}{{l *{{5}}{{Y}}}} \\toprule $\\mathbf{{P_c \\;\\backslash\\; P_m}}$""" # Добавляем заголовки Pm for pm in pm_values: latex_code += f" & \\textbf{{{pm.strip()}}}" latex_code += " \\\\\n \\midrule\n" # Добавляем строки данных for row in data_rows: pc_value = row[0].strip() latex_code += f" \\textbf{{{pc_value}}}" # Добавляем значения для каждого Pm for i in range(1, min(6, len(row))): # Максимум 5 колонок Pm value = format_value(row[i], best_time, best_fitness) latex_code += f" & {value}" # Заполняем недостающие колонки если их меньше 5 for i in range(len(row) - 1, 5): latex_code += " & —" latex_code += " \\\\\n" latex_code += f""" \\bottomrule \\end{{tabularx}} \\label{{tab:pc_pm_results_{n}}} \\end{{table}}""" return latex_code def main(): """Основная функция скрипта.""" experiments_path = Path("experiments") if not experiments_path.exists(): print("Папка experiments не найдена!") return tables = [] # Сканируем все подпапки в experiments, сортируем по числовому значению N subdirs = [ subdir for subdir in experiments_path.iterdir() if subdir.is_dir() and subdir.name.isdigit() ] subdirs.sort(key=lambda x: int(x.name)) for subdir in subdirs: n = subdir.name csv_file = subdir / "results.csv" if csv_file.exists(): print(f"Обрабатываем {csv_file}...") try: header, data_rows = parse_csv_file(str(csv_file)) best_time = find_best_time(data_rows) best_fitness = find_best_fitness(data_rows) latex_table = generate_latex_table(n, header, data_rows) tables.append(latex_table) print( f"✓ Таблица для N={n} готова (лучшее время: {best_time}, лучший фитнес: {best_fitness})" ) except Exception as e: print(f"✗ Ошибка при обработке {csv_file}: {e}") else: print(f"✗ Файл {csv_file} не найден") # Сохраняем все таблицы в файл if tables: with open("tables.tex", "w", encoding="utf-8") as f: f.write("% Автоматически сгенерированные LaTeX таблицы\n") f.write( "% Лучший результат по времени и по фитнесу выделены жирным отдельно\n" ) f.write("% Убедитесь, что подключен \\usepackage{tabularx}\n") if HIGHLIGHT_COLOR is not None: f.write( "% ВНИМАНИЕ: Убедитесь, что подключен \\usepackage{xcolor} для цветового выделения\n" ) f.write( "% Используйте \\newcolumntype{Y}{>{\\centering\\arraybackslash}X} перед таблицами\n\n" ) for i, table in enumerate(tables): if i > 0: f.write("\n \n") f.write(table + "\n") print(f"\n✓ Все таблицы сохранены в файл 'tables.tex'") print(f"Сгенерировано таблиц: {len(tables)}") else: print("Не найдено данных для генерации таблиц!") if __name__ == "__main__": main()