Скрипт для генерации 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()
|
||||||
@@ -467,7 +467,7 @@
|
|||||||
\item $p_m = 0.001, 0.01, 0.05, 0.1, 0.2$ -- вероятность мутации.
|
\item $p_m = 0.001, 0.01, 0.05, 0.1, 0.2$ -- вероятность мутации.
|
||||||
\end{itemize}
|
\end{itemize}
|
||||||
|
|
||||||
Результаты измерений представлены в таблицах \ref{tab:pc_pm_results}--\ref{tab:pc_pm_results4}. В ячейках указано усредненное время в миллисекундах нахождения минимума функции. В скобках указано усредненное количество поколений, за которое было найдено решение. Каждый эксперимент запускался 30 раз. Если в ячейке стоит прочерк, то это означает, что решение не было найдено за 200 поколений. Лучшее значение по времени выполнения для каждого размера популяции выделено жирным шрифтом.
|
Результаты измерений представлены в таблицах \ref{tab:pc_pm_results_10}--\ref{tab:pc_pm_results_50}. В ячейках указано усредненное время в миллисекундах нахождения минимума функции. В скобках указано усредненное количество поколений, за которое было найдено решение. Каждый эксперимент запускался 30 раз. Если в ячейке стоит прочерк, то это означает, что решение не было найдено за 200 поколений. Лучшее значение по времени выполнения для каждого размера популяции выделено жирным шрифтом.
|
||||||
|
|
||||||
\newcolumntype{Y}{>{\centering\arraybackslash}X}
|
\newcolumntype{Y}{>{\centering\arraybackslash}X}
|
||||||
|
|
||||||
@@ -479,17 +479,18 @@
|
|||||||
\toprule
|
\toprule
|
||||||
$\mathbf{P_c \;\backslash\; P_m}$ & \textbf{0.001} & \textbf{0.010} & \textbf{0.050} & \textbf{0.100} & \textbf{0.200} \\
|
$\mathbf{P_c \;\backslash\; P_m}$ & \textbf{0.001} & \textbf{0.010} & \textbf{0.050} & \textbf{0.100} & \textbf{0.200} \\
|
||||||
\midrule
|
\midrule
|
||||||
\textbf{0.3} & 11.5 (167) & 8.4 (123) & 5.4 (78) & 4.9 (71) & 3.3 (48) \\
|
\textbf{0.3} & — & — & 8.9 (87) & 5.3 (46) & — \\
|
||||||
\textbf{0.4} & 10.1 (144) & 7.1 (104) & 6.3 (92) & 4.7 (67) & 4.7 (67) \\
|
\textbf{0.4} & — & — & 19.1 (127) & 14.2 (111) & 2.9 (24) \\
|
||||||
\textbf{0.5} & 11.4 (168) & 7.7 (112) & 5.4 (79) & 6.1 (83) & \textbf{3.1 (44)} \\
|
\textbf{0.5} & — & — & 13.3 (117) & 13.7 (123) & 10.1 (74) \\
|
||||||
\textbf{0.6} & 11.0 (160) & 6.7 (97) & 4.9 (70) & 4.7 (67) & 5.3 (74) \\
|
\textbf{0.6} & — & — & 7.8 (68) & 14.4 (100) & 7.5 (57) \\
|
||||||
\textbf{0.7} & 12.1 (174) & 9.3 (135) & 3.7 (52) & 4.7 (67) & 6.5 (92) \\
|
\textbf{0.7} & — & 6.9 (59) & — & \textbf{1.1 (9)} & — \\
|
||||||
\textbf{0.8} & 8.7 (126) & 8.3 (119) & 3.9 (57) & 7.9 (113)& 4.4 (61) \\
|
\textbf{0.8} & — & — & — & 5.4 (41) & — \\
|
||||||
\bottomrule
|
\bottomrule
|
||||||
\end{tabularx}
|
\end{tabularx}
|
||||||
\label{tab:pc_pm_results}
|
\label{tab:pc_pm_results_10}
|
||||||
\end{table}
|
\end{table}
|
||||||
|
|
||||||
|
|
||||||
\begin{table}[h!]
|
\begin{table}[h!]
|
||||||
\centering
|
\centering
|
||||||
\small
|
\small
|
||||||
@@ -498,17 +499,18 @@
|
|||||||
\toprule
|
\toprule
|
||||||
$\mathbf{P_c \;\backslash\; P_m}$ & \textbf{0.001} & \textbf{0.010} & \textbf{0.050} & \textbf{0.100} & \textbf{0.200} \\
|
$\mathbf{P_c \;\backslash\; P_m}$ & \textbf{0.001} & \textbf{0.010} & \textbf{0.050} & \textbf{0.100} & \textbf{0.200} \\
|
||||||
\midrule
|
\midrule
|
||||||
\textbf{0.3} & 14.7 (111) & 8.2 (62) & 4.9 (37) & 4.7 (35) & 8.7 (63) \\
|
\textbf{0.3} & — & 3.2 (17) & 11.8 (55) & — & — \\
|
||||||
\textbf{0.4} & 12.8 (95) & 7.3 (54) & 4.7 (35) & 4.3 (32) & 8.2 (61) \\
|
\textbf{0.4} & — & 2.6 (11) & 4.8 (22) & 17.7 (85) & — \\
|
||||||
\textbf{0.5} & 10.5 (78) & 5.4 (40) & \textbf{2.2 (16)} & 5.5 (40) & 12.1 (89) \\
|
\textbf{0.5} & \textbf{1.9 (10)} & — & 29.0 (137) & — & — \\
|
||||||
\textbf{0.6} & 14.0 (103) & 6.5 (48) & 3.4 (25) & 4.0 (30) & 14.0 (87) \\
|
\textbf{0.6} & — & 2.7 (13) & 17.6 (81) & 35.7 (157) & — \\
|
||||||
\textbf{0.7} & 11.5 (84) & 6.2 (46) & 3.0 (22) & 3.2 (24) & 11.6 (83) \\
|
\textbf{0.7} & — & 2.6 (13) & 9.1 (38) & 28.3 (119) & — \\
|
||||||
\textbf{0.8} & 9.2 (64) & 5.8 (41) & 2.5 (18) & 3.0 (22) & 11.2 (78) \\
|
\textbf{0.8} & — & 17.6 (76) & 13.7 (57) & 23.4 (95) & — \\
|
||||||
\bottomrule
|
\bottomrule
|
||||||
\end{tabularx}
|
\end{tabularx}
|
||||||
\label{tab:pc_pm_results2}
|
\label{tab:pc_pm_results_25}
|
||||||
\end{table}
|
\end{table}
|
||||||
|
|
||||||
|
|
||||||
\begin{table}[h!]
|
\begin{table}[h!]
|
||||||
\centering
|
\centering
|
||||||
\small
|
\small
|
||||||
@@ -517,16 +519,18 @@
|
|||||||
\toprule
|
\toprule
|
||||||
$\mathbf{P_c \;\backslash\; P_m}$ & \textbf{0.001} & \textbf{0.010} & \textbf{0.050} & \textbf{0.100} & \textbf{0.200} \\
|
$\mathbf{P_c \;\backslash\; P_m}$ & \textbf{0.001} & \textbf{0.010} & \textbf{0.050} & \textbf{0.100} & \textbf{0.200} \\
|
||||||
\midrule
|
\midrule
|
||||||
\textbf{0.3} & 6.1 (26) & 5.2 (22) & 6.3 (26) & 11.6 (48) & 40.2 (147) \\
|
\textbf{0.3} & 5.6 (19) & 4.7 (15) & — & — & — \\
|
||||||
\textbf{0.4} & 6.1 (26) & 4.5 (19) & 5.2 (22) & 9.8 (40) & 37.2 (149) \\
|
\textbf{0.4} & \textbf{3.3 (11)} & 48.7 (148) & — & — & — \\
|
||||||
\textbf{0.5} & 10.5 (44) & 4.9 (20) & 7.6 (28) & 17.1 (65) & 36.2 (144) \\
|
\textbf{0.5} & 4.0 (12) & 8.0 (24) & 56.5 (151) & — & — \\
|
||||||
\textbf{0.6} & 7.5 (31) & \textbf{4.6 (19)} & 5.6 (23) & 18.8 (76) & 42.0 (158) \\
|
\textbf{0.6} & 3.6 (10) & 4.9 (14) & 29.3 (77) & — & — \\
|
||||||
\textbf{0.7} & 7.6 (31) & 4.7 (20) & 7.6 (31) & 13.9 (55) & 34.3 (136) \\
|
\textbf{0.7} & 3.9 (11) & 36.5 (87) & 44.2 (107) & — & — \\
|
||||||
\textbf{0.8} & 10.8 (44) & 5.0 (21) & 6.1 (24) & 13.9 (56) & 36.5 (145) \\
|
\textbf{0.8} & — & 76.4 (189) & 17.3 (41) & — & — \\
|
||||||
\bottomrule
|
\bottomrule
|
||||||
\end{tabularx}
|
\end{tabularx}
|
||||||
\label{tab:pc_pm_results3}
|
\label{tab:pc_pm_results_50}
|
||||||
\end{table}
|
\end{table}
|
||||||
|
|
||||||
|
|
||||||
\begin{table}[h!]
|
\begin{table}[h!]
|
||||||
\centering
|
\centering
|
||||||
\small
|
\small
|
||||||
@@ -535,16 +539,17 @@
|
|||||||
\toprule
|
\toprule
|
||||||
$\mathbf{P_c \;\backslash\; P_m}$ & \textbf{0.001} & \textbf{0.010} & \textbf{0.050} & \textbf{0.100} & \textbf{0.200} \\
|
$\mathbf{P_c \;\backslash\; P_m}$ & \textbf{0.001} & \textbf{0.010} & \textbf{0.050} & \textbf{0.100} & \textbf{0.200} \\
|
||||||
\midrule
|
\midrule
|
||||||
\textbf{0.3} & \textbf{7.6 (16)} & 9.5 (21) & 29.0 (60) & 62.9 (128) & -- \\
|
\textbf{0.3} & 7.8 (14) & 12.6 (22) & — & — & — \\
|
||||||
\textbf{0.4} & 8.0 (17) & 9.6 (21) & 31.5 (68) & 56.6 (120) & -- \\
|
\textbf{0.4} & — & 14.9 (25) & — & — & — \\
|
||||||
\textbf{0.5} & 9.1 (20) & 9.2 (20) & 22.6 (48) & 59.4 (124) & -- \\
|
\textbf{0.5} & 7.3 (12) & 10.9 (17) & — & — & — \\
|
||||||
\textbf{0.6} & 17.8 (38) & 12.3 (26) & 30.0 (64) & 61.1 (128) & 95.3 (196) \\
|
\textbf{0.6} & 8.4 (13) & 12.4 (16) & — & — & — \\
|
||||||
\textbf{0.7} & 10.0 (22) & 14.3 (31) & 30.3 (64) & 49.1 (103) & -- \\
|
\textbf{0.7} & 9.9 (14) & 11.1 (15) & — & — & — \\
|
||||||
\textbf{0.8} & 16.4 (34) & 12.1 (25) & 31.4 (64) & 54.9 (114) & -- \\
|
\textbf{0.8} & \textbf{7.0 (10)} & 28.4 (38) & — & — & — \\
|
||||||
\bottomrule
|
\bottomrule
|
||||||
\end{tabularx}
|
\end{tabularx}
|
||||||
\label{tab:pc_pm_results4}
|
\label{tab:pc_pm_results_100}
|
||||||
\end{table}
|
\end{table}
|
||||||
|
|
||||||
|
|
||||||
\newpage
|
\newpage
|
||||||
\phantom{text}
|
\phantom{text}
|
||||||
|
|||||||
Reference in New Issue
Block a user