Скрипт для генерации tex таблиц по результатам экспериментов
This commit is contained in:
243
lab2/csv_to_tex.py
Normal file
243
lab2/csv_to_tex.py
Normal file
@@ -0,0 +1,243 @@
|
||||
"""
|
||||
Скрипт для конвертации результатов экспериментов из 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()
|
||||
Reference in New Issue
Block a user