From 60b860ce3619d5f165bf8eda6ad596d5d458d1e2 Mon Sep 17 00:00:00 2001 From: Florian Fischer Date: Tue, 11 Feb 2020 13:23:55 +0100 Subject: move plotting code from src.benchmark to src.plots --- src/plots.py | 518 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 518 insertions(+) create mode 100644 src/plots.py (limited to 'src/plots.py') diff --git a/src/plots.py b/src/plots.py new file mode 100644 index 0000000..b6ef4f8 --- /dev/null +++ b/src/plots.py @@ -0,0 +1,518 @@ +import os + +import matplotlib +import matplotlib.pyplot as plt +import numpy as np +import tikzplotlib + +from src.benchmark import Benchmark +import src.globalvars +from src.util import print_warn + +# This is useful when evaluating strings in the plot functions. str(np.NaN) == "nan" +nan = np.NaN + + +###### Summary helpers ###### +def _eval_with_stat(bench, evaluation, alloc, perm, stat): + try: + s = evaluation.format(**bench.results["stats"][alloc][perm][stat]) + except KeyError as e: + import traceback + print_warn(traceback.format_exc()) + print_warn(f"For {alloc} in {perm}") + return nan + return eval(s) + +def plot_single_arg(bench, yval, ylabel="'y-label'", xlabel="'x-label'", + autoticks=True, title="'default title'", filepostfix="", + sumdir="", arg="", scale=None, file_ext=src.globalvars.summary_file_ext): + + args = bench.results["args"] + allocators = bench.results["allocators"] + + arg = arg or list(args.keys())[0] + + if not autoticks: + x_vals = list(range(1, len(args[arg]) + 1)) + else: + x_vals = args[arg] + + for allocator in allocators: + y_vals = [] + for perm in bench.iterate_args(args=args): + if scale: + if scale == allocator: + y_vals = [1] * len(x_vals) + else: + mean = _eval_with_stat(bench, yval, allocator, perm, "mean") + norm_mean = _eval_with_stat(bench, yval, scale, perm, "mean") + y_vals.append(mean / norm_mean) + else: + y_vals.append(_eval_with_stat(bench, yval, allocator, perm, "mean")) + + plt.plot(x_vals, y_vals, marker='.', linestyle='-', + label=allocator, color=allocators[allocator]["color"]) + + plt.legend(loc="best") + if not autoticks: + plt.xticks(x_vals, args[arg]) + plt.xlabel(eval(xlabel)) + plt.ylabel(eval(ylabel)) + plt.title(eval(title)) + figname = os.path.join(sumdir, f"{bench.name}.{filepostfix}.{file_ext}") + if figname.endswith(".tex"): + tikzplotlib.save(figname) + else: + plt.savefig(figname) + plt.clf() + +def barplot_single_arg(bench, yval, ylabel="'y-label'", xlabel="'x-label'", + title="'default title'", filepostfix="", sumdir="", + arg="", scale=None, file_ext=src.globalvars.summary_file_ext, yerr=True): + + args = bench.results["args"] + allocators = bench.results["allocators"] + nallocators = len(allocators) + + if arg: + arg = args[arg] + elif args.keys(): + arg = args[list(args.keys())[0]] + else: + arg = [""] + + narg = len(arg) + + for i, allocator in enumerate(allocators): + x_vals = list(range(i, narg * (nallocators+1), nallocators+1)) + y_vals = [] + y_errs = None + if yerr: + y_errs = [] + + for perm in bench.iterate_args(args=args): + if scale: + if scale == allocator: + y_vals = [1] * len(x_vals) + else: + mean = _eval_with_stat(bench, yval, allocator, perm, "mean") + norm_mean = _eval_with_stat(bench, yval, scale, perm, "mean") + y_vals.append(mean / norm_mean) + else: + y_vals.append(_eval_with_stat(bench, yval, allocator, perm, "mean")) + + if yerr: + y_errs.append(_eval_with_stat(bench, yval, allocator, perm, "std")) + + plt.bar(x_vals, y_vals, width=1, label=allocator, yerr=y_errs, + color=allocators[allocator]["color"]) + + plt.legend(loc="best") + plt.xticks(list(range(int(np.floor(nallocators/2)), narg*(nallocators+1), nallocators+1)), arg) + plt.xlabel(eval(xlabel)) + plt.ylabel(eval(ylabel)) + plt.title(eval(title)) + figname = os.path.join(sumdir, f"{bench.name}.{filepostfix}.{file_ext}") + if figname.endswith(".tex"): + import tikzplotlib + tikzplotlib.save(figname) + else: + plt.savefig(figname) + plt.clf() + +def plot_fixed_arg(bench, yval, ylabel="'y-label'", xlabel="loose_arg", + autoticks=True, title="'default title'", filepostfix="", + sumdir="", fixed=None, file_ext=src.globalvars.summary_file_ext, scale=None): + + args = bench.results["args"] + allocators = bench.results["allocators"] + + for arg in fixed or args: + loose_arg = [a for a in args if a != arg][0] + + if not autoticks: + x_vals = list(range(1, len(args[loose_arg]) + 1)) + else: + x_vals = args[loose_arg] + + for arg_value in args[arg]: + for allocator in allocators: + y_vals = [] + for perm in bench.iterate_args_fixed({arg: arg_value}, args=args): + if scale: + if scale == allocator: + y_vals = [1] * len(x_vals) + else: + mean = _eval_with_stat(bench, yval, allocator, perm, "mean") + norm_mean = _eval_with_stat(bench, yval, scale, perm, "mean") + y_vals.append(mean / norm_mean) + else: + y_vals.append(_eval_with_stat(bench, yval, allocator, perm, "mean")) + + plt.plot(x_vals, y_vals, marker='.', linestyle='-', + label=allocator, color=allocators[allocator]["color"]) + + plt.legend(loc="best") + if not autoticks: + plt.xticks(x_vals, args[loose_arg]) + plt.xlabel(eval(xlabel)) + plt.ylabel(eval(ylabel)) + plt.title(eval(title)) + figname = os.path.join(sumdir, + f"{bench.name}.{arg}.{arg_value}.{filepostfix}.{file_ext}") + if figname.endswith(".tex"): + import tikzplotlib + tikzplotlib.save(figname) + else: + plt.savefig(figname) + plt.clf() + +def export_facts_to_file(bench, comment_symbol, f): + """Write collected facts about used system and benchmark to file""" + print(comment_symbol, bench.name, file=f) + print(file=f) + print(comment_symbol, "Common facts:", file=f) + for k, v in src.facter.FACTS.items(): + print(comment_symbol, k + ":", v, file=f) + print(file=f) + print(comment_symbol, "Benchmark facts:", file=f) + for k, v in bench.results["facts"].items(): + print(comment_symbol, k + ":", v, file=f) + print(file=f) + +def export_stats_to_csv(bench, datapoint, path=None): + """Write descriptive statistics about datapoint to csv file""" + allocators = bench.results["allocators"] + args = bench.results["args"] + stats = bench.results["stats"] + + if path is None: + path = datapoint + + path = path + ".csv" + + stats_fields = list(stats[list(allocators)[0]][list(bench.iterate_args(args=args))[0]]) + fieldnames = ["allocator", *args, *stats_fields] + widths = [] + for fieldname in fieldnames: + widths.append(len(fieldname) + 2) + + # collect rows + rows = {} + for alloc in allocators: + rows[alloc] = {} + for perm in bench.iterate_args(args=args): + d = [] + d.append(alloc) + d += list(perm._asdict().values()) + d += [stats[alloc][perm][s][datapoint] for s in stats[alloc][perm]] + d[-1] = (",".join([str(x) for x in d[-1]])) + rows[alloc][perm] = d + + # calc widths + for i in range(0, len(fieldnames)): + for alloc in allocators: + for perm in bench.iterate_args(args=args): + field_len = len(str(rows[alloc][perm][i])) + 2 + if field_len > widths[i]: + widths[i] = field_len + + with open(path, "w") as f: + headerline = "" + for i, h in enumerate(fieldnames): + headerline += h.capitalize().ljust(widths[i]).replace("_", "-") + print(headerline, file=f) + + for alloc in allocators: + for perm in bench.iterate_args(args=args): + line = "" + for i, x in enumerate(rows[alloc][perm]): + line += str(x).ljust(widths[i]) + print(line.replace("_", "-"), file=f) + +def export_stats_to_dataref(bench, datapoint, path=None): + """Write descriptive statistics about datapoint to dataref file""" + stats = bench.results["stats"] + + if path is None: + path = datapoint + + path = path + ".dataref" + + # Example: \drefset{/mysql/glibc/40/Lower-whisker}{71552.0} + line = "\\drefset{{/{}/{}/{}/{}}}{{{}}}" + + with open(path, "w") as f: + # Write facts to file + export_facts_to_file(bench, "%", f) + + for alloc in bench.results["allocators"]: + for perm in bench.iterate_args(args=bench.results["args"]): + for statistic, values in stats[alloc][perm].items(): + cur_line = line.format(bench.name, alloc, + "/".join([str(p) for p in list(perm)]), + statistic, values[datapoint]) + # Replace empty outliers + cur_line.replace("[]", "") + # Replace underscores + cur_line.replace("_", "-") + print(cur_line, file=f) + +def write_best_doublearg_tex_table(bench, evaluation, sort=">", + filepostfix="", sumdir="", std=False): + args = bench.results["args"] + keys = list(args.keys()) + allocators = bench.results["allocators"] + + header_arg = keys[0] if len(args[keys[0]]) < len(args[keys[1]]) else keys[1] + row_arg = [arg for arg in args if arg != header_arg][0] + + headers = args[header_arg] + rows = args[row_arg] + + cell_text = [] + for av in rows: + row = [] + for perm in bench.iterate_args_fixed({row_arg: av}, args=args): + best = [] + best_val = None + for allocator in allocators: + d = [] + for m in bench.results[allocator][perm]: + d.append(eval(evaluation.format(**m))) + mean = np.mean(d) + if not best_val: + best = [allocator] + best_val = mean + elif ((sort == ">" and mean > best_val) + or (sort == "<" and mean < best_val)): + best = [allocator] + best_val = mean + elif mean == best_val: + best.append(allocator) + + row.append("{}: {:.3f}".format(best[0], best_val)) + cell_text.append(row) + + fname = os.path.join(sumdir, ".".join([bench.name, filepostfix, "tex"])) + with open(fname, "w") as f: + print("\\documentclass{standalone}", file=f) + print("\\begin{document}", file=f) + print("\\begin{tabular}{|", end="", file=f) + print(" l |" * len(headers), "}", file=f) + + print(header_arg+"/"+row_arg, end=" & ", file=f) + for header in headers[:-1]: + print(header, end="& ", file=f) + print(headers[-1], "\\\\", file=f) + + for i, row in enumerate(cell_text): + print(rows[i], end=" & ", file=f) + for e in row[:-1]: + print(e, end=" & ", file=f) + print(row[-1], "\\\\", file=f) + print("\\end{tabular}", file=f) + print("\\end{document}", file=f) + +def write_tex_table(bench, entries, sort=">", + filepostfix="", sumdir="", std=False): + """generate a latex standalone table from an list of entries dictionaries + + Entries must have at least the two keys: "label" and "expression". + The optional "sort" key specifies the direction of the order: + ">" : bigger is better. + "<" : smaller is better. + + Table layout: + + | alloc1 | alloc2 | .... + --------------------------------------- + | name1 name2 | ... + --------------------------------------- + perm1 | eavl1 eval2 | ... + perm2 | eval1 eval2 | ... + """ + args = bench.results["args"] + allocators = bench.results["allocators"] + nallocators = len(allocators) + nentries = len(entries) + perm_fields = bench.Perm._fields + nperm_fields = len(perm_fields) + + alloc_header_line = f"\\multicolumn{{{nperm_fields}}}{{c|}}{{}} &" + for alloc in allocators: + alloc_header_line += f"\\multicolumn{{{nentries}}}{{c|}}{{{alloc}}} &" + alloc_header_line = alloc_header_line[:-1] + "\\\\" + + perm_fields_header = "" + for field in bench.Perm._fields: + perm_fields_header += f'{field} &' + entry_header_line = "" + for entry in entries: + entry_header_line += f'{entry["label"]} &' + entry_header_line = perm_fields_header + entry_header_line * nallocators + entry_header_line = entry_header_line[:-1] + "\\\\" + + fname = os.path.join(sumdir, ".".join([bench.name, filepostfix, "tex"])) + with open(fname, "w") as f: + print("\\documentclass{standalone}", file=f) + print("\\usepackage{booktabs}", file=f) + print("\\usepackage{xcolor}", file=f) + print("\\begin{document}", file=f) + print("\\begin{tabular}{|", f"{'c|'*nperm_fields}", f"{'c'*nentries}|"*nallocators, "}", file=f) + print("\\toprule", file=f) + + print(alloc_header_line, file=f) + print("\\hline", file=f) + print(entry_header_line, file=f) + print("\\hline", file=f) + + for perm in bench.iterate_args(args=args): + values = [[] for _ in entries] + maxs = [None for _ in entries] + mins = [None for _ in entries] + for allocator in allocators: + for i, entry in enumerate(entries): + expr = entry["expression"] + values[i].append(eval(expr.format(**bench.results["stats"][allocator][perm]["mean"]))) + + # get max and min for each entry + for i, entry in enumerate(entries): + if not "sort" in entry: + continue + # bigger is better + elif entry["sort"] == ">": + maxs[i] = max(values[i]) + mins[i] = min(values[i]) + # smaller is better + elif entry["sort"] == "<": + mins[i] = max(values[i]) + maxs[i] = min(values[i]) + + # build row + row = "" + perm_dict = perm._asdict() + for field in perm_fields: + row += str(perm_dict[field]) + "&" + + for i, _ in enumerate(allocators): + for y, entry_vals in enumerate(values): + val = entry_vals[i] + + # format + val_str = str(val) + if type(val) == float: + val_str = f"{val:.2f}" + + # colorize + if val == maxs[y]: + val_str = f"\\textcolor{{green}}{{{val_str}}}" + elif val == mins[y]: + val_str = f"\\textcolor{{red}}{{{val_str}}}" + row += f"{val_str} &" + #escape _ for latex + row = row.replace("_", "\\_") + print(row[:-1], "\\\\", file=f) + + print("\\end{tabular}", file=f) + print("\\end{document}", file=f) + +def pgfplot_legend(bench, sumdir=""): + + allocators = bench.results["allocators"] + s =\ +""" +\\documentclass{standalone} +\\usepackage{pgfplots} + +\\usepackage{pgfkeys} + +\\newenvironment{customlegend}[1][]{% +\t\\begingroup +\t\\csname pgfplots@init@cleared@structures\\endcsname +\t\\pgfplotsset{#1}% +}{% +\t\\csname pgfplots@createlegend\\endcsname +\t\\endgroup +}% +\\def\\addlegendimage{\\csname pgfplots@addlegendimage\\endcsname} + +\\usepackage{xcolor} +""" + + for alloc_name, alloc_dict in allocators.items(): + # define color + rgb = matplotlib.colors.to_rgb(alloc_dict["color"]) + s += f"\\providecolor{{{alloc_name}-color}}{{rgb}}{{{rgb[0]},{rgb[1]},{rgb[2]}}}\n" + + s +=\ +""" +\\begin{document} +\\begin{tikzpicture} +\\begin{customlegend}[ +\tlegend entries={""" + + alloc_list = "" + addlegendimage_list = "" + for alloc_name in allocators: + alloc_list += f"{alloc_name}, " + addlegendimage_list += "\t\\addlegendimage{}\n" + + s += alloc_list[:-2] + "},\n]" + s += addlegendimage_list + s +=\ +""" +\\end{customlegend} +\\end{tikzpicture} +\\end{document}""" + + with open(os.path.join(sumdir, "legend.tex"), "w") as legend_file: + print(s, file=legend_file) + +def pgfplot_linear(bench, perms, xval, yval, ylabel="'y-label'", xlabel="'x-label'", + title="'default title'", postfix="", sumdir="", scale=None): + + allocators = bench.results["allocators"] + perms = list(perms) + title = eval(title) + s =\ +"""\\documentclass{standalone} +\\usepackage{pgfplots} +\\usepackage{xcolor} +""" + + for alloc_name, alloc_dict in allocators.items(): + s += f"\\begin{{filecontents*}}{{{alloc_name}.dat}}\n" + for i, perm in enumerate(perms): + x = _eval_with_stat(bench, xval, alloc_name, perm, "mean") + y = _eval_with_stat(bench, yval, alloc_name, perm, "mean") + s += f"{x} {y}\n" + s += "\\end{filecontents*}\n" + + # define color + rgb = matplotlib.colors.to_rgb(alloc_dict["color"]) + s += f"\\providecolor{{{alloc_name}-color}}{{rgb}}{{{rgb[0]},{rgb[1]},{rgb[2]}}}\n" + + s +=\ +f""" +\\begin{{document}} +\\begin{{tikzpicture}} +\\begin{{axis}}[ +\ttitle={{{title}}}, +\txlabel={{{eval(xlabel)}}}, +\tylabel={{{eval(ylabel)}}}, +] +""" + + for alloc_name in allocators: + s += f"\\addplot [{alloc_name}-color] table {{{alloc_name}.dat}};\n" + # s += f"\t\\addplot table {{{alloc_name}.dat}};\n" + + s +=\ +"""\\end{axis} +\\end{tikzpicture} +\\end{document}""" + + with open(os.path.join(sumdir, f"{bench.name}.{postfix}.tex"), "w") as plot_file: + print(s, file=plot_file) -- cgit v1.2.3 From 5d6a470ce0c7d334205293de9b596a5ea4eb2e8c Mon Sep 17 00:00:00 2001 From: Florian Fischer Date: Tue, 11 Feb 2020 14:22:07 +0100 Subject: cleanup plot code * use str.format instead of eval for plot labels * use meaningful variable names * add license header * add some doc strings --- src/plots.py | 249 ++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 136 insertions(+), 113 deletions(-) (limited to 'src/plots.py') diff --git a/src/plots.py b/src/plots.py index b6ef4f8..1be0d31 100644 --- a/src/plots.py +++ b/src/plots.py @@ -1,11 +1,29 @@ +# Copyright 2018-2019 Florian Fischer +# +# This file is part of allocbench. +# +# allocbench is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# allocbench is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with allocbench. If not, see . +"""Plot different graphs from allocbench results""" + import os +import traceback import matplotlib import matplotlib.pyplot as plt import numpy as np import tikzplotlib -from src.benchmark import Benchmark import src.globalvars from src.util import print_warn @@ -13,20 +31,20 @@ from src.util import print_warn nan = np.NaN -###### Summary helpers ###### def _eval_with_stat(bench, evaluation, alloc, perm, stat): + """Helper to evaluate a datapoint description string""" try: - s = evaluation.format(**bench.results["stats"][alloc][perm][stat]) - except KeyError as e: - import traceback + res = evaluation.format(**bench.results["stats"][alloc][perm][stat]) + except KeyError: print_warn(traceback.format_exc()) print_warn(f"For {alloc} in {perm}") return nan - return eval(s) + return eval(res) -def plot_single_arg(bench, yval, ylabel="'y-label'", xlabel="'x-label'", - autoticks=True, title="'default title'", filepostfix="", +def plot_single_arg(bench, yval, ylabel="y-label", xlabel="x-label", + autoticks=True, title="default title", filepostfix="", sumdir="", arg="", scale=None, file_ext=src.globalvars.summary_file_ext): + """plot line graphs for each permutation of the benchmark's command arguments""" args = bench.results["args"] allocators = bench.results["allocators"] @@ -57,9 +75,11 @@ def plot_single_arg(bench, yval, ylabel="'y-label'", xlabel="'x-label'", plt.legend(loc="best") if not autoticks: plt.xticks(x_vals, args[arg]) - plt.xlabel(eval(xlabel)) - plt.ylabel(eval(ylabel)) - plt.title(eval(title)) + label_substitutions = vars() + label_substitutions.update(vars(bench)) + plt.xlabel(xlabel.format(**label_substitutions)) + plt.ylabel(ylabel.format(**label_substitutions)) + plt.title(title.format(**label_substitutions)) figname = os.path.join(sumdir, f"{bench.name}.{filepostfix}.{file_ext}") if figname.endswith(".tex"): tikzplotlib.save(figname) @@ -70,6 +90,7 @@ def plot_single_arg(bench, yval, ylabel="'y-label'", xlabel="'x-label'", def barplot_single_arg(bench, yval, ylabel="'y-label'", xlabel="'x-label'", title="'default title'", filepostfix="", sumdir="", arg="", scale=None, file_ext=src.globalvars.summary_file_ext, yerr=True): + """plot bar plots for each permutation of the benchmark's command arguments""" args = bench.results["args"] allocators = bench.results["allocators"] @@ -110,12 +131,14 @@ def barplot_single_arg(bench, yval, ylabel="'y-label'", xlabel="'x-label'", plt.legend(loc="best") plt.xticks(list(range(int(np.floor(nallocators/2)), narg*(nallocators+1), nallocators+1)), arg) - plt.xlabel(eval(xlabel)) - plt.ylabel(eval(ylabel)) - plt.title(eval(title)) + + label_substitutions = vars() + label_substitutions.update(vars(bench)) + plt.xlabel(xlabel.format(**label_substitutions)) + plt.ylabel(ylabel.format(**label_substitutions)) + plt.title(title.format(**label_substitutions)) figname = os.path.join(sumdir, f"{bench.name}.{filepostfix}.{file_ext}") if figname.endswith(".tex"): - import tikzplotlib tikzplotlib.save(figname) else: plt.savefig(figname) @@ -156,30 +179,32 @@ def plot_fixed_arg(bench, yval, ylabel="'y-label'", xlabel="loose_arg", plt.legend(loc="best") if not autoticks: plt.xticks(x_vals, args[loose_arg]) - plt.xlabel(eval(xlabel)) - plt.ylabel(eval(ylabel)) - plt.title(eval(title)) + + label_substitutions = vars() + label_substitutions.update(vars(bench)) + plt.xlabel(xlabel.format(**label_substitutions)) + plt.ylabel(ylabel.format(**label_substitutions)) + plt.title(title.format(**label_substitutions)) figname = os.path.join(sumdir, f"{bench.name}.{arg}.{arg_value}.{filepostfix}.{file_ext}") if figname.endswith(".tex"): - import tikzplotlib tikzplotlib.save(figname) else: plt.savefig(figname) plt.clf() -def export_facts_to_file(bench, comment_symbol, f): +def export_facts_to_file(bench, comment_symbol, output_file): """Write collected facts about used system and benchmark to file""" - print(comment_symbol, bench.name, file=f) - print(file=f) - print(comment_symbol, "Common facts:", file=f) - for k, v in src.facter.FACTS.items(): - print(comment_symbol, k + ":", v, file=f) - print(file=f) - print(comment_symbol, "Benchmark facts:", file=f) - for k, v in bench.results["facts"].items(): - print(comment_symbol, k + ":", v, file=f) - print(file=f) + print(comment_symbol, bench.name, file=output_file) + print(file=output_file) + print(comment_symbol, "Common facts:", file=output_file) + for fact, value in src.facter.FACTS.items(): + print("f{comment_symbol} {fact}: {value}", file=output_file) + print(file=output_file) + print(comment_symbol, "Benchmark facts:", file=output_file) + for fact, value in bench.results["facts"].items(): + print(f"{comment_symbol} {fact}: {value}", file=output_file) + print(file=output_file) def export_stats_to_csv(bench, datapoint, path=None): """Write descriptive statistics about datapoint to csv file""" @@ -203,12 +228,12 @@ def export_stats_to_csv(bench, datapoint, path=None): for alloc in allocators: rows[alloc] = {} for perm in bench.iterate_args(args=args): - d = [] - d.append(alloc) - d += list(perm._asdict().values()) - d += [stats[alloc][perm][s][datapoint] for s in stats[alloc][perm]] - d[-1] = (",".join([str(x) for x in d[-1]])) - rows[alloc][perm] = d + row = [] + row.append(alloc) + row += list(perm._asdict().values()) + row += [stats[alloc][perm][stat][datapoint] for stat in stats[alloc][perm]] + row[-1] = (",".join([str(x) for x in row[-1]])) + rows[alloc][perm] = row # calc widths for i in range(0, len(fieldnames)): @@ -218,18 +243,18 @@ def export_stats_to_csv(bench, datapoint, path=None): if field_len > widths[i]: widths[i] = field_len - with open(path, "w") as f: + with open(path, "w") as csv_file: headerline = "" - for i, h in enumerate(fieldnames): - headerline += h.capitalize().ljust(widths[i]).replace("_", "-") - print(headerline, file=f) + for i, name in enumerate(fieldnames): + headerline += name.capitalize().ljust(widths[i]).replace("_", "-") + print(headerline, file=csv_file) for alloc in allocators: for perm in bench.iterate_args(args=args): line = "" - for i, x in enumerate(rows[alloc][perm]): - line += str(x).ljust(widths[i]) - print(line.replace("_", "-"), file=f) + for i, row in enumerate(rows[alloc][perm]): + line += str(row).ljust(widths[i]) + print(line.replace("_", "-"), file=csv_file) def export_stats_to_dataref(bench, datapoint, path=None): """Write descriptive statistics about datapoint to dataref file""" @@ -243,9 +268,9 @@ def export_stats_to_dataref(bench, datapoint, path=None): # Example: \drefset{/mysql/glibc/40/Lower-whisker}{71552.0} line = "\\drefset{{/{}/{}/{}/{}}}{{{}}}" - with open(path, "w") as f: + with open(path, "w") as dataref_file: # Write facts to file - export_facts_to_file(bench, "%", f) + export_facts_to_file(bench, "%", dataref_file) for alloc in bench.results["allocators"]: for perm in bench.iterate_args(args=bench.results["args"]): @@ -257,10 +282,10 @@ def export_stats_to_dataref(bench, datapoint, path=None): cur_line.replace("[]", "") # Replace underscores cur_line.replace("_", "-") - print(cur_line, file=f) + print(cur_line, file=dataref_file) -def write_best_doublearg_tex_table(bench, evaluation, sort=">", - filepostfix="", sumdir="", std=False): +def write_best_doublearg_tex_table(bench, expr, sort=">", + filepostfix="", sumdir=""): args = bench.results["args"] keys = list(args.keys()) allocators = bench.results["allocators"] @@ -278,10 +303,8 @@ def write_best_doublearg_tex_table(bench, evaluation, sort=">", best = [] best_val = None for allocator in allocators: - d = [] - for m in bench.results[allocator][perm]: - d.append(eval(evaluation.format(**m))) - mean = np.mean(d) + mean = _eval_with_stat(bench, expr, allocator, perm, "mean") + if not best_val: best = [allocator] best_val = mean @@ -296,27 +319,26 @@ def write_best_doublearg_tex_table(bench, evaluation, sort=">", cell_text.append(row) fname = os.path.join(sumdir, ".".join([bench.name, filepostfix, "tex"])) - with open(fname, "w") as f: - print("\\documentclass{standalone}", file=f) - print("\\begin{document}", file=f) - print("\\begin{tabular}{|", end="", file=f) - print(" l |" * len(headers), "}", file=f) + with open(fname, "w") as tex_file: + print("\\documentclass{standalone}", file=tex_file) + print("\\begin{document}", file=tex_file) + print("\\begin{tabular}{|", end="", file=tex_file) + print(" l |" * len(headers), "}", file=tex_file) - print(header_arg+"/"+row_arg, end=" & ", file=f) + print(header_arg+"/"+row_arg, end=" & ", file=tex_file) for header in headers[:-1]: - print(header, end="& ", file=f) - print(headers[-1], "\\\\", file=f) + print(header, end="& ", file=tex_file) + print(headers[-1], "\\\\", file=tex_file) for i, row in enumerate(cell_text): - print(rows[i], end=" & ", file=f) - for e in row[:-1]: - print(e, end=" & ", file=f) - print(row[-1], "\\\\", file=f) - print("\\end{tabular}", file=f) - print("\\end{document}", file=f) - -def write_tex_table(bench, entries, sort=">", - filepostfix="", sumdir="", std=False): + print(rows[i], end=" & ", file=tex_file) + for entry in row[:-1]: + print(entry, end=" & ", file=tex_file) + print(row[-1], "\\\\", file=tex_file) + print("\\end{tabular}", file=tex_file) + print("\\end{document}", file=tex_file) + +def write_tex_table(bench, entries, filepostfix="", sumdir=""): """generate a latex standalone table from an list of entries dictionaries Entries must have at least the two keys: "label" and "expression". @@ -355,18 +377,18 @@ def write_tex_table(bench, entries, sort=">", entry_header_line = entry_header_line[:-1] + "\\\\" fname = os.path.join(sumdir, ".".join([bench.name, filepostfix, "tex"])) - with open(fname, "w") as f: - print("\\documentclass{standalone}", file=f) - print("\\usepackage{booktabs}", file=f) - print("\\usepackage{xcolor}", file=f) - print("\\begin{document}", file=f) - print("\\begin{tabular}{|", f"{'c|'*nperm_fields}", f"{'c'*nentries}|"*nallocators, "}", file=f) - print("\\toprule", file=f) - - print(alloc_header_line, file=f) - print("\\hline", file=f) - print(entry_header_line, file=f) - print("\\hline", file=f) + with open(fname, "w") as tex_file: + print("\\documentclass{standalone}", file=tex_file) + print("\\usepackage{booktabs}", file=tex_file) + print("\\usepackage{xcolor}", file=tex_file) + print("\\begin{document}", file=tex_file) + print("\\begin{tabular}{|", f"{'c|'*nperm_fields}", f"{'c'*nentries}|"*nallocators, "}", file=tex_file) + print("\\toprule", file=tex_file) + + print(alloc_header_line, file=tex_file) + print("\\hline", file=tex_file) + print(entry_header_line, file=tex_file) + print("\\hline", file=tex_file) for perm in bench.iterate_args(args=args): values = [[] for _ in entries] @@ -375,14 +397,14 @@ def write_tex_table(bench, entries, sort=">", for allocator in allocators: for i, entry in enumerate(entries): expr = entry["expression"] - values[i].append(eval(expr.format(**bench.results["stats"][allocator][perm]["mean"]))) + values[i].append(_eval_with_stat(bench, expr, allocator, perm, "mean")) # get max and min for each entry for i, entry in enumerate(entries): if not "sort" in entry: continue # bigger is better - elif entry["sort"] == ">": + if entry["sort"] == ">": maxs[i] = max(values[i]) mins[i] = min(values[i]) # smaller is better @@ -397,31 +419,32 @@ def write_tex_table(bench, entries, sort=">", row += str(perm_dict[field]) + "&" for i, _ in enumerate(allocators): - for y, entry_vals in enumerate(values): + for j, entry_vals in enumerate(values): val = entry_vals[i] # format val_str = str(val) - if type(val) == float: + if isinstance(val, float): val_str = f"{val:.2f}" # colorize - if val == maxs[y]: + if val == maxs[j]: val_str = f"\\textcolor{{green}}{{{val_str}}}" - elif val == mins[y]: + elif val == mins[j]: val_str = f"\\textcolor{{red}}{{{val_str}}}" row += f"{val_str} &" #escape _ for latex row = row.replace("_", "\\_") - print(row[:-1], "\\\\", file=f) + print(row[:-1], "\\\\", file=tex_file) - print("\\end{tabular}", file=f) - print("\\end{document}", file=f) + print("\\end{tabular}", file=tex_file) + print("\\end{document}", file=tex_file) def pgfplot_legend(bench, sumdir=""): + """create a standalone pgfplot legend""" allocators = bench.results["allocators"] - s =\ + tex =\ """ \\documentclass{standalone} \\usepackage{pgfplots} @@ -444,9 +467,9 @@ def pgfplot_legend(bench, sumdir=""): for alloc_name, alloc_dict in allocators.items(): # define color rgb = matplotlib.colors.to_rgb(alloc_dict["color"]) - s += f"\\providecolor{{{alloc_name}-color}}{{rgb}}{{{rgb[0]},{rgb[1]},{rgb[2]}}}\n" + tex += f"\\providecolor{{{alloc_name}-color}}{{rgb}}{{{rgb[0]},{rgb[1]},{rgb[2]}}}\n" - s +=\ + tex +=\ """ \\begin{document} \\begin{tikzpicture} @@ -459,60 +482,60 @@ def pgfplot_legend(bench, sumdir=""): alloc_list += f"{alloc_name}, " addlegendimage_list += "\t\\addlegendimage{}\n" - s += alloc_list[:-2] + "},\n]" - s += addlegendimage_list - s +=\ + tex += alloc_list[:-2] + "},\n]" + tex += addlegendimage_list + tex +=\ """ \\end{customlegend} \\end{tikzpicture} \\end{document}""" with open(os.path.join(sumdir, "legend.tex"), "w") as legend_file: - print(s, file=legend_file) + print(tex, file=legend_file) def pgfplot_linear(bench, perms, xval, yval, ylabel="'y-label'", xlabel="'x-label'", title="'default title'", postfix="", sumdir="", scale=None): allocators = bench.results["allocators"] perms = list(perms) - title = eval(title) - s =\ + title = title.format(**vars(), **vars(bench)) + tex =\ """\\documentclass{standalone} \\usepackage{pgfplots} \\usepackage{xcolor} """ for alloc_name, alloc_dict in allocators.items(): - s += f"\\begin{{filecontents*}}{{{alloc_name}.dat}}\n" - for i, perm in enumerate(perms): - x = _eval_with_stat(bench, xval, alloc_name, perm, "mean") - y = _eval_with_stat(bench, yval, alloc_name, perm, "mean") - s += f"{x} {y}\n" - s += "\\end{filecontents*}\n" + tex += f"\\begin{{filecontents*}}{{{alloc_name}.dat}}\n" + for perm in perms: + xval = _eval_with_stat(bench, xval, alloc_name, perm, "mean") + yval = _eval_with_stat(bench, yval, alloc_name, perm, "mean") + tex += f"{xval} {yval}\n" + tex += "\\end{filecontents*}\n" # define color rgb = matplotlib.colors.to_rgb(alloc_dict["color"]) - s += f"\\providecolor{{{alloc_name}-color}}{{rgb}}{{{rgb[0]},{rgb[1]},{rgb[2]}}}\n" + tex += f"\\providecolor{{{alloc_name}-color}}{{rgb}}{{{rgb[0]},{rgb[1]},{rgb[2]}}}\n" - s +=\ + tex +=\ f""" \\begin{{document}} \\begin{{tikzpicture}} \\begin{{axis}}[ \ttitle={{{title}}}, -\txlabel={{{eval(xlabel)}}}, -\tylabel={{{eval(ylabel)}}}, +\txlabel={{{xlabel}}}, +\tylabel={{{ylabel}}}, ] """ for alloc_name in allocators: - s += f"\\addplot [{alloc_name}-color] table {{{alloc_name}.dat}};\n" - # s += f"\t\\addplot table {{{alloc_name}.dat}};\n" + tex += f"\\addplot [{alloc_name}-color] table {{{alloc_name}.dat}};\n" + # tex += f"\t\\addplot table {{{alloc_name}.dat}};\n" - s +=\ + tex +=\ """\\end{axis} \\end{tikzpicture} \\end{document}""" with open(os.path.join(sumdir, f"{bench.name}.{postfix}.tex"), "w") as plot_file: - print(s, file=plot_file) + print(tex, file=plot_file) -- cgit v1.2.3 From 569b2b78baed903539895bd022ffe9d2e1576373 Mon Sep 17 00:00:00 2001 From: Florian Fischer Date: Tue, 11 Feb 2020 15:28:10 +0100 Subject: add summarize option to specify a custom latex preamble this is meant for some latex setup specifics like including common.tex --- src/plots.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/plots.py') diff --git a/src/plots.py b/src/plots.py index 1be0d31..d7fb0ea 100644 --- a/src/plots.py +++ b/src/plots.py @@ -469,6 +469,9 @@ def pgfplot_legend(bench, sumdir=""): rgb = matplotlib.colors.to_rgb(alloc_dict["color"]) tex += f"\\providecolor{{{alloc_name}-color}}{{rgb}}{{{rgb[0]},{rgb[1]},{rgb[2]}}}\n" + if src.globalvars.latex_custom_preamble: + tex += src.globalvars.latex_custom_preamble + "\n" + tex +=\ """ \\begin{document} @@ -517,6 +520,9 @@ def pgfplot_linear(bench, perms, xval, yval, ylabel="'y-label'", xlabel="'x-labe rgb = matplotlib.colors.to_rgb(alloc_dict["color"]) tex += f"\\providecolor{{{alloc_name}-color}}{{rgb}}{{{rgb[0]},{rgb[1]},{rgb[2]}}}\n" + if src.globalvars.latex_custom_preamble: + tex += src.globalvars.latex_custom_preamble + "\n" + tex +=\ f""" \\begin{{document}} -- cgit v1.2.3 From b4abe62aee1262051697702752a19b52ecfaca41 Mon Sep 17 00:00:00 2001 From: Florian Fischer Date: Tue, 11 Feb 2020 15:29:49 +0100 Subject: set any missing allocator color The matplotlib "C" color cycle list is used for missing colors --- src/plots.py | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) (limited to 'src/plots.py') diff --git a/src/plots.py b/src/plots.py index d7fb0ea..1542c49 100644 --- a/src/plots.py +++ b/src/plots.py @@ -31,6 +31,22 @@ from src.util import print_warn nan = np.NaN +def _get_alloc_color(bench, alloc): + """Populate all not set allocator colors with matplotlib 'C' colors""" + if isinstance(alloc, str): + alloc = bench.results["allocators"][alloc] + if alloc["color"] is None: + allocs = bench.results["allocators"] + explicit_colors = [v["color"] for v in allocs.values() if v["color"] is not None] + matplotlib_c_colors = ["C" + str(i) for i in range(0,16)] + avail_colors = [c for c in matplotlib_c_colors if c not in explicit_colors] + + for alloc in allocs.values(): + if alloc["color"] is None: + alloc["color"] = avail_colors.pop() + + return alloc["color"] + def _eval_with_stat(bench, evaluation, alloc, perm, stat): """Helper to evaluate a datapoint description string""" try: @@ -70,7 +86,7 @@ def plot_single_arg(bench, yval, ylabel="y-label", xlabel="x-label", y_vals.append(_eval_with_stat(bench, yval, allocator, perm, "mean")) plt.plot(x_vals, y_vals, marker='.', linestyle='-', - label=allocator, color=allocators[allocator]["color"]) + label=allocator, color=_get_alloc_color(bench, allocator)) plt.legend(loc="best") if not autoticks: @@ -127,7 +143,7 @@ def barplot_single_arg(bench, yval, ylabel="'y-label'", xlabel="'x-label'", y_errs.append(_eval_with_stat(bench, yval, allocator, perm, "std")) plt.bar(x_vals, y_vals, width=1, label=allocator, yerr=y_errs, - color=allocators[allocator]["color"]) + color=_get_alloc_color(bench, allocator)) plt.legend(loc="best") plt.xticks(list(range(int(np.floor(nallocators/2)), narg*(nallocators+1), nallocators+1)), arg) @@ -174,7 +190,7 @@ def plot_fixed_arg(bench, yval, ylabel="'y-label'", xlabel="loose_arg", y_vals.append(_eval_with_stat(bench, yval, allocator, perm, "mean")) plt.plot(x_vals, y_vals, marker='.', linestyle='-', - label=allocator, color=allocators[allocator]["color"]) + label=allocator, color=_get_alloc_color(bench, allocator)) plt.legend(loc="best") if not autoticks: @@ -466,7 +482,7 @@ def pgfplot_legend(bench, sumdir=""): for alloc_name, alloc_dict in allocators.items(): # define color - rgb = matplotlib.colors.to_rgb(alloc_dict["color"]) + rgb = matplotlib.colors.to_rgb(_get_alloc_color(bench, alloc_dict)) tex += f"\\providecolor{{{alloc_name}-color}}{{rgb}}{{{rgb[0]},{rgb[1]},{rgb[2]}}}\n" if src.globalvars.latex_custom_preamble: @@ -483,7 +499,7 @@ def pgfplot_legend(bench, sumdir=""): addlegendimage_list = "" for alloc_name in allocators: alloc_list += f"{alloc_name}, " - addlegendimage_list += "\t\\addlegendimage{}\n" + addlegendimage_list += f"\t\\addlegendimage{{color={alloc_name}-color}}\n" tex += alloc_list[:-2] + "},\n]" tex += addlegendimage_list @@ -517,7 +533,7 @@ def pgfplot_linear(bench, perms, xval, yval, ylabel="'y-label'", xlabel="'x-labe tex += "\\end{filecontents*}\n" # define color - rgb = matplotlib.colors.to_rgb(alloc_dict["color"]) + rgb = matplotlib.colors.to_rgb(_get_alloc_color(bench, alloc_dict)) tex += f"\\providecolor{{{alloc_name}-color}}{{rgb}}{{{rgb[0]},{rgb[1]},{rgb[2]}}}\n" if src.globalvars.latex_custom_preamble: -- cgit v1.2.3 From 910bd14c7c1feeeac75eda8198dd963c8eee1c90 Mon Sep 17 00:00:00 2001 From: Florian Fischer Date: Tue, 11 Feb 2020 15:31:26 +0100 Subject: add legend file name to pgfplot_legend arguments --- src/plots.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/plots.py') diff --git a/src/plots.py b/src/plots.py index 1542c49..12ffefd 100644 --- a/src/plots.py +++ b/src/plots.py @@ -456,7 +456,7 @@ def write_tex_table(bench, entries, filepostfix="", sumdir=""): print("\\end{tabular}", file=tex_file) print("\\end{document}", file=tex_file) -def pgfplot_legend(bench, sumdir=""): +def pgfplot_legend(bench, sumdir="", file_name="pgfplot_legend"): """create a standalone pgfplot legend""" allocators = bench.results["allocators"] @@ -509,7 +509,7 @@ def pgfplot_legend(bench, sumdir=""): \\end{tikzpicture} \\end{document}""" - with open(os.path.join(sumdir, "legend.tex"), "w") as legend_file: + with open(os.path.join(sumdir, f"{file_name}.tex"), "w") as legend_file: print(tex, file=legend_file) def pgfplot_linear(bench, perms, xval, yval, ylabel="'y-label'", xlabel="'x-label'", -- cgit v1.2.3 From 7ff3e52a6516c3dcad91fbc6da57c78d3bbc1f31 Mon Sep 17 00:00:00 2001 From: Florian Fischer Date: Tue, 11 Feb 2020 15:32:07 +0100 Subject: use str.format labels and fix value retrieval in pgfplot_linear --- src/plots.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'src/plots.py') diff --git a/src/plots.py b/src/plots.py index 12ffefd..0c6d4f9 100644 --- a/src/plots.py +++ b/src/plots.py @@ -512,8 +512,8 @@ def pgfplot_legend(bench, sumdir="", file_name="pgfplot_legend"): with open(os.path.join(sumdir, f"{file_name}.tex"), "w") as legend_file: print(tex, file=legend_file) -def pgfplot_linear(bench, perms, xval, yval, ylabel="'y-label'", xlabel="'x-label'", - title="'default title'", postfix="", sumdir="", scale=None): +def pgfplot_linear(bench, perms, xexpr, yexpr, ylabel="y-label", xlabel="x-label", + title="default title", postfix="", sumdir="", scale=None): allocators = bench.results["allocators"] perms = list(perms) @@ -527,8 +527,8 @@ def pgfplot_linear(bench, perms, xval, yval, ylabel="'y-label'", xlabel="'x-labe for alloc_name, alloc_dict in allocators.items(): tex += f"\\begin{{filecontents*}}{{{alloc_name}.dat}}\n" for perm in perms: - xval = _eval_with_stat(bench, xval, alloc_name, perm, "mean") - yval = _eval_with_stat(bench, yval, alloc_name, perm, "mean") + xval = _eval_with_stat(bench, xexpr, alloc_name, perm, "mean") + yval = _eval_with_stat(bench, yexpr, alloc_name, perm, "mean") tex += f"{xval} {yval}\n" tex += "\\end{filecontents*}\n" @@ -539,6 +539,11 @@ def pgfplot_linear(bench, perms, xval, yval, ylabel="'y-label'", xlabel="'x-labe if src.globalvars.latex_custom_preamble: tex += src.globalvars.latex_custom_preamble + "\n" + label_substitutions = vars() + label_substitutions.update(vars(bench)) + xlabel = xlabel.format(**label_substitutions) + ylabel = ylabel.format(**label_substitutions) + title = title.format(**label_substitutions) tex +=\ f""" \\begin{{document}} -- cgit v1.2.3 From 0168c475446f32e4ff3ebd92668d17d7cbd346c9 Mon Sep 17 00:00:00 2001 From: Florian Fischer Date: Tue, 11 Feb 2020 15:40:19 +0100 Subject: improve default labels --- src/plots.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/plots.py') diff --git a/src/plots.py b/src/plots.py index 0c6d4f9..8a73fa0 100644 --- a/src/plots.py +++ b/src/plots.py @@ -103,8 +103,8 @@ def plot_single_arg(bench, yval, ylabel="y-label", xlabel="x-label", plt.savefig(figname) plt.clf() -def barplot_single_arg(bench, yval, ylabel="'y-label'", xlabel="'x-label'", - title="'default title'", filepostfix="", sumdir="", +def barplot_single_arg(bench, yval, ylabel="y-label", xlabel="x-label", + title="default title", filepostfix="", sumdir="", arg="", scale=None, file_ext=src.globalvars.summary_file_ext, yerr=True): """plot bar plots for each permutation of the benchmark's command arguments""" @@ -160,8 +160,8 @@ def barplot_single_arg(bench, yval, ylabel="'y-label'", xlabel="'x-label'", plt.savefig(figname) plt.clf() -def plot_fixed_arg(bench, yval, ylabel="'y-label'", xlabel="loose_arg", - autoticks=True, title="'default title'", filepostfix="", +def plot_fixed_arg(bench, yval, ylabel="y-label", xlabel="{loose_arg}", + autoticks=True, title="default title", filepostfix="", sumdir="", fixed=None, file_ext=src.globalvars.summary_file_ext, scale=None): args = bench.results["args"] -- cgit v1.2.3 From 40860ce5b8134618dad6457049d533aa211b0139 Mon Sep 17 00:00:00 2001 From: Florian Fischer Date: Wed, 12 Feb 2020 14:44:28 +0100 Subject: define a style per allocator the style is named like the allocator and can be overwritten using a custom preamble. --- src/plots.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src/plots.py') diff --git a/src/plots.py b/src/plots.py index 8a73fa0..8345181 100644 --- a/src/plots.py +++ b/src/plots.py @@ -484,6 +484,7 @@ def pgfplot_legend(bench, sumdir="", file_name="pgfplot_legend"): # define color rgb = matplotlib.colors.to_rgb(_get_alloc_color(bench, alloc_dict)) tex += f"\\providecolor{{{alloc_name}-color}}{{rgb}}{{{rgb[0]},{rgb[1]},{rgb[2]}}}\n" + tex += f"\\pgfplotsset{{{alloc_name}/.style={{color={alloc_name}-color}}}}\n\n" if src.globalvars.latex_custom_preamble: tex += src.globalvars.latex_custom_preamble + "\n" @@ -499,7 +500,7 @@ def pgfplot_legend(bench, sumdir="", file_name="pgfplot_legend"): addlegendimage_list = "" for alloc_name in allocators: alloc_list += f"{alloc_name}, " - addlegendimage_list += f"\t\\addlegendimage{{color={alloc_name}-color}}\n" + addlegendimage_list += f"\t\\addlegendimage{{{alloc_name}}}\n" tex += alloc_list[:-2] + "},\n]" tex += addlegendimage_list @@ -535,6 +536,7 @@ def pgfplot_linear(bench, perms, xexpr, yexpr, ylabel="y-label", xlabel="x-label # define color rgb = matplotlib.colors.to_rgb(_get_alloc_color(bench, alloc_dict)) tex += f"\\providecolor{{{alloc_name}-color}}{{rgb}}{{{rgb[0]},{rgb[1]},{rgb[2]}}}\n" + tex += f"\\pgfplotsset{{{alloc_name}/.style={{color={alloc_name}-color}}}}\n\n" if src.globalvars.latex_custom_preamble: tex += src.globalvars.latex_custom_preamble + "\n" @@ -556,8 +558,8 @@ f""" """ for alloc_name in allocators: - tex += f"\\addplot [{alloc_name}-color] table {{{alloc_name}.dat}};\n" - # tex += f"\t\\addplot table {{{alloc_name}.dat}};\n" + # tex += f"\\addplot [{alloc_name}-color] table {{{alloc_name}.dat}};\n" + tex += f"\t\\addplot+[{alloc_name}] table {{{alloc_name}.dat}};\n" tex +=\ """\\end{axis} -- cgit v1.2.3