""" Скрипт для конвертации результатов экспериментов из CSV в LaTeX таблицы. Этот скрипт автоматически сканирует папку experiments/, находит все подпапки с файлами results.csv, парсит данные экспериментов и генерирует LaTeX код таблиц в формате, готовом для вставки в отчёт. Структура входных данных: - experiments/N/results.csv, где N - размер популяции - CSV содержит результаты экспериментов с различными параметрами Pc и Pm - Значения в формате "X.Y (Z)" где X.Y - время выполнения, Z - количество итераций - "—" для отсутствующих данных Выходной файл: tables.tex с готовым LaTeX кодом всех таблиц. Лучший результат по времени выполнения в каждой таблице выделяется жирным. """ import re from pathlib import Path 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)". 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 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 format_value(value: str, best_time: float | None = None) -> str: """ Форматирует значение для LaTeX таблицы, выделяя лучший результат жирным. Args: value: Строковое значение из CSV best_time: Лучшее время в таблице для сравнения Returns: Отформатированное значение для LaTeX """ value = value.strip() if value == "—" or value == "" or value == "–": return "—" # Проверяем, является ли это лучшим результатом current_time = extract_time_value(value) if ( current_time is not None and best_time is not None and abs(current_time - best_time) < 0.001 ): 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) # Извлекаем заголовки колонок из 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) 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) latex_table = generate_latex_table(n, header, data_rows) tables.append(latex_table) print(f"✓ Таблица для N={n} готова (лучшее время: {best_time})") 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") 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()