Skip to content

export

export

Export utilities for experiment results.

Provides functions for exporting results to CSV, JSON, LaTeX tables, and markdown, suitable for academic publications.

export_to_csv(results, filepath, metric='squared_error', include_timeseries=False)

Export results to CSV file.

Args: results: Dictionary mapping config name to AggregatedResults filepath: Path to output CSV file metric: Metric to export include_timeseries: Whether to include full timeseries (large!)

Source code in src/alberta_framework/utils/export.py
def export_to_csv(
    results: dict[str, "AggregatedResults"],
    filepath: str | Path,
    metric: str = "squared_error",
    include_timeseries: bool = False,
) -> None:
    """Export results to CSV file.

    Args:
        results: Dictionary mapping config name to AggregatedResults
        filepath: Path to output CSV file
        metric: Metric to export
        include_timeseries: Whether to include full timeseries (large!)
    """
    filepath = Path(filepath)
    filepath.parent.mkdir(parents=True, exist_ok=True)

    if include_timeseries:
        _export_timeseries_csv(results, filepath, metric)
    else:
        _export_summary_csv(results, filepath, metric)

export_to_json(results, filepath, include_timeseries=False)

Export results to JSON file.

Args: results: Dictionary mapping config name to AggregatedResults filepath: Path to output JSON file include_timeseries: Whether to include full timeseries (large!)

Source code in src/alberta_framework/utils/export.py
def export_to_json(
    results: dict[str, "AggregatedResults"],
    filepath: str | Path,
    include_timeseries: bool = False,
) -> None:
    """Export results to JSON file.

    Args:
        results: Dictionary mapping config name to AggregatedResults
        filepath: Path to output JSON file
        include_timeseries: Whether to include full timeseries (large!)
    """
    filepath = Path(filepath)
    filepath.parent.mkdir(parents=True, exist_ok=True)

    from typing import Any

    data: dict[str, Any] = {}
    for name, agg in results.items():
        summary_data: dict[str, dict[str, Any]] = {}
        for metric_name, summary in agg.summary.items():
            summary_data[metric_name] = {
                "mean": summary.mean,
                "std": summary.std,
                "min": summary.min,
                "max": summary.max,
                "n_seeds": summary.n_seeds,
                "values": summary.values.tolist(),
            }

        config_data: dict[str, Any] = {
            "seeds": agg.seeds,
            "summary": summary_data,
        }

        if include_timeseries:
            config_data["timeseries"] = {
                metric: arr.tolist() for metric, arr in agg.metric_arrays.items()
            }

        data[name] = config_data

    with open(filepath, "w") as f:
        json.dump(data, f, indent=2)

generate_latex_table(results, significance_results=None, metric='squared_error', caption='Experimental Results', label='tab:results', metric_label='Error', lower_is_better=True)

Generate a LaTeX table of results.

Args: results: Dictionary mapping config name to AggregatedResults significance_results: Optional pairwise significance test results metric: Metric to display caption: Table caption label: LaTeX label for the table metric_label: Human-readable name for the metric lower_is_better: Whether lower metric values are better

Returns: LaTeX table as a string

Source code in src/alberta_framework/utils/export.py
def generate_latex_table(
    results: dict[str, "AggregatedResults"],
    significance_results: dict[tuple[str, str], "SignificanceResult"] | None = None,
    metric: str = "squared_error",
    caption: str = "Experimental Results",
    label: str = "tab:results",
    metric_label: str = "Error",
    lower_is_better: bool = True,
) -> str:
    """Generate a LaTeX table of results.

    Args:
        results: Dictionary mapping config name to AggregatedResults
        significance_results: Optional pairwise significance test results
        metric: Metric to display
        caption: Table caption
        label: LaTeX label for the table
        metric_label: Human-readable name for the metric
        lower_is_better: Whether lower metric values are better

    Returns:
        LaTeX table as a string
    """
    lines = []
    lines.append(r"\begin{table}[ht]")
    lines.append(r"\centering")
    lines.append(r"\caption{" + caption + "}")
    lines.append(r"\label{" + label + "}")
    lines.append(r"\begin{tabular}{lcc}")
    lines.append(r"\toprule")
    lines.append(r"Method & " + metric_label + r" & Seeds \\")
    lines.append(r"\midrule")

    # Find best result
    summaries = {name: agg.summary[metric] for name, agg in results.items()}
    if lower_is_better:
        best_name = min(summaries.keys(), key=lambda k: summaries[k].mean)
    else:
        best_name = max(summaries.keys(), key=lambda k: summaries[k].mean)

    for name, agg in results.items():
        summary = agg.summary[metric]
        mean_str = f"{summary.mean:.4f}"
        std_str = f"{summary.std:.4f}"

        # Bold if best
        if name == best_name:
            value_str = rf"\textbf{{{mean_str}}} $\pm$ {std_str}"
        else:
            value_str = rf"{mean_str} $\pm$ {std_str}"

        # Add significance marker if provided
        if significance_results:
            sig_marker = _get_significance_marker(name, best_name, significance_results)
            value_str += sig_marker

        # Escape underscores in method name
        escaped_name = name.replace("_", r"\_")
        lines.append(rf"{escaped_name} & {value_str} & {summary.n_seeds} \\")

    lines.append(r"\bottomrule")
    lines.append(r"\end{tabular}")

    if significance_results:
        lines.append(r"\vspace{0.5em}")
        lines.append(r"\footnotesize{$^*$ $p < 0.05$, $^{**}$ $p < 0.01$, $^{***}$ $p < 0.001$}")

    lines.append(r"\end{table}")

    return "\n".join(lines)

generate_markdown_table(results, significance_results=None, metric='squared_error', metric_label='Error', lower_is_better=True)

Generate a markdown table of results.

Args: results: Dictionary mapping config name to AggregatedResults significance_results: Optional pairwise significance test results metric: Metric to display metric_label: Human-readable name for the metric lower_is_better: Whether lower metric values are better

Returns: Markdown table as a string

Source code in src/alberta_framework/utils/export.py
def generate_markdown_table(
    results: dict[str, "AggregatedResults"],
    significance_results: dict[tuple[str, str], "SignificanceResult"] | None = None,
    metric: str = "squared_error",
    metric_label: str = "Error",
    lower_is_better: bool = True,
) -> str:
    """Generate a markdown table of results.

    Args:
        results: Dictionary mapping config name to AggregatedResults
        significance_results: Optional pairwise significance test results
        metric: Metric to display
        metric_label: Human-readable name for the metric
        lower_is_better: Whether lower metric values are better

    Returns:
        Markdown table as a string
    """
    lines = []
    lines.append(f"| Method | {metric_label} (mean ± std) | Seeds |")
    lines.append("|--------|-------------------------|-------|")

    # Find best result
    summaries = {name: agg.summary[metric] for name, agg in results.items()}
    if lower_is_better:
        best_name = min(summaries.keys(), key=lambda k: summaries[k].mean)
    else:
        best_name = max(summaries.keys(), key=lambda k: summaries[k].mean)

    for name, agg in results.items():
        summary = agg.summary[metric]
        mean_str = f"{summary.mean:.4f}"
        std_str = f"{summary.std:.4f}"

        # Bold if best
        if name == best_name:
            value_str = f"**{mean_str}** ± {std_str}"
        else:
            value_str = f"{mean_str} ± {std_str}"

        # Add significance marker if provided
        if significance_results:
            sig_marker = _get_md_significance_marker(name, best_name, significance_results)
            value_str += sig_marker

        lines.append(f"| {name} | {value_str} | {summary.n_seeds} |")

    if significance_results:
        lines.append("")
        lines.append("\\* p < 0.05, \\*\\* p < 0.01, \\*\\*\\* p < 0.001")

    return "\n".join(lines)

generate_significance_table(significance_results, format='latex')

Generate a table of pairwise significance results.

Args: significance_results: Pairwise significance test results format: Output format ("latex" or "markdown")

Returns: Formatted table as string

Source code in src/alberta_framework/utils/export.py
def generate_significance_table(
    significance_results: dict[tuple[str, str], "SignificanceResult"],
    format: str = "latex",
) -> str:
    """Generate a table of pairwise significance results.

    Args:
        significance_results: Pairwise significance test results
        format: Output format ("latex" or "markdown")

    Returns:
        Formatted table as string
    """
    if format == "latex":
        return _generate_significance_latex(significance_results)
    else:
        return _generate_significance_markdown(significance_results)

save_experiment_report(results, output_dir, experiment_name, significance_results=None, metric='squared_error')

Save a complete experiment report with all artifacts.

Args: results: Dictionary mapping config name to AggregatedResults output_dir: Directory to save artifacts experiment_name: Name for the experiment (used in filenames) significance_results: Optional pairwise significance test results metric: Primary metric to report

Returns: Dictionary mapping artifact type to file path

Source code in src/alberta_framework/utils/export.py
def save_experiment_report(
    results: dict[str, "AggregatedResults"],
    output_dir: str | Path,
    experiment_name: str,
    significance_results: dict[tuple[str, str], "SignificanceResult"] | None = None,
    metric: str = "squared_error",
) -> dict[str, Path]:
    """Save a complete experiment report with all artifacts.

    Args:
        results: Dictionary mapping config name to AggregatedResults
        output_dir: Directory to save artifacts
        experiment_name: Name for the experiment (used in filenames)
        significance_results: Optional pairwise significance test results
        metric: Primary metric to report

    Returns:
        Dictionary mapping artifact type to file path
    """
    output_dir = Path(output_dir)
    output_dir.mkdir(parents=True, exist_ok=True)

    artifacts: dict[str, Path] = {}

    # Export summary CSV
    csv_path = output_dir / f"{experiment_name}_summary.csv"
    export_to_csv(results, csv_path, metric=metric)
    artifacts["summary_csv"] = csv_path

    # Export JSON
    json_path = output_dir / f"{experiment_name}_results.json"
    export_to_json(results, json_path, include_timeseries=False)
    artifacts["json"] = json_path

    # Generate LaTeX table
    latex_path = output_dir / f"{experiment_name}_table.tex"
    latex_content = generate_latex_table(
        results,
        significance_results=significance_results,
        metric=metric,
        caption=f"{experiment_name} Results",
        label=f"tab:{experiment_name}",
    )
    with open(latex_path, "w") as f:
        f.write(latex_content)
    artifacts["latex_table"] = latex_path

    # Generate markdown table
    md_path = output_dir / f"{experiment_name}_table.md"
    md_content = generate_markdown_table(
        results,
        significance_results=significance_results,
        metric=metric,
    )
    with open(md_path, "w") as f:
        f.write(md_content)
    artifacts["markdown_table"] = md_path

    # If significance results provided, save those too
    if significance_results:
        sig_latex_path = output_dir / f"{experiment_name}_significance.tex"
        sig_latex = generate_significance_table(significance_results, format="latex")
        with open(sig_latex_path, "w") as f:
            f.write(sig_latex)
        artifacts["significance_latex"] = sig_latex_path

        sig_md_path = output_dir / f"{experiment_name}_significance.md"
        sig_md = generate_significance_table(significance_results, format="markdown")
        with open(sig_md_path, "w") as f:
            f.write(sig_md)
        artifacts["significance_md"] = sig_md_path

    return artifacts

results_to_dataframe(results, metric='squared_error')

Convert results to a pandas DataFrame.

Requires pandas to be installed.

Args: results: Dictionary mapping config name to AggregatedResults metric: Metric to include

Returns: DataFrame with results

Source code in src/alberta_framework/utils/export.py
def results_to_dataframe(
    results: dict[str, "AggregatedResults"],
    metric: str = "squared_error",
) -> "pd.DataFrame":
    """Convert results to a pandas DataFrame.

    Requires pandas to be installed.

    Args:
        results: Dictionary mapping config name to AggregatedResults
        metric: Metric to include

    Returns:
        DataFrame with results
    """
    try:
        import pandas as pd
    except ImportError:
        raise ImportError("pandas is required. Install with: pip install pandas")

    rows = []
    for name, agg in results.items():
        summary = agg.summary[metric]
        rows.append(
            {
                "method": name,
                "mean": summary.mean,
                "std": summary.std,
                "min": summary.min,
                "max": summary.max,
                "n_seeds": summary.n_seeds,
            }
        )

    return pd.DataFrame(rows)