Files
genetic-algorithms/lab3/csv_to_tex.py
2025-10-15 16:43:11 +03:00

340 lines
13 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Скрипт для конвертации результатов экспериментов из 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()