From ae98b68cf549f15c6cfbecb8f9941085b217bf2f Mon Sep 17 00:00:00 2001
From: EgeKaraismailogluQC
<128645043+EgeKaraismailogluQC@users.noreply.github.com>
Date: Thu, 30 Apr 2026 16:03:23 +0200
Subject: [PATCH 01/19] wip
---
diffly/metrics.py | 70 +++++++++++++++++++++++++++++++++++++++++++
diffly/summary.py | 50 +++++++++++++++++++++++++++++++
tests/test_metrics.py | 69 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 189 insertions(+)
create mode 100644 diffly/metrics.py
create mode 100644 tests/test_metrics.py
diff --git a/diffly/metrics.py b/diffly/metrics.py
new file mode 100644
index 0000000..e1d3705
--- /dev/null
+++ b/diffly/metrics.py
@@ -0,0 +1,70 @@
+# Copyright (c) QuantCo 2025-2026
+# SPDX-License-Identifier: BSD-3-Clause
+
+from __future__ import annotations
+
+from collections.abc import Callable
+
+import polars as pl
+
+Metric = Callable[[pl.Expr, pl.Expr], pl.Expr]
+"""A metric is a callable mapping ``(left_expr, right_expr)`` to a scalar
+aggregation expression. The expressions refer to the left-side and right-side
+values of a single column across all joined rows."""
+
+
+def mean(left: pl.Expr, right: pl.Expr) -> pl.Expr:
+ """Mean of ``right - left``."""
+ return (right - left).mean()
+
+
+def median(left: pl.Expr, right: pl.Expr) -> pl.Expr:
+ """Median of ``right - left``."""
+ return (right - left).median()
+
+
+def min(left: pl.Expr, right: pl.Expr) -> pl.Expr:
+ """Minimum of ``right - left``."""
+ return (right - left).min()
+
+
+def max(left: pl.Expr, right: pl.Expr) -> pl.Expr:
+ """Maximum of ``right - left``."""
+ return (right - left).max()
+
+
+def std(left: pl.Expr, right: pl.Expr) -> pl.Expr:
+ """Standard deviation of ``right - left``."""
+ return (right - left).std()
+
+
+def mean_absolute_deviation(left: pl.Expr, right: pl.Expr) -> pl.Expr:
+ """Mean of ``|right - left|``."""
+ return (right - left).abs().mean()
+
+
+def mean_relative_deviation(left: pl.Expr, right: pl.Expr) -> pl.Expr:
+ """Mean of ``|(right - left) / left|``. Yields ``inf`` or ``null`` where
+ ``left`` is zero."""
+ return ((right - left) / left).abs().mean()
+
+
+def quantile(q: float) -> Metric:
+ """Factory returning a metric that computes the ``q``-quantile of
+ ``right - left``."""
+
+ def _quantile(left: pl.Expr, right: pl.Expr) -> pl.Expr:
+ return (right - left).quantile(q)
+
+ return _quantile
+
+
+_PRESETS: dict[str, Metric] = {
+ "mean": mean,
+ "median": median,
+ "min": min,
+ "max": max,
+ "std": std,
+ "mean_absolute_deviation": mean_absolute_deviation,
+ "mean_relative_deviation": mean_relative_deviation,
+}
diff --git a/diffly/summary.py b/diffly/summary.py
index d4dc343..cfa9ed1 100644
--- a/diffly/summary.py
+++ b/diffly/summary.py
@@ -6,11 +6,13 @@
import dataclasses
import io
import json
+from collections.abc import Mapping
from dataclasses import dataclass
from datetime import date, datetime, timedelta
from decimal import Decimal
from typing import TYPE_CHECKING, Any, Literal, cast
+import polars as pl
from rich import box
from rich.columns import Columns as RichColumns
from rich.console import Console, Group, RenderableType
@@ -20,6 +22,7 @@
from rich.text import Text
from ._utils import Side, capitalize_first
+from .metrics import Metric
if TYPE_CHECKING: # pragma: no cover
from .comparison import DataFrameComparison
@@ -57,6 +60,7 @@ def __init__(
right_name: str,
slim: bool,
hidden_columns: list[str] | None,
+ metrics: Mapping[str, Metric] | None,
):
self.slim = slim
self._data = _compute_summary_data(
@@ -69,6 +73,7 @@ def __init__(
right_name=right_name,
slim=slim,
hidden_columns=hidden_columns,
+ metrics=metrics,
)
def format(self, pretty: bool | None = None) -> str:
@@ -703,6 +708,7 @@ class SummaryDataColumn:
match_rate: float
n_total_changes: int
changes: list[SummaryDataColumnChange] | None
+ metrics: dict[str, Any] | None = None
@dataclass
@@ -720,6 +726,7 @@ class SummaryData:
_other_common_columns: list[str]
_truncated_left_name: str
_truncated_right_name: str
+ _metric_labels: list[str] = dataclasses.field(default_factory=list)
def to_dict(self) -> dict[str, Any]:
def _convert(obj: Any) -> Any:
@@ -758,6 +765,7 @@ def _compute_summary_data(
right_name: str,
slim: bool,
hidden_columns: list[str] | None,
+ metrics: Mapping[str, Metric] | None,
) -> SummaryData:
from .comparison import DataFrameComparison
@@ -823,6 +831,10 @@ def _validate_primary_key_hidden_columns() -> None:
_truncated_right_name=truncated_right,
)
+ metrics_resolved: dict[str, Metric] = dict(metrics or {})
+ metrics_by_column = _compute_column_metrics(comp, metrics_resolved)
+ metric_labels = list(metrics_resolved.keys())
+
schemas = _compute_schemas(comp, slim)
rows = _compute_rows(comp, slim)
columns = _compute_columns(
@@ -831,6 +843,7 @@ def _validate_primary_key_hidden_columns() -> None:
show_perfect_column_matches,
top_k_changes_by_column,
show_sample_primary_key_per_change,
+ metrics_by_column,
)
sample_rows_left_only, sample_rows_right_only = _compute_sample_rows(
comp, sample_k_rows_only
@@ -850,6 +863,7 @@ def _validate_primary_key_hidden_columns() -> None:
_other_common_columns=comp._other_common_columns,
_truncated_left_name=truncated_left,
_truncated_right_name=truncated_right,
+ _metric_labels=metric_labels,
)
@@ -911,12 +925,47 @@ def _compute_rows(comp: DataFrameComparison, slim: bool) -> SummaryDataRows | No
)
+def _compute_column_metrics(
+ comp: DataFrameComparison,
+ metrics: Mapping[str, Metric],
+) -> dict[str, dict[str, Any]]:
+ if not metrics:
+ return {}
+ if comp.primary_key is None or comp.num_rows_joined() == 0:
+ return {}
+
+ numeric_cols = [
+ c
+ for c in comp._other_common_columns
+ if comp.left_schema[c].is_numeric() and comp.right_schema[c].is_numeric()
+ ]
+ out: dict[str, dict[str, Any]] = {c: {} for c in numeric_cols}
+ if not numeric_cols:
+ return out
+
+ joined = comp.joined(lazy=True)
+ agg_exprs = [
+ metric(
+ pl.col(f"{c}_{Side.LEFT}"),
+ pl.col(f"{c}_{Side.RIGHT}"),
+ ).alias(f"{label}__{c}")
+ for label, metric in metrics.items()
+ for c in numeric_cols
+ ]
+ row = joined.select(agg_exprs).collect().row(0, named=True)
+ for c in numeric_cols:
+ for label in metrics:
+ out[c][label] = row[f"{label}__{c}"]
+ return out
+
+
def _compute_columns(
comp: DataFrameComparison,
slim: bool,
show_perfect_column_matches: bool,
top_k_changes_by_column: dict[str, int],
show_sample_primary_key_per_change: bool,
+ metrics_by_column: dict[str, dict[str, Any]],
) -> list[SummaryDataColumn] | None:
# NOTE: We can only compute column matches if there are primary key columns and at
# least one joined row.
@@ -963,6 +1012,7 @@ def _compute_columns(
match_rate=rate,
n_total_changes=n_total_changes,
changes=changes,
+ metrics=metrics_by_column.get(col_name),
)
)
return columns
diff --git a/tests/test_metrics.py b/tests/test_metrics.py
new file mode 100644
index 0000000..2239510
--- /dev/null
+++ b/tests/test_metrics.py
@@ -0,0 +1,69 @@
+# Copyright (c) QuantCo 2025-2026
+# SPDX-License-Identifier: BSD-3-Clause
+
+import math
+
+import polars as pl
+import pytest
+
+from diffly import metrics
+
+
+@pytest.fixture
+def frame() -> pl.DataFrame:
+ # deltas (right - left): [0, 0, 2, null]
+ return pl.DataFrame({"l": [1, 2, 3, None], "r": [1, 2, 5, 4]})
+
+
+def _apply(metric, frame: pl.DataFrame) -> float:
+ return frame.select(metric(pl.col("l"), pl.col("r"))).item()
+
+
+def test_mean(frame: pl.DataFrame) -> None:
+ assert _apply(metrics.mean, frame) == pytest.approx(2 / 3)
+
+
+def test_median(frame: pl.DataFrame) -> None:
+ assert _apply(metrics.median, frame) == 0
+
+
+def test_min(frame: pl.DataFrame) -> None:
+ assert _apply(metrics.min, frame) == 0
+
+
+def test_max(frame: pl.DataFrame) -> None:
+ assert _apply(metrics.max, frame) == 2
+
+
+def test_std(frame: pl.DataFrame) -> None:
+ sample_mean = 2 / 3
+ assert _apply(metrics.std, frame) == pytest.approx(
+ math.sqrt(
+ ((0 - sample_mean) ** 2 + (0 - sample_mean) ** 2 + (2 - sample_mean) ** 2)
+ / 2
+ )
+ )
+
+
+def test_mean_absolute_deviation() -> None:
+ # deltas: [-1, 0, 2, null]; |deltas|: [1, 0, 2, null]; mean = 1.0
+ frame = pl.DataFrame({"l": [2, 2, 3, None], "r": [1, 2, 5, 4]})
+ assert _apply(metrics.mean_absolute_deviation, frame) == pytest.approx(1.0)
+
+
+def test_mean_relative_deviation() -> None:
+ # left: [1, 2, 4, None]; delta: [0, 0, 2, null]; rel: [0, 0, 0.5, null]; mean = 1/6
+ frame = pl.DataFrame({"l": [1, 2, 4, None], "r": [1, 2, 6, 4]})
+ assert _apply(metrics.mean_relative_deviation, frame) == pytest.approx(1 / 6)
+
+
+def test_mean_relative_deviation_div_by_zero() -> None:
+ # Matches numpy: x/0 -> inf, so .abs().mean() -> inf
+ frame = pl.DataFrame({"l": [0.0, 1.0], "r": [1.0, 1.0]})
+ assert math.isinf(_apply(metrics.mean_relative_deviation, frame))
+
+
+def test_quantile(frame: pl.DataFrame) -> None:
+ # deltas [0, 0, 2]: p50 = 0, p100 = 2
+ assert _apply(metrics.quantile(0.5), frame) == 0
+ assert _apply(metrics.quantile(1.0), frame) == 2
From 31bcd399c16e9a215fe74cb8dcd1f30666bcbf01 Mon Sep 17 00:00:00 2001
From: EgeKaraismailogluQC
<128645043+EgeKaraismailogluQC@users.noreply.github.com>
Date: Thu, 30 Apr 2026 16:27:27 +0200
Subject: [PATCH 02/19] wip
---
diffly/cli.py | 18 ++++++++++++++++++
diffly/comparison.py | 10 ++++++++++
diffly/summary.py | 22 ++++++++++++++++++++--
diffly/testing.py | 11 +++++++++++
tests/summary/test_summary.py | 9 ++++++++-
5 files changed, 67 insertions(+), 3 deletions(-)
diff --git a/diffly/cli.py b/diffly/cli.py
index 002af8d..e86b264 100644
--- a/diffly/cli.py
+++ b/diffly/cli.py
@@ -11,6 +11,7 @@
from ._compat import typer
from ._utils import ABS_TOL_DEFAULT, ABS_TOL_TEMPORAL_DEFAULT, REL_TOL_DEFAULT
+from .metrics import _PRESETS
app = typer.Typer()
@@ -129,9 +130,25 @@ def main(
)
),
] = [],
+ metric: Annotated[
+ list[str],
+ typer.Option(
+ help=(
+ "Metric presets to display per numerical column. Repeatable. "
+ f"Available: {', '.join(_PRESETS)}."
+ )
+ ),
+ ] = [],
) -> None:
"""Compare two `parquet` files and print the comparison result."""
+ for name in metric:
+ if name not in _PRESETS:
+ raise typer.BadParameter(
+ f"Unknown metric: {name!r}. Available: {', '.join(_PRESETS)}."
+ )
+ metrics = {name: _PRESETS[name] for name in metric}
+
comparison = compare_frames(
pl.scan_parquet(left),
pl.scan_parquet(right),
@@ -149,6 +166,7 @@ def main(
right_name=right_name,
slim=slim,
hidden_columns=hidden_columns,
+ metrics=metrics,
)
if output_json:
typer.echo(summary.to_json())
diff --git a/diffly/comparison.py b/diffly/comparison.py
index becf915..6f8b9c0 100644
--- a/diffly/comparison.py
+++ b/diffly/comparison.py
@@ -15,6 +15,7 @@
from ._cache import cached_method
from ._conditions import condition_equal_columns, condition_equal_rows
from ._exceptions import PrimaryKeyError
+from .metrics import Metric
from ._utils import (
ABS_TOL_DEFAULT,
ABS_TOL_TEMPORAL_DEFAULT,
@@ -919,6 +920,7 @@ def summary(
right_name: str = Side.RIGHT,
slim: bool = False,
hidden_columns: list[str] | None = None,
+ metrics: Mapping[str, Metric] | None = None,
) -> Summary:
"""Generate a summary of all aspects of the comparison.
@@ -948,6 +950,13 @@ def summary(
advanced users who are familiar with the summary format.
hidden_columns: Columns for which no values are printed, e.g. because they
contain sensitive information.
+ metrics: Optional mapping from display label to a metric callable
+ ``(left_expr, right_expr) -> pl.Expr``. Each callable receives two
+ :class:`polars.Expr` referring to the left and right values of a single
+ numerical column across all joined rows, and must return a scalar
+ aggregation expression. See :mod:`diffly.metrics` for presets
+ (``mean``, ``median``, ``mean_absolute_deviation`` etc.). Metrics are
+ only computed for numerical columns.
Returns:
A summary which can be printed or written to a file.
@@ -973,6 +982,7 @@ def summary(
right_name=right_name,
slim=slim,
hidden_columns=hidden_columns,
+ metrics=metrics,
)
# ----------------------------------- UTILITIES ----------------------------------- #
diff --git a/diffly/summary.py b/diffly/summary.py
index cfa9ed1..0d3148b 100644
--- a/diffly/summary.py
+++ b/diffly/summary.py
@@ -570,13 +570,16 @@ def _section_columns(self) -> RenderableType:
elif not columns:
display_items.append(Text("All columns match perfectly.", style="italic"))
else:
- matches = Table(show_header=False)
+ metric_labels = self._data._metric_labels
+ matches = Table(show_header=bool(metric_labels))
matches.add_column(
"Column",
max_width=COLUMN_SECTION_COLUMN_WIDTH,
overflow=OVERFLOW,
)
matches.add_column("Match Rate", justify="right")
+ for label in metric_labels:
+ matches.add_column(label, justify="right")
has_top_changes_column = any(
c.changes is not None for c in columns if c.match_rate < 1
)
@@ -588,6 +591,9 @@ def _section_columns(self) -> RenderableType:
Text(col.name, style="cyan"),
f"{_format_fraction_as_percentage(col.match_rate)}",
]
+ for label in metric_labels:
+ value = col.metrics.get(label) if col.metrics else None
+ row_items.append(_format_metric_value(value))
if col.changes is not None:
change_lines = []
for change in col.changes:
@@ -1088,6 +1094,10 @@ def _format_fraction_as_percentage(fraction: float) -> str:
return f"{percentage:.2f}%"
+def _yellow(raw: Any) -> str:
+ return f"[yellow]{raw}[/yellow]"
+
+
def _format_value(value: Any) -> str:
if isinstance(value, list):
formatted = [_format_value(x) for x in value]
@@ -1104,7 +1114,15 @@ def _format_value(value: Any) -> str:
raw = str(value)
else:
raw = value
- return f"[yellow]{raw}[/yellow]"
+ return _yellow(raw)
+
+
+def _format_metric_value(value: Any) -> str:
+ if value is None:
+ return ""
+ if isinstance(value, float):
+ return _yellow(f"{value:.4g}")
+ return _format_value(value)
def _trim_whitespaces(s: str) -> str:
diff --git a/diffly/testing.py b/diffly/testing.py
index ed0f132..1831f0c 100644
--- a/diffly/testing.py
+++ b/diffly/testing.py
@@ -19,6 +19,7 @@
from ._compat import dy
from .comparison import DataFrameComparison, compare_frames
+from .metrics import Metric
def assert_collection_equal(
@@ -39,6 +40,7 @@ def assert_collection_equal(
right_name: str = Side.RIGHT,
slim: bool = False,
hidden_columns: list[str] | None = None,
+ metrics: Mapping[str, Metric] | None = None,
) -> None:
"""Assert that two :mod:`dataframely` collections are equal.
@@ -82,6 +84,9 @@ def assert_collection_equal(
advanced users who are familiar with the summary format.
hidden_columns: Columns for which no values are printed, e.g. because they
contain sensitive information.
+ metrics: Optional mapping from display label to a metric callable
+ ``(left_expr, right_expr) -> pl.Expr``. See :mod:`diffly.metrics` for
+ presets.
Raises:
AssertionError: If the collections are not equal.
@@ -138,6 +143,7 @@ def assert_collection_equal(
right_name=right_name,
slim=slim,
hidden_columns=hidden_columns,
+ metrics=metrics,
)
)
}"""
@@ -167,6 +173,7 @@ def assert_frame_equal(
right_name: str = Side.RIGHT,
slim: bool = False,
hidden_columns: list[str] | None = None,
+ metrics: Mapping[str, Metric] | None = None,
) -> None:
"""Assert that two :mod:`polars` data frames are equal.
@@ -217,6 +224,9 @@ def assert_frame_equal(
advanced users who are familiar with the summary format.
hidden_columns: Columns for which no values are printed, e.g. because they
contain sensitive information.
+ metrics: Optional mapping from display label to a metric callable
+ ``(left_expr, right_expr) -> pl.Expr``. See :mod:`diffly.metrics` for
+ presets.
Raises:
AssertionError: If the data frames are not equal.
@@ -262,6 +272,7 @@ def assert_frame_equal(
right_name=right_name,
slim=slim,
hidden_columns=hidden_columns,
+ metrics=metrics,
)
text = textwrap.indent(str(summary), " " * 2)
raise AssertionError(f"Data frames are not equal:\n\n{text}")
diff --git a/tests/summary/test_summary.py b/tests/summary/test_summary.py
index ff654a2..a4e3fdb 100644
--- a/tests/summary/test_summary.py
+++ b/tests/summary/test_summary.py
@@ -216,11 +216,18 @@ def test_summary_data_parametrized(
if show_value_changes
else None
),
+ "metrics": None,
}
expected_columns = []
if show_perfect_column_matches:
expected_columns.append(
- {"name": "status", "match_rate": 1.0, "n_total_changes": 0, "changes": None}
+ {
+ "name": "status",
+ "match_rate": 1.0,
+ "n_total_changes": 0,
+ "changes": None,
+ "metrics": None,
+ }
)
expected_columns.append(value_col)
From db7735f8dd52312ef9f00ef6e93ca09eb97725fe Mon Sep 17 00:00:00 2001
From: EgeKaraismailogluQC
<128645043+EgeKaraismailogluQC@users.noreply.github.com>
Date: Thu, 30 Apr 2026 16:34:35 +0200
Subject: [PATCH 03/19] wip
---
diffly/comparison.py | 2 +-
diffly/metrics.py | 9 ++--
...alse_sample_rows_False_sample_pk_False.txt | 29 +++++++++++++
...False_sample_rows_True_sample_pk_False.txt | 29 +++++++++++++
...True_sample_rows_False_sample_pk_False.txt | 17 ++++++++
..._True_sample_rows_True_sample_pk_False.txt | 17 ++++++++
...alse_sample_rows_False_sample_pk_False.txt | 32 +++++++++++++++
..._False_sample_rows_True_sample_pk_True.txt | 34 +++++++++++++++
...True_sample_rows_False_sample_pk_False.txt | 20 +++++++++
...m_True_sample_rows_True_sample_pk_True.txt | 22 ++++++++++
...alse_sample_rows_False_sample_pk_False.txt | 29 +++++++++++++
...False_sample_rows_True_sample_pk_False.txt | 29 +++++++++++++
...True_sample_rows_False_sample_pk_False.txt | 17 ++++++++
..._True_sample_rows_True_sample_pk_False.txt | 17 ++++++++
...alse_sample_rows_False_sample_pk_False.txt | 32 +++++++++++++++
..._False_sample_rows_True_sample_pk_True.txt | 34 +++++++++++++++
...True_sample_rows_False_sample_pk_False.txt | 20 +++++++++
...m_True_sample_rows_True_sample_pk_True.txt | 22 ++++++++++
...alse_sample_rows_False_sample_pk_False.txt | 29 +++++++++++++
...False_sample_rows_True_sample_pk_False.txt | 29 +++++++++++++
...True_sample_rows_False_sample_pk_False.txt | 17 ++++++++
..._True_sample_rows_True_sample_pk_False.txt | 17 ++++++++
...alse_sample_rows_False_sample_pk_False.txt | 32 +++++++++++++++
..._False_sample_rows_True_sample_pk_True.txt | 34 +++++++++++++++
...True_sample_rows_False_sample_pk_False.txt | 20 +++++++++
...m_True_sample_rows_True_sample_pk_True.txt | 22 ++++++++++
...alse_sample_rows_False_sample_pk_False.txt | 29 +++++++++++++
...False_sample_rows_True_sample_pk_False.txt | 29 +++++++++++++
...True_sample_rows_False_sample_pk_False.txt | 17 ++++++++
..._True_sample_rows_True_sample_pk_False.txt | 17 ++++++++
...alse_sample_rows_False_sample_pk_False.txt | 32 +++++++++++++++
..._False_sample_rows_True_sample_pk_True.txt | 34 +++++++++++++++
...True_sample_rows_False_sample_pk_False.txt | 20 +++++++++
...m_True_sample_rows_True_sample_pk_True.txt | 22 ++++++++++
.../metrics_custom/test_metrics_custom.py | 37 +++++++++++++++++
...alse_sample_rows_False_sample_pk_False.txt | 29 +++++++++++++
...False_sample_rows_True_sample_pk_False.txt | 29 +++++++++++++
...True_sample_rows_False_sample_pk_False.txt | 17 ++++++++
..._True_sample_rows_True_sample_pk_False.txt | 17 ++++++++
...alse_sample_rows_False_sample_pk_False.txt | 32 +++++++++++++++
..._False_sample_rows_True_sample_pk_True.txt | 32 +++++++++++++++
...True_sample_rows_False_sample_pk_False.txt | 20 +++++++++
...m_True_sample_rows_True_sample_pk_True.txt | 20 +++++++++
...alse_sample_rows_False_sample_pk_False.txt | 29 +++++++++++++
...False_sample_rows_True_sample_pk_False.txt | 29 +++++++++++++
...True_sample_rows_False_sample_pk_False.txt | 17 ++++++++
..._True_sample_rows_True_sample_pk_False.txt | 17 ++++++++
...alse_sample_rows_False_sample_pk_False.txt | 32 +++++++++++++++
..._False_sample_rows_True_sample_pk_True.txt | 32 +++++++++++++++
...True_sample_rows_False_sample_pk_False.txt | 20 +++++++++
...m_True_sample_rows_True_sample_pk_True.txt | 20 +++++++++
...alse_sample_rows_False_sample_pk_False.txt | 29 +++++++++++++
...False_sample_rows_True_sample_pk_False.txt | 29 +++++++++++++
...True_sample_rows_False_sample_pk_False.txt | 17 ++++++++
..._True_sample_rows_True_sample_pk_False.txt | 17 ++++++++
...alse_sample_rows_False_sample_pk_False.txt | 32 +++++++++++++++
..._False_sample_rows_True_sample_pk_True.txt | 32 +++++++++++++++
...True_sample_rows_False_sample_pk_False.txt | 20 +++++++++
...m_True_sample_rows_True_sample_pk_True.txt | 20 +++++++++
...alse_sample_rows_False_sample_pk_False.txt | 29 +++++++++++++
...False_sample_rows_True_sample_pk_False.txt | 29 +++++++++++++
...True_sample_rows_False_sample_pk_False.txt | 17 ++++++++
..._True_sample_rows_True_sample_pk_False.txt | 17 ++++++++
...alse_sample_rows_False_sample_pk_False.txt | 32 +++++++++++++++
..._False_sample_rows_True_sample_pk_True.txt | 32 +++++++++++++++
...True_sample_rows_False_sample_pk_False.txt | 20 +++++++++
...m_True_sample_rows_True_sample_pk_True.txt | 20 +++++++++
.../test_metrics_presets_few.py | 33 +++++++++++++++
...alse_sample_rows_False_sample_pk_False.txt | 29 +++++++++++++
...False_sample_rows_True_sample_pk_False.txt | 29 +++++++++++++
...True_sample_rows_False_sample_pk_False.txt | 17 ++++++++
..._True_sample_rows_True_sample_pk_False.txt | 17 ++++++++
...alse_sample_rows_False_sample_pk_False.txt | 37 +++++++++++++++++
..._False_sample_rows_True_sample_pk_True.txt | 41 +++++++++++++++++++
...True_sample_rows_False_sample_pk_False.txt | 25 +++++++++++
...m_True_sample_rows_True_sample_pk_True.txt | 29 +++++++++++++
...alse_sample_rows_False_sample_pk_False.txt | 29 +++++++++++++
...False_sample_rows_True_sample_pk_False.txt | 29 +++++++++++++
...True_sample_rows_False_sample_pk_False.txt | 17 ++++++++
..._True_sample_rows_True_sample_pk_False.txt | 17 ++++++++
...alse_sample_rows_False_sample_pk_False.txt | 37 +++++++++++++++++
..._False_sample_rows_True_sample_pk_True.txt | 41 +++++++++++++++++++
...True_sample_rows_False_sample_pk_False.txt | 25 +++++++++++
...m_True_sample_rows_True_sample_pk_True.txt | 29 +++++++++++++
...alse_sample_rows_False_sample_pk_False.txt | 29 +++++++++++++
...False_sample_rows_True_sample_pk_False.txt | 29 +++++++++++++
...True_sample_rows_False_sample_pk_False.txt | 17 ++++++++
..._True_sample_rows_True_sample_pk_False.txt | 17 ++++++++
...alse_sample_rows_False_sample_pk_False.txt | 37 +++++++++++++++++
..._False_sample_rows_True_sample_pk_True.txt | 41 +++++++++++++++++++
...True_sample_rows_False_sample_pk_False.txt | 25 +++++++++++
...m_True_sample_rows_True_sample_pk_True.txt | 29 +++++++++++++
...alse_sample_rows_False_sample_pk_False.txt | 29 +++++++++++++
...False_sample_rows_True_sample_pk_False.txt | 29 +++++++++++++
...True_sample_rows_False_sample_pk_False.txt | 17 ++++++++
..._True_sample_rows_True_sample_pk_False.txt | 17 ++++++++
...alse_sample_rows_False_sample_pk_False.txt | 37 +++++++++++++++++
..._False_sample_rows_True_sample_pk_True.txt | 41 +++++++++++++++++++
...True_sample_rows_False_sample_pk_False.txt | 25 +++++++++++
...m_True_sample_rows_True_sample_pk_True.txt | 29 +++++++++++++
.../test_metrics_presets_many.py | 41 +++++++++++++++++++
tests/test_metrics.py | 3 +-
102 files changed, 2600 insertions(+), 5 deletions(-)
create mode 100644 tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
create mode 100644 tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
create mode 100644 tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
create mode 100644 tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
create mode 100644 tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
create mode 100644 tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
create mode 100644 tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
create mode 100644 tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
create mode 100644 tests/summary/fixtures/metrics_custom/test_metrics_custom.py
create mode 100644 tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
create mode 100644 tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
create mode 100644 tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
create mode 100644 tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
create mode 100644 tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
create mode 100644 tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
create mode 100644 tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
create mode 100644 tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
create mode 100644 tests/summary/fixtures/metrics_presets_few/test_metrics_presets_few.py
create mode 100644 tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
create mode 100644 tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
create mode 100644 tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
create mode 100644 tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
create mode 100644 tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
create mode 100644 tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
create mode 100644 tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
create mode 100644 tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
create mode 100644 tests/summary/fixtures/metrics_presets_many/test_metrics_presets_many.py
diff --git a/diffly/comparison.py b/diffly/comparison.py
index 6f8b9c0..6a500a2 100644
--- a/diffly/comparison.py
+++ b/diffly/comparison.py
@@ -15,7 +15,6 @@
from ._cache import cached_method
from ._conditions import condition_equal_columns, condition_equal_rows
from ._exceptions import PrimaryKeyError
-from .metrics import Metric
from ._utils import (
ABS_TOL_DEFAULT,
ABS_TOL_TEMPORAL_DEFAULT,
@@ -26,6 +25,7 @@
lazy_len,
make_and_validate_mapping,
)
+from .metrics import Metric
if TYPE_CHECKING: # pragma: no cover
# NOTE: We cannot import at runtime as we're otherwise running into circular
diff --git a/diffly/metrics.py b/diffly/metrics.py
index e1d3705..fd2f8a2 100644
--- a/diffly/metrics.py
+++ b/diffly/metrics.py
@@ -8,9 +8,12 @@
import polars as pl
Metric = Callable[[pl.Expr, pl.Expr], pl.Expr]
-"""A metric is a callable mapping ``(left_expr, right_expr)`` to a scalar
-aggregation expression. The expressions refer to the left-side and right-side
-values of a single column across all joined rows."""
+"""A metric is a callable mapping ``(left_expr, right_expr)`` to a scalar aggregation
+expression.
+
+The expressions refer to the left-side and right-side values of a single column across
+all joined rows.
+"""
def mean(left: pl.Expr, right: pl.Expr) -> pl.Expr:
diff --git a/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..5797057
--- /dev/null
+++ b/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,29 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ Diffly Summary ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: id
+
+ Schemas
+ ▔▔▔▔▔▔▔
+ Schemas match exactly (column count: 4).
+
+ Rows
+ ▔▔▔▔
+ Left count Right count
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ p95_delta ┃ max_abs_delta ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 2 │ 2 │
+ │ qty │ 80.00% │ 0.2 │ 1 │ 1 │
+ │ status │ 80.00% │ │ │ │
+ └────────┴────────────┴──────┴───────────┴───────────────┘
diff --git a/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
new file mode 100644
index 0000000..5797057
--- /dev/null
+++ b/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
@@ -0,0 +1,29 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ Diffly Summary ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: id
+
+ Schemas
+ ▔▔▔▔▔▔▔
+ Schemas match exactly (column count: 4).
+
+ Rows
+ ▔▔▔▔
+ Left count Right count
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ p95_delta ┃ max_abs_delta ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 2 │ 2 │
+ │ qty │ 80.00% │ 0.2 │ 1 │ 1 │
+ │ status │ 80.00% │ │ │ │
+ └────────┴────────────┴──────┴───────────┴───────────────┘
diff --git a/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..8f5930b
--- /dev/null
+++ b/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,17 @@
+ Rows
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ p95_delta ┃ max_abs_delta ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 2 │ 2 │
+ │ qty │ 80.00% │ 0.2 │ 1 │ 1 │
+ │ status │ 80.00% │ │ │ │
+ └────────┴────────────┴──────┴───────────┴───────────────┘
diff --git a/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
new file mode 100644
index 0000000..8f5930b
--- /dev/null
+++ b/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
@@ -0,0 +1,17 @@
+ Rows
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ p95_delta ┃ max_abs_delta ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 2 │ 2 │
+ │ qty │ 80.00% │ 0.2 │ 1 │ 1 │
+ │ status │ 80.00% │ │ │ │
+ └────────┴────────────┴──────┴───────────┴───────────────┘
diff --git a/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..83cf3d6
--- /dev/null
+++ b/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,32 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ Diffly Summary ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: id
+
+ Schemas
+ ▔▔▔▔▔▔▔
+ Schemas match exactly (column count: 4).
+
+ Rows
+ ▔▔▔▔
+ Left count Right count
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ p95_delta ┃ max_abs_delta ┃ Top Changes ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 2 │ 2 │ 40.0 -> 42.0 (1x) │
+ │ │ │ │ │ │ 20.0 -> 21.0 (1x) │
+ ├────────┼────────────┼──────┼───────────┼───────────────┼───────────────────┤
+ │ qty │ 80.00% │ 0.2 │ 1 │ 1 │ 4 -> 5 (1x) │
+ ├────────┼────────────┼──────┼───────────┼───────────────┼───────────────────┤
+ │ status │ 80.00% │ │ │ │ "c" -> "x" (1x) │
+ └────────┴────────────┴──────┴───────────┴───────────────┴───────────────────┘
diff --git a/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
new file mode 100644
index 0000000..3334cf4
--- /dev/null
+++ b/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
@@ -0,0 +1,34 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ Diffly Summary ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: id
+
+ Schemas
+ ▔▔▔▔▔▔▔
+ Schemas match exactly (column count: 4).
+
+ Rows
+ ▔▔▔▔
+ Left count Right count
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ p95_delta ┃ max_abs_delta ┃ Top Changes ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 2 │ 2 │ 40.0 -> 42.0 (1x, e.g. │
+ │ │ │ │ │ │ 4) │
+ │ │ │ │ │ │ 20.0 -> 21.0 (1x, e.g. │
+ │ │ │ │ │ │ 2) │
+ ├────────┼────────────┼──────┼───────────┼───────────────┼──────────────────────────┤
+ │ qty │ 80.00% │ 0.2 │ 1 │ 1 │ 4 -> 5 (1x, e.g. 4) │
+ ├────────┼────────────┼──────┼───────────┼───────────────┼──────────────────────────┤
+ │ status │ 80.00% │ │ │ │ "c" -> "x" (1x, e.g. 3) │
+ └────────┴────────────┴──────┴───────────┴───────────────┴──────────────────────────┘
diff --git a/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..7e14449
--- /dev/null
+++ b/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,20 @@
+ Rows
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ p95_delta ┃ max_abs_delta ┃ Top Changes ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 2 │ 2 │ 40.0 -> 42.0 (1x) │
+ │ │ │ │ │ │ 20.0 -> 21.0 (1x) │
+ ├────────┼────────────┼──────┼───────────┼───────────────┼───────────────────┤
+ │ qty │ 80.00% │ 0.2 │ 1 │ 1 │ 4 -> 5 (1x) │
+ ├────────┼────────────┼──────┼───────────┼───────────────┼───────────────────┤
+ │ status │ 80.00% │ │ │ │ "c" -> "x" (1x) │
+ └────────┴────────────┴──────┴───────────┴───────────────┴───────────────────┘
diff --git a/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
new file mode 100644
index 0000000..27fc901
--- /dev/null
+++ b/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
@@ -0,0 +1,22 @@
+ Rows
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ p95_delta ┃ max_abs_delta ┃ Top Changes ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 2 │ 2 │ 40.0 -> 42.0 (1x, e.g. │
+ │ │ │ │ │ │ 4) │
+ │ │ │ │ │ │ 20.0 -> 21.0 (1x, e.g. │
+ │ │ │ │ │ │ 2) │
+ ├────────┼────────────┼──────┼───────────┼───────────────┼──────────────────────────┤
+ │ qty │ 80.00% │ 0.2 │ 1 │ 1 │ 4 -> 5 (1x, e.g. 4) │
+ ├────────┼────────────┼──────┼───────────┼───────────────┼──────────────────────────┤
+ │ status │ 80.00% │ │ │ │ "c" -> "x" (1x, e.g. 3) │
+ └────────┴────────────┴──────┴───────────┴───────────────┴──────────────────────────┘
diff --git a/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..5797057
--- /dev/null
+++ b/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,29 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ Diffly Summary ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: id
+
+ Schemas
+ ▔▔▔▔▔▔▔
+ Schemas match exactly (column count: 4).
+
+ Rows
+ ▔▔▔▔
+ Left count Right count
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ p95_delta ┃ max_abs_delta ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 2 │ 2 │
+ │ qty │ 80.00% │ 0.2 │ 1 │ 1 │
+ │ status │ 80.00% │ │ │ │
+ └────────┴────────────┴──────┴───────────┴───────────────┘
diff --git a/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
new file mode 100644
index 0000000..5797057
--- /dev/null
+++ b/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
@@ -0,0 +1,29 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ Diffly Summary ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: id
+
+ Schemas
+ ▔▔▔▔▔▔▔
+ Schemas match exactly (column count: 4).
+
+ Rows
+ ▔▔▔▔
+ Left count Right count
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ p95_delta ┃ max_abs_delta ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 2 │ 2 │
+ │ qty │ 80.00% │ 0.2 │ 1 │ 1 │
+ │ status │ 80.00% │ │ │ │
+ └────────┴────────────┴──────┴───────────┴───────────────┘
diff --git a/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..8f5930b
--- /dev/null
+++ b/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,17 @@
+ Rows
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ p95_delta ┃ max_abs_delta ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 2 │ 2 │
+ │ qty │ 80.00% │ 0.2 │ 1 │ 1 │
+ │ status │ 80.00% │ │ │ │
+ └────────┴────────────┴──────┴───────────┴───────────────┘
diff --git a/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
new file mode 100644
index 0000000..8f5930b
--- /dev/null
+++ b/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
@@ -0,0 +1,17 @@
+ Rows
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ p95_delta ┃ max_abs_delta ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 2 │ 2 │
+ │ qty │ 80.00% │ 0.2 │ 1 │ 1 │
+ │ status │ 80.00% │ │ │ │
+ └────────┴────────────┴──────┴───────────┴───────────────┘
diff --git a/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..83cf3d6
--- /dev/null
+++ b/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,32 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ Diffly Summary ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: id
+
+ Schemas
+ ▔▔▔▔▔▔▔
+ Schemas match exactly (column count: 4).
+
+ Rows
+ ▔▔▔▔
+ Left count Right count
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ p95_delta ┃ max_abs_delta ┃ Top Changes ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 2 │ 2 │ 40.0 -> 42.0 (1x) │
+ │ │ │ │ │ │ 20.0 -> 21.0 (1x) │
+ ├────────┼────────────┼──────┼───────────┼───────────────┼───────────────────┤
+ │ qty │ 80.00% │ 0.2 │ 1 │ 1 │ 4 -> 5 (1x) │
+ ├────────┼────────────┼──────┼───────────┼───────────────┼───────────────────┤
+ │ status │ 80.00% │ │ │ │ "c" -> "x" (1x) │
+ └────────┴────────────┴──────┴───────────┴───────────────┴───────────────────┘
diff --git a/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
new file mode 100644
index 0000000..3334cf4
--- /dev/null
+++ b/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
@@ -0,0 +1,34 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ Diffly Summary ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: id
+
+ Schemas
+ ▔▔▔▔▔▔▔
+ Schemas match exactly (column count: 4).
+
+ Rows
+ ▔▔▔▔
+ Left count Right count
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ p95_delta ┃ max_abs_delta ┃ Top Changes ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 2 │ 2 │ 40.0 -> 42.0 (1x, e.g. │
+ │ │ │ │ │ │ 4) │
+ │ │ │ │ │ │ 20.0 -> 21.0 (1x, e.g. │
+ │ │ │ │ │ │ 2) │
+ ├────────┼────────────┼──────┼───────────┼───────────────┼──────────────────────────┤
+ │ qty │ 80.00% │ 0.2 │ 1 │ 1 │ 4 -> 5 (1x, e.g. 4) │
+ ├────────┼────────────┼──────┼───────────┼───────────────┼──────────────────────────┤
+ │ status │ 80.00% │ │ │ │ "c" -> "x" (1x, e.g. 3) │
+ └────────┴────────────┴──────┴───────────┴───────────────┴──────────────────────────┘
diff --git a/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..7e14449
--- /dev/null
+++ b/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,20 @@
+ Rows
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ p95_delta ┃ max_abs_delta ┃ Top Changes ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 2 │ 2 │ 40.0 -> 42.0 (1x) │
+ │ │ │ │ │ │ 20.0 -> 21.0 (1x) │
+ ├────────┼────────────┼──────┼───────────┼───────────────┼───────────────────┤
+ │ qty │ 80.00% │ 0.2 │ 1 │ 1 │ 4 -> 5 (1x) │
+ ├────────┼────────────┼──────┼───────────┼───────────────┼───────────────────┤
+ │ status │ 80.00% │ │ │ │ "c" -> "x" (1x) │
+ └────────┴────────────┴──────┴───────────┴───────────────┴───────────────────┘
diff --git a/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
new file mode 100644
index 0000000..27fc901
--- /dev/null
+++ b/tests/summary/fixtures/metrics_custom/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
@@ -0,0 +1,22 @@
+ Rows
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ p95_delta ┃ max_abs_delta ┃ Top Changes ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 2 │ 2 │ 40.0 -> 42.0 (1x, e.g. │
+ │ │ │ │ │ │ 4) │
+ │ │ │ │ │ │ 20.0 -> 21.0 (1x, e.g. │
+ │ │ │ │ │ │ 2) │
+ ├────────┼────────────┼──────┼───────────┼───────────────┼──────────────────────────┤
+ │ qty │ 80.00% │ 0.2 │ 1 │ 1 │ 4 -> 5 (1x, e.g. 4) │
+ ├────────┼────────────┼──────┼───────────┼───────────────┼──────────────────────────┤
+ │ status │ 80.00% │ │ │ │ "c" -> "x" (1x, e.g. 3) │
+ └────────┴────────────┴──────┴───────────┴───────────────┴──────────────────────────┘
diff --git a/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..862245c
--- /dev/null
+++ b/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,29 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ [1m Diffly Summary [0m ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: [36mid[0m
+
+ [1mSchemas[0m
+ ▔▔▔▔▔▔▔
+ [3mSchemas match exactly (column count: 4).[0m
+
+ [1mRows[0m
+ ▔▔▔▔
+ [1mLeft count[0m[1m [0m[1mRight count[0m
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mp95_delta[0m[1m [0m┃[1m [0m[1mmax_abs_delta[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │ [33m2[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │ [33m1[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │ │
+ └────────┴────────────┴──────┴───────────┴───────────────┘
diff --git a/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
new file mode 100644
index 0000000..862245c
--- /dev/null
+++ b/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
@@ -0,0 +1,29 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ [1m Diffly Summary [0m ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: [36mid[0m
+
+ [1mSchemas[0m
+ ▔▔▔▔▔▔▔
+ [3mSchemas match exactly (column count: 4).[0m
+
+ [1mRows[0m
+ ▔▔▔▔
+ [1mLeft count[0m[1m [0m[1mRight count[0m
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mp95_delta[0m[1m [0m┃[1m [0m[1mmax_abs_delta[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │ [33m2[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │ [33m1[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │ │
+ └────────┴────────────┴──────┴───────────┴───────────────┘
diff --git a/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..133969c
--- /dev/null
+++ b/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,17 @@
+ [1mRows[0m
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mp95_delta[0m[1m [0m┃[1m [0m[1mmax_abs_delta[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │ [33m2[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │ [33m1[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │ │
+ └────────┴────────────┴──────┴───────────┴───────────────┘
diff --git a/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
new file mode 100644
index 0000000..133969c
--- /dev/null
+++ b/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
@@ -0,0 +1,17 @@
+ [1mRows[0m
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mp95_delta[0m[1m [0m┃[1m [0m[1mmax_abs_delta[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │ [33m2[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │ [33m1[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │ │
+ └────────┴────────────┴──────┴───────────┴───────────────┘
diff --git a/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..adfe9fb
--- /dev/null
+++ b/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,32 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ [1m Diffly Summary [0m ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: [36mid[0m
+
+ [1mSchemas[0m
+ ▔▔▔▔▔▔▔
+ [3mSchemas match exactly (column count: 4).[0m
+
+ [1mRows[0m
+ ▔▔▔▔
+ [1mLeft count[0m[1m [0m[1mRight count[0m
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mp95_delta[0m[1m [0m┃[1m [0m[1mmax_abs_delta[0m[1m [0m┃[1m [0m[1m Top Changes[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │ [33m2[0m │ [33m40.0[0m -> [33m42.0[0m (1x) │
+ │ │ │ │ │ │ [33m20.0[0m -> [33m21.0[0m (1x) │
+ ├────────┼────────────┼──────┼───────────┼───────────────┼───────────────────┤
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │ [33m1[0m │ [33m4[0m -> [33m5[0m (1x) │
+ ├────────┼────────────┼──────┼───────────┼───────────────┼───────────────────┤
+ │ [36mstatus[0m │ 80.00% │ │ │ │ [33m"c"[0m -> [33m"x"[0m (1x) │
+ └────────┴────────────┴──────┴───────────┴───────────────┴───────────────────┘
diff --git a/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
new file mode 100644
index 0000000..3d9be9c
--- /dev/null
+++ b/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
@@ -0,0 +1,34 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ [1m Diffly Summary [0m ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: [36mid[0m
+
+ [1mSchemas[0m
+ ▔▔▔▔▔▔▔
+ [3mSchemas match exactly (column count: 4).[0m
+
+ [1mRows[0m
+ ▔▔▔▔
+ [1mLeft count[0m[1m [0m[1mRight count[0m
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mp95_delta[0m[1m [0m┃[1m [0m[1mmax_abs_delta[0m[1m [0m┃[1m [0m[1m Top Changes[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │ [33m2[0m │ [33m40.0[0m -> [33m42.0[0m (1x, e.g. │
+ │ │ │ │ │ │ [33m4[0m) │
+ │ │ │ │ │ │ [33m20.0[0m -> [33m21.0[0m (1x, e.g. │
+ │ │ │ │ │ │ [33m2[0m) │
+ ├────────┼────────────┼──────┼───────────┼───────────────┼──────────────────────────┤
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │ [33m1[0m │ [33m4[0m -> [33m5[0m (1x, e.g. [33m4[0m) │
+ ├────────┼────────────┼──────┼───────────┼───────────────┼──────────────────────────┤
+ │ [36mstatus[0m │ 80.00% │ │ │ │ [33m"c"[0m -> [33m"x"[0m (1x, e.g. [33m3[0m) │
+ └────────┴────────────┴──────┴───────────┴───────────────┴──────────────────────────┘
diff --git a/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..b1fb118
--- /dev/null
+++ b/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,20 @@
+ [1mRows[0m
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mp95_delta[0m[1m [0m┃[1m [0m[1mmax_abs_delta[0m[1m [0m┃[1m [0m[1m Top Changes[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │ [33m2[0m │ [33m40.0[0m -> [33m42.0[0m (1x) │
+ │ │ │ │ │ │ [33m20.0[0m -> [33m21.0[0m (1x) │
+ ├────────┼────────────┼──────┼───────────┼───────────────┼───────────────────┤
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │ [33m1[0m │ [33m4[0m -> [33m5[0m (1x) │
+ ├────────┼────────────┼──────┼───────────┼───────────────┼───────────────────┤
+ │ [36mstatus[0m │ 80.00% │ │ │ │ [33m"c"[0m -> [33m"x"[0m (1x) │
+ └────────┴────────────┴──────┴───────────┴───────────────┴───────────────────┘
diff --git a/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
new file mode 100644
index 0000000..f840203
--- /dev/null
+++ b/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
@@ -0,0 +1,22 @@
+ [1mRows[0m
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mp95_delta[0m[1m [0m┃[1m [0m[1mmax_abs_delta[0m[1m [0m┃[1m [0m[1m Top Changes[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │ [33m2[0m │ [33m40.0[0m -> [33m42.0[0m (1x, e.g. │
+ │ │ │ │ │ │ [33m4[0m) │
+ │ │ │ │ │ │ [33m20.0[0m -> [33m21.0[0m (1x, e.g. │
+ │ │ │ │ │ │ [33m2[0m) │
+ ├────────┼────────────┼──────┼───────────┼───────────────┼──────────────────────────┤
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │ [33m1[0m │ [33m4[0m -> [33m5[0m (1x, e.g. [33m4[0m) │
+ ├────────┼────────────┼──────┼───────────┼───────────────┼──────────────────────────┤
+ │ [36mstatus[0m │ 80.00% │ │ │ │ [33m"c"[0m -> [33m"x"[0m (1x, e.g. [33m3[0m) │
+ └────────┴────────────┴──────┴───────────┴───────────────┴──────────────────────────┘
diff --git a/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..862245c
--- /dev/null
+++ b/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,29 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ [1m Diffly Summary [0m ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: [36mid[0m
+
+ [1mSchemas[0m
+ ▔▔▔▔▔▔▔
+ [3mSchemas match exactly (column count: 4).[0m
+
+ [1mRows[0m
+ ▔▔▔▔
+ [1mLeft count[0m[1m [0m[1mRight count[0m
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mp95_delta[0m[1m [0m┃[1m [0m[1mmax_abs_delta[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │ [33m2[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │ [33m1[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │ │
+ └────────┴────────────┴──────┴───────────┴───────────────┘
diff --git a/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
new file mode 100644
index 0000000..862245c
--- /dev/null
+++ b/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
@@ -0,0 +1,29 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ [1m Diffly Summary [0m ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: [36mid[0m
+
+ [1mSchemas[0m
+ ▔▔▔▔▔▔▔
+ [3mSchemas match exactly (column count: 4).[0m
+
+ [1mRows[0m
+ ▔▔▔▔
+ [1mLeft count[0m[1m [0m[1mRight count[0m
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mp95_delta[0m[1m [0m┃[1m [0m[1mmax_abs_delta[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │ [33m2[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │ [33m1[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │ │
+ └────────┴────────────┴──────┴───────────┴───────────────┘
diff --git a/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..133969c
--- /dev/null
+++ b/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,17 @@
+ [1mRows[0m
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mp95_delta[0m[1m [0m┃[1m [0m[1mmax_abs_delta[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │ [33m2[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │ [33m1[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │ │
+ └────────┴────────────┴──────┴───────────┴───────────────┘
diff --git a/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
new file mode 100644
index 0000000..133969c
--- /dev/null
+++ b/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
@@ -0,0 +1,17 @@
+ [1mRows[0m
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mp95_delta[0m[1m [0m┃[1m [0m[1mmax_abs_delta[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │ [33m2[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │ [33m1[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │ │
+ └────────┴────────────┴──────┴───────────┴───────────────┘
diff --git a/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..adfe9fb
--- /dev/null
+++ b/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,32 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ [1m Diffly Summary [0m ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: [36mid[0m
+
+ [1mSchemas[0m
+ ▔▔▔▔▔▔▔
+ [3mSchemas match exactly (column count: 4).[0m
+
+ [1mRows[0m
+ ▔▔▔▔
+ [1mLeft count[0m[1m [0m[1mRight count[0m
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mp95_delta[0m[1m [0m┃[1m [0m[1mmax_abs_delta[0m[1m [0m┃[1m [0m[1m Top Changes[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │ [33m2[0m │ [33m40.0[0m -> [33m42.0[0m (1x) │
+ │ │ │ │ │ │ [33m20.0[0m -> [33m21.0[0m (1x) │
+ ├────────┼────────────┼──────┼───────────┼───────────────┼───────────────────┤
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │ [33m1[0m │ [33m4[0m -> [33m5[0m (1x) │
+ ├────────┼────────────┼──────┼───────────┼───────────────┼───────────────────┤
+ │ [36mstatus[0m │ 80.00% │ │ │ │ [33m"c"[0m -> [33m"x"[0m (1x) │
+ └────────┴────────────┴──────┴───────────┴───────────────┴───────────────────┘
diff --git a/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
new file mode 100644
index 0000000..3d9be9c
--- /dev/null
+++ b/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
@@ -0,0 +1,34 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ [1m Diffly Summary [0m ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: [36mid[0m
+
+ [1mSchemas[0m
+ ▔▔▔▔▔▔▔
+ [3mSchemas match exactly (column count: 4).[0m
+
+ [1mRows[0m
+ ▔▔▔▔
+ [1mLeft count[0m[1m [0m[1mRight count[0m
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mp95_delta[0m[1m [0m┃[1m [0m[1mmax_abs_delta[0m[1m [0m┃[1m [0m[1m Top Changes[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │ [33m2[0m │ [33m40.0[0m -> [33m42.0[0m (1x, e.g. │
+ │ │ │ │ │ │ [33m4[0m) │
+ │ │ │ │ │ │ [33m20.0[0m -> [33m21.0[0m (1x, e.g. │
+ │ │ │ │ │ │ [33m2[0m) │
+ ├────────┼────────────┼──────┼───────────┼───────────────┼──────────────────────────┤
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │ [33m1[0m │ [33m4[0m -> [33m5[0m (1x, e.g. [33m4[0m) │
+ ├────────┼────────────┼──────┼───────────┼───────────────┼──────────────────────────┤
+ │ [36mstatus[0m │ 80.00% │ │ │ │ [33m"c"[0m -> [33m"x"[0m (1x, e.g. [33m3[0m) │
+ └────────┴────────────┴──────┴───────────┴───────────────┴──────────────────────────┘
diff --git a/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..b1fb118
--- /dev/null
+++ b/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,20 @@
+ [1mRows[0m
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mp95_delta[0m[1m [0m┃[1m [0m[1mmax_abs_delta[0m[1m [0m┃[1m [0m[1m Top Changes[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │ [33m2[0m │ [33m40.0[0m -> [33m42.0[0m (1x) │
+ │ │ │ │ │ │ [33m20.0[0m -> [33m21.0[0m (1x) │
+ ├────────┼────────────┼──────┼───────────┼───────────────┼───────────────────┤
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │ [33m1[0m │ [33m4[0m -> [33m5[0m (1x) │
+ ├────────┼────────────┼──────┼───────────┼───────────────┼───────────────────┤
+ │ [36mstatus[0m │ 80.00% │ │ │ │ [33m"c"[0m -> [33m"x"[0m (1x) │
+ └────────┴────────────┴──────┴───────────┴───────────────┴───────────────────┘
diff --git a/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
new file mode 100644
index 0000000..f840203
--- /dev/null
+++ b/tests/summary/fixtures/metrics_custom/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
@@ -0,0 +1,22 @@
+ [1mRows[0m
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mp95_delta[0m[1m [0m┃[1m [0m[1mmax_abs_delta[0m[1m [0m┃[1m [0m[1m Top Changes[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │ [33m2[0m │ [33m40.0[0m -> [33m42.0[0m (1x, e.g. │
+ │ │ │ │ │ │ [33m4[0m) │
+ │ │ │ │ │ │ [33m20.0[0m -> [33m21.0[0m (1x, e.g. │
+ │ │ │ │ │ │ [33m2[0m) │
+ ├────────┼────────────┼──────┼───────────┼───────────────┼──────────────────────────┤
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │ [33m1[0m │ [33m4[0m -> [33m5[0m (1x, e.g. [33m4[0m) │
+ ├────────┼────────────┼──────┼───────────┼───────────────┼──────────────────────────┤
+ │ [36mstatus[0m │ 80.00% │ │ │ │ [33m"c"[0m -> [33m"x"[0m (1x, e.g. [33m3[0m) │
+ └────────┴────────────┴──────┴───────────┴───────────────┴──────────────────────────┘
diff --git a/tests/summary/fixtures/metrics_custom/test_metrics_custom.py b/tests/summary/fixtures/metrics_custom/test_metrics_custom.py
new file mode 100644
index 0000000..5c8778e
--- /dev/null
+++ b/tests/summary/fixtures/metrics_custom/test_metrics_custom.py
@@ -0,0 +1,37 @@
+# Copyright (c) QuantCo 2025-2026
+# SPDX-License-Identifier: BSD-3-Clause
+
+import polars as pl
+import pytest
+
+from diffly import compare_frames, metrics
+from tests.utils import generate_summaries
+
+
+@pytest.mark.generate
+def test_generate() -> None:
+ left = pl.DataFrame(
+ {
+ "id": [1, 2, 3, 4, 5],
+ "price": [10.0, 20.0, 30.0, 40.0, 50.0],
+ "qty": [1, 2, 3, 4, 5],
+ "status": ["a", "b", "c", "d", "e"],
+ }
+ )
+ right = pl.DataFrame(
+ {
+ "id": [1, 2, 3, 4, 5],
+ "price": [10.0, 21.0, 30.0, 42.0, 50.0],
+ "qty": [1, 2, 3, 5, 5],
+ "status": ["a", "b", "x", "d", "e"],
+ }
+ )
+ comp = compare_frames(left, right, primary_key=["id"])
+ generate_summaries(
+ comp,
+ metrics={
+ "mean": metrics.mean,
+ "p95_delta": metrics.quantile(0.95),
+ "max_abs_delta": lambda left, right: (right - left).abs().max(),
+ },
+ )
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..ce7fbde
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,29 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ Diffly Summary ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: id
+
+ Schemas
+ ▔▔▔▔▔▔▔
+ Schemas match exactly (column count: 4).
+
+ Rows
+ ▔▔▔▔
+ Left count Right count
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ max ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 2 │
+ │ qty │ 80.00% │ 0.2 │ 1 │
+ │ status │ 80.00% │ │ │
+ └────────┴────────────┴──────┴─────┘
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
new file mode 100644
index 0000000..ce7fbde
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
@@ -0,0 +1,29 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ Diffly Summary ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: id
+
+ Schemas
+ ▔▔▔▔▔▔▔
+ Schemas match exactly (column count: 4).
+
+ Rows
+ ▔▔▔▔
+ Left count Right count
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ max ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 2 │
+ │ qty │ 80.00% │ 0.2 │ 1 │
+ │ status │ 80.00% │ │ │
+ └────────┴────────────┴──────┴─────┘
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..be0aae1
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,17 @@
+ Rows
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ max ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 2 │
+ │ qty │ 80.00% │ 0.2 │ 1 │
+ │ status │ 80.00% │ │ │
+ └────────┴────────────┴──────┴─────┘
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
new file mode 100644
index 0000000..be0aae1
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
@@ -0,0 +1,17 @@
+ Rows
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ max ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 2 │
+ │ qty │ 80.00% │ 0.2 │ 1 │
+ │ status │ 80.00% │ │ │
+ └────────┴────────────┴──────┴─────┘
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..74f09a6
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,32 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ Diffly Summary ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: id
+
+ Schemas
+ ▔▔▔▔▔▔▔
+ Schemas match exactly (column count: 4).
+
+ Rows
+ ▔▔▔▔
+ Left count Right count
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ max ┃ Top Changes ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 2 │ 40.0 -> 42.0 (1x) │
+ │ │ │ │ │ 20.0 -> 21.0 (1x) │
+ ├────────┼────────────┼──────┼─────┼───────────────────┤
+ │ qty │ 80.00% │ 0.2 │ 1 │ 4 -> 5 (1x) │
+ ├────────┼────────────┼──────┼─────┼───────────────────┤
+ │ status │ 80.00% │ │ │ "c" -> "x" (1x) │
+ └────────┴────────────┴──────┴─────┴───────────────────┘
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
new file mode 100644
index 0000000..bc6c74b
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
@@ -0,0 +1,32 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ Diffly Summary ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: id
+
+ Schemas
+ ▔▔▔▔▔▔▔
+ Schemas match exactly (column count: 4).
+
+ Rows
+ ▔▔▔▔
+ Left count Right count
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ max ┃ Top Changes ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 2 │ 40.0 -> 42.0 (1x, e.g. 4) │
+ │ │ │ │ │ 20.0 -> 21.0 (1x, e.g. 2) │
+ ├────────┼────────────┼──────┼─────┼───────────────────────────┤
+ │ qty │ 80.00% │ 0.2 │ 1 │ 4 -> 5 (1x, e.g. 4) │
+ ├────────┼────────────┼──────┼─────┼───────────────────────────┤
+ │ status │ 80.00% │ │ │ "c" -> "x" (1x, e.g. 3) │
+ └────────┴────────────┴──────┴─────┴───────────────────────────┘
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..9af95d6
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,20 @@
+ Rows
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ max ┃ Top Changes ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 2 │ 40.0 -> 42.0 (1x) │
+ │ │ │ │ │ 20.0 -> 21.0 (1x) │
+ ├────────┼────────────┼──────┼─────┼───────────────────┤
+ │ qty │ 80.00% │ 0.2 │ 1 │ 4 -> 5 (1x) │
+ ├────────┼────────────┼──────┼─────┼───────────────────┤
+ │ status │ 80.00% │ │ │ "c" -> "x" (1x) │
+ └────────┴────────────┴──────┴─────┴───────────────────┘
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
new file mode 100644
index 0000000..6d9581d
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
@@ -0,0 +1,20 @@
+ Rows
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ max ┃ Top Changes ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 2 │ 40.0 -> 42.0 (1x, e.g. 4) │
+ │ │ │ │ │ 20.0 -> 21.0 (1x, e.g. 2) │
+ ├────────┼────────────┼──────┼─────┼───────────────────────────┤
+ │ qty │ 80.00% │ 0.2 │ 1 │ 4 -> 5 (1x, e.g. 4) │
+ ├────────┼────────────┼──────┼─────┼───────────────────────────┤
+ │ status │ 80.00% │ │ │ "c" -> "x" (1x, e.g. 3) │
+ └────────┴────────────┴──────┴─────┴───────────────────────────┘
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..ce7fbde
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,29 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ Diffly Summary ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: id
+
+ Schemas
+ ▔▔▔▔▔▔▔
+ Schemas match exactly (column count: 4).
+
+ Rows
+ ▔▔▔▔
+ Left count Right count
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ max ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 2 │
+ │ qty │ 80.00% │ 0.2 │ 1 │
+ │ status │ 80.00% │ │ │
+ └────────┴────────────┴──────┴─────┘
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
new file mode 100644
index 0000000..ce7fbde
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
@@ -0,0 +1,29 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ Diffly Summary ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: id
+
+ Schemas
+ ▔▔▔▔▔▔▔
+ Schemas match exactly (column count: 4).
+
+ Rows
+ ▔▔▔▔
+ Left count Right count
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ max ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 2 │
+ │ qty │ 80.00% │ 0.2 │ 1 │
+ │ status │ 80.00% │ │ │
+ └────────┴────────────┴──────┴─────┘
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..be0aae1
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,17 @@
+ Rows
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ max ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 2 │
+ │ qty │ 80.00% │ 0.2 │ 1 │
+ │ status │ 80.00% │ │ │
+ └────────┴────────────┴──────┴─────┘
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
new file mode 100644
index 0000000..be0aae1
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
@@ -0,0 +1,17 @@
+ Rows
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ max ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 2 │
+ │ qty │ 80.00% │ 0.2 │ 1 │
+ │ status │ 80.00% │ │ │
+ └────────┴────────────┴──────┴─────┘
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..74f09a6
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,32 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ Diffly Summary ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: id
+
+ Schemas
+ ▔▔▔▔▔▔▔
+ Schemas match exactly (column count: 4).
+
+ Rows
+ ▔▔▔▔
+ Left count Right count
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ max ┃ Top Changes ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 2 │ 40.0 -> 42.0 (1x) │
+ │ │ │ │ │ 20.0 -> 21.0 (1x) │
+ ├────────┼────────────┼──────┼─────┼───────────────────┤
+ │ qty │ 80.00% │ 0.2 │ 1 │ 4 -> 5 (1x) │
+ ├────────┼────────────┼──────┼─────┼───────────────────┤
+ │ status │ 80.00% │ │ │ "c" -> "x" (1x) │
+ └────────┴────────────┴──────┴─────┴───────────────────┘
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
new file mode 100644
index 0000000..bc6c74b
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
@@ -0,0 +1,32 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ Diffly Summary ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: id
+
+ Schemas
+ ▔▔▔▔▔▔▔
+ Schemas match exactly (column count: 4).
+
+ Rows
+ ▔▔▔▔
+ Left count Right count
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ max ┃ Top Changes ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 2 │ 40.0 -> 42.0 (1x, e.g. 4) │
+ │ │ │ │ │ 20.0 -> 21.0 (1x, e.g. 2) │
+ ├────────┼────────────┼──────┼─────┼───────────────────────────┤
+ │ qty │ 80.00% │ 0.2 │ 1 │ 4 -> 5 (1x, e.g. 4) │
+ ├────────┼────────────┼──────┼─────┼───────────────────────────┤
+ │ status │ 80.00% │ │ │ "c" -> "x" (1x, e.g. 3) │
+ └────────┴────────────┴──────┴─────┴───────────────────────────┘
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..9af95d6
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,20 @@
+ Rows
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ max ┃ Top Changes ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 2 │ 40.0 -> 42.0 (1x) │
+ │ │ │ │ │ 20.0 -> 21.0 (1x) │
+ ├────────┼────────────┼──────┼─────┼───────────────────┤
+ │ qty │ 80.00% │ 0.2 │ 1 │ 4 -> 5 (1x) │
+ ├────────┼────────────┼──────┼─────┼───────────────────┤
+ │ status │ 80.00% │ │ │ "c" -> "x" (1x) │
+ └────────┴────────────┴──────┴─────┴───────────────────┘
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
new file mode 100644
index 0000000..6d9581d
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
@@ -0,0 +1,20 @@
+ Rows
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ max ┃ Top Changes ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 2 │ 40.0 -> 42.0 (1x, e.g. 4) │
+ │ │ │ │ │ 20.0 -> 21.0 (1x, e.g. 2) │
+ ├────────┼────────────┼──────┼─────┼───────────────────────────┤
+ │ qty │ 80.00% │ 0.2 │ 1 │ 4 -> 5 (1x, e.g. 4) │
+ ├────────┼────────────┼──────┼─────┼───────────────────────────┤
+ │ status │ 80.00% │ │ │ "c" -> "x" (1x, e.g. 3) │
+ └────────┴────────────┴──────┴─────┴───────────────────────────┘
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..996d5c0
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,29 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ [1m Diffly Summary [0m ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: [36mid[0m
+
+ [1mSchemas[0m
+ ▔▔▔▔▔▔▔
+ [3mSchemas match exactly (column count: 4).[0m
+
+ [1mRows[0m
+ ▔▔▔▔
+ [1mLeft count[0m[1m [0m[1mRight count[0m
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │
+ └────────┴────────────┴──────┴─────┘
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
new file mode 100644
index 0000000..996d5c0
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
@@ -0,0 +1,29 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ [1m Diffly Summary [0m ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: [36mid[0m
+
+ [1mSchemas[0m
+ ▔▔▔▔▔▔▔
+ [3mSchemas match exactly (column count: 4).[0m
+
+ [1mRows[0m
+ ▔▔▔▔
+ [1mLeft count[0m[1m [0m[1mRight count[0m
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │
+ └────────┴────────────┴──────┴─────┘
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..a4fdffb
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,17 @@
+ [1mRows[0m
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │
+ └────────┴────────────┴──────┴─────┘
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
new file mode 100644
index 0000000..a4fdffb
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
@@ -0,0 +1,17 @@
+ [1mRows[0m
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │
+ └────────┴────────────┴──────┴─────┘
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..a2e7af7
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,32 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ [1m Diffly Summary [0m ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: [36mid[0m
+
+ [1mSchemas[0m
+ ▔▔▔▔▔▔▔
+ [3mSchemas match exactly (column count: 4).[0m
+
+ [1mRows[0m
+ ▔▔▔▔
+ [1mLeft count[0m[1m [0m[1mRight count[0m
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m Top Changes[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │ [33m40.0[0m -> [33m42.0[0m (1x) │
+ │ │ │ │ │ [33m20.0[0m -> [33m21.0[0m (1x) │
+ ├────────┼────────────┼──────┼─────┼───────────────────┤
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │ [33m4[0m -> [33m5[0m (1x) │
+ ├────────┼────────────┼──────┼─────┼───────────────────┤
+ │ [36mstatus[0m │ 80.00% │ │ │ [33m"c"[0m -> [33m"x"[0m (1x) │
+ └────────┴────────────┴──────┴─────┴───────────────────┘
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
new file mode 100644
index 0000000..b12c378
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
@@ -0,0 +1,32 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ [1m Diffly Summary [0m ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: [36mid[0m
+
+ [1mSchemas[0m
+ ▔▔▔▔▔▔▔
+ [3mSchemas match exactly (column count: 4).[0m
+
+ [1mRows[0m
+ ▔▔▔▔
+ [1mLeft count[0m[1m [0m[1mRight count[0m
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m Top Changes[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │ [33m40.0[0m -> [33m42.0[0m (1x, e.g. [33m4[0m) │
+ │ │ │ │ │ [33m20.0[0m -> [33m21.0[0m (1x, e.g. [33m2[0m) │
+ ├────────┼────────────┼──────┼─────┼───────────────────────────┤
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │ [33m4[0m -> [33m5[0m (1x, e.g. [33m4[0m) │
+ ├────────┼────────────┼──────┼─────┼───────────────────────────┤
+ │ [36mstatus[0m │ 80.00% │ │ │ [33m"c"[0m -> [33m"x"[0m (1x, e.g. [33m3[0m) │
+ └────────┴────────────┴──────┴─────┴───────────────────────────┘
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..de9090a
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,20 @@
+ [1mRows[0m
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m Top Changes[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │ [33m40.0[0m -> [33m42.0[0m (1x) │
+ │ │ │ │ │ [33m20.0[0m -> [33m21.0[0m (1x) │
+ ├────────┼────────────┼──────┼─────┼───────────────────┤
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │ [33m4[0m -> [33m5[0m (1x) │
+ ├────────┼────────────┼──────┼─────┼───────────────────┤
+ │ [36mstatus[0m │ 80.00% │ │ │ [33m"c"[0m -> [33m"x"[0m (1x) │
+ └────────┴────────────┴──────┴─────┴───────────────────┘
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
new file mode 100644
index 0000000..939518a
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
@@ -0,0 +1,20 @@
+ [1mRows[0m
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m Top Changes[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │ [33m40.0[0m -> [33m42.0[0m (1x, e.g. [33m4[0m) │
+ │ │ │ │ │ [33m20.0[0m -> [33m21.0[0m (1x, e.g. [33m2[0m) │
+ ├────────┼────────────┼──────┼─────┼───────────────────────────┤
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │ [33m4[0m -> [33m5[0m (1x, e.g. [33m4[0m) │
+ ├────────┼────────────┼──────┼─────┼───────────────────────────┤
+ │ [36mstatus[0m │ 80.00% │ │ │ [33m"c"[0m -> [33m"x"[0m (1x, e.g. [33m3[0m) │
+ └────────┴────────────┴──────┴─────┴───────────────────────────┘
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..996d5c0
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,29 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ [1m Diffly Summary [0m ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: [36mid[0m
+
+ [1mSchemas[0m
+ ▔▔▔▔▔▔▔
+ [3mSchemas match exactly (column count: 4).[0m
+
+ [1mRows[0m
+ ▔▔▔▔
+ [1mLeft count[0m[1m [0m[1mRight count[0m
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │
+ └────────┴────────────┴──────┴─────┘
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
new file mode 100644
index 0000000..996d5c0
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
@@ -0,0 +1,29 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ [1m Diffly Summary [0m ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: [36mid[0m
+
+ [1mSchemas[0m
+ ▔▔▔▔▔▔▔
+ [3mSchemas match exactly (column count: 4).[0m
+
+ [1mRows[0m
+ ▔▔▔▔
+ [1mLeft count[0m[1m [0m[1mRight count[0m
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │
+ └────────┴────────────┴──────┴─────┘
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..a4fdffb
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,17 @@
+ [1mRows[0m
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │
+ └────────┴────────────┴──────┴─────┘
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
new file mode 100644
index 0000000..a4fdffb
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
@@ -0,0 +1,17 @@
+ [1mRows[0m
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │
+ └────────┴────────────┴──────┴─────┘
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..a2e7af7
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,32 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ [1m Diffly Summary [0m ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: [36mid[0m
+
+ [1mSchemas[0m
+ ▔▔▔▔▔▔▔
+ [3mSchemas match exactly (column count: 4).[0m
+
+ [1mRows[0m
+ ▔▔▔▔
+ [1mLeft count[0m[1m [0m[1mRight count[0m
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m Top Changes[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │ [33m40.0[0m -> [33m42.0[0m (1x) │
+ │ │ │ │ │ [33m20.0[0m -> [33m21.0[0m (1x) │
+ ├────────┼────────────┼──────┼─────┼───────────────────┤
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │ [33m4[0m -> [33m5[0m (1x) │
+ ├────────┼────────────┼──────┼─────┼───────────────────┤
+ │ [36mstatus[0m │ 80.00% │ │ │ [33m"c"[0m -> [33m"x"[0m (1x) │
+ └────────┴────────────┴──────┴─────┴───────────────────┘
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
new file mode 100644
index 0000000..b12c378
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
@@ -0,0 +1,32 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ [1m Diffly Summary [0m ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: [36mid[0m
+
+ [1mSchemas[0m
+ ▔▔▔▔▔▔▔
+ [3mSchemas match exactly (column count: 4).[0m
+
+ [1mRows[0m
+ ▔▔▔▔
+ [1mLeft count[0m[1m [0m[1mRight count[0m
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m Top Changes[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │ [33m40.0[0m -> [33m42.0[0m (1x, e.g. [33m4[0m) │
+ │ │ │ │ │ [33m20.0[0m -> [33m21.0[0m (1x, e.g. [33m2[0m) │
+ ├────────┼────────────┼──────┼─────┼───────────────────────────┤
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │ [33m4[0m -> [33m5[0m (1x, e.g. [33m4[0m) │
+ ├────────┼────────────┼──────┼─────┼───────────────────────────┤
+ │ [36mstatus[0m │ 80.00% │ │ │ [33m"c"[0m -> [33m"x"[0m (1x, e.g. [33m3[0m) │
+ └────────┴────────────┴──────┴─────┴───────────────────────────┘
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..de9090a
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,20 @@
+ [1mRows[0m
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m Top Changes[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │ [33m40.0[0m -> [33m42.0[0m (1x) │
+ │ │ │ │ │ [33m20.0[0m -> [33m21.0[0m (1x) │
+ ├────────┼────────────┼──────┼─────┼───────────────────┤
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │ [33m4[0m -> [33m5[0m (1x) │
+ ├────────┼────────────┼──────┼─────┼───────────────────┤
+ │ [36mstatus[0m │ 80.00% │ │ │ [33m"c"[0m -> [33m"x"[0m (1x) │
+ └────────┴────────────┴──────┴─────┴───────────────────┘
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
new file mode 100644
index 0000000..939518a
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
@@ -0,0 +1,20 @@
+ [1mRows[0m
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m Top Changes[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │ [33m40.0[0m -> [33m42.0[0m (1x, e.g. [33m4[0m) │
+ │ │ │ │ │ [33m20.0[0m -> [33m21.0[0m (1x, e.g. [33m2[0m) │
+ ├────────┼────────────┼──────┼─────┼───────────────────────────┤
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │ [33m4[0m -> [33m5[0m (1x, e.g. [33m4[0m) │
+ ├────────┼────────────┼──────┼─────┼───────────────────────────┤
+ │ [36mstatus[0m │ 80.00% │ │ │ [33m"c"[0m -> [33m"x"[0m (1x, e.g. [33m3[0m) │
+ └────────┴────────────┴──────┴─────┴───────────────────────────┘
diff --git a/tests/summary/fixtures/metrics_presets_few/test_metrics_presets_few.py b/tests/summary/fixtures/metrics_presets_few/test_metrics_presets_few.py
new file mode 100644
index 0000000..7556fec
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_few/test_metrics_presets_few.py
@@ -0,0 +1,33 @@
+# Copyright (c) QuantCo 2025-2026
+# SPDX-License-Identifier: BSD-3-Clause
+
+import polars as pl
+import pytest
+
+from diffly import compare_frames, metrics
+from tests.utils import generate_summaries
+
+
+@pytest.mark.generate
+def test_generate() -> None:
+ left = pl.DataFrame(
+ {
+ "id": [1, 2, 3, 4, 5],
+ "price": [10.0, 20.0, 30.0, 40.0, 50.0],
+ "qty": [1, 2, 3, 4, 5],
+ "status": ["a", "b", "c", "d", "e"],
+ }
+ )
+ right = pl.DataFrame(
+ {
+ "id": [1, 2, 3, 4, 5],
+ "price": [10.0, 21.0, 30.0, 42.0, 50.0],
+ "qty": [1, 2, 3, 5, 5],
+ "status": ["a", "b", "x", "d", "e"],
+ }
+ )
+ comp = compare_frames(left, right, primary_key=["id"])
+ generate_summaries(
+ comp,
+ metrics={"mean": metrics.mean, "max": metrics.max},
+ )
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..418475e
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,29 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ Diffly Summary ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: id
+
+ Schemas
+ ▔▔▔▔▔▔▔
+ Schemas match exactly (column count: 4).
+
+ Rows
+ ▔▔▔▔
+ Left count Right count
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ median ┃ min ┃ max ┃ std ┃ mad ┃ mrd ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │
+ │ status │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
new file mode 100644
index 0000000..418475e
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
@@ -0,0 +1,29 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ Diffly Summary ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: id
+
+ Schemas
+ ▔▔▔▔▔▔▔
+ Schemas match exactly (column count: 4).
+
+ Rows
+ ▔▔▔▔
+ Left count Right count
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ median ┃ min ┃ max ┃ std ┃ mad ┃ mrd ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │
+ │ status │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..84cbcd7
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,17 @@
+ Rows
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ median ┃ min ┃ max ┃ std ┃ mad ┃ mrd ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │
+ │ status │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
new file mode 100644
index 0000000..84cbcd7
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
@@ -0,0 +1,17 @@
+ Rows
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ median ┃ min ┃ max ┃ std ┃ mad ┃ mrd ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │
+ │ status │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..588bb44
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,37 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ Diffly Summary ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: id
+
+ Schemas
+ ▔▔▔▔▔▔▔
+ Schemas match exactly (column count: 4).
+
+ Rows
+ ▔▔▔▔
+ Left count Right count
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┳━━━━━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Top ┃
+ ┃ Column ┃ Match Rate ┃ mean ┃ median ┃ min ┃ max ┃ std ┃ mad ┃ mrd ┃ Changes ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━╇━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │ 40.0 -> │
+ │ │ │ │ │ │ │ │ │ │ 42.0 (1x) │
+ │ │ │ │ │ │ │ │ │ │ 20.0 -> │
+ │ │ │ │ │ │ │ │ │ │ 21.0 (1x) │
+ ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │ 4 -> 5 │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
+ │ status │ 80.00% │ │ │ │ │ │ │ │ "c" -> │
+ │ │ │ │ │ │ │ │ │ │ "x" (1x) │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┴───────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
new file mode 100644
index 0000000..a91325f
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
@@ -0,0 +1,41 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ Diffly Summary ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: id
+
+ Schemas
+ ▔▔▔▔▔▔▔
+ Schemas match exactly (column count: 4).
+
+ Rows
+ ▔▔▔▔
+ Left count Right count
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┳━━━━━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Top ┃
+ ┃ Column ┃ Match Rate ┃ mean ┃ median ┃ min ┃ max ┃ std ┃ mad ┃ mrd ┃ Changes ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━╇━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │ 40.0 -> │
+ │ │ │ │ │ │ │ │ │ │ 42.0 (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. 4) │
+ │ │ │ │ │ │ │ │ │ │ 20.0 -> │
+ │ │ │ │ │ │ │ │ │ │ 21.0 (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. 2) │
+ ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │ 4 -> 5 │
+ │ │ │ │ │ │ │ │ │ │ (1x, e.g. │
+ │ │ │ │ │ │ │ │ │ │ 4) │
+ ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
+ │ status │ 80.00% │ │ │ │ │ │ │ │ "c" -> │
+ │ │ │ │ │ │ │ │ │ │ "x" (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. 3) │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┴───────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..811a324
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,25 @@
+ Rows
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┳━━━━━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Top ┃
+ ┃ Column ┃ Match Rate ┃ mean ┃ median ┃ min ┃ max ┃ std ┃ mad ┃ mrd ┃ Changes ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━╇━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │ 40.0 -> │
+ │ │ │ │ │ │ │ │ │ │ 42.0 (1x) │
+ │ │ │ │ │ │ │ │ │ │ 20.0 -> │
+ │ │ │ │ │ │ │ │ │ │ 21.0 (1x) │
+ ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │ 4 -> 5 │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
+ │ status │ 80.00% │ │ │ │ │ │ │ │ "c" -> │
+ │ │ │ │ │ │ │ │ │ │ "x" (1x) │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┴───────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
new file mode 100644
index 0000000..09353d4
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
@@ -0,0 +1,29 @@
+ Rows
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┳━━━━━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Top ┃
+ ┃ Column ┃ Match Rate ┃ mean ┃ median ┃ min ┃ max ┃ std ┃ mad ┃ mrd ┃ Changes ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━╇━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │ 40.0 -> │
+ │ │ │ │ │ │ │ │ │ │ 42.0 (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. 4) │
+ │ │ │ │ │ │ │ │ │ │ 20.0 -> │
+ │ │ │ │ │ │ │ │ │ │ 21.0 (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. 2) │
+ ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │ 4 -> 5 │
+ │ │ │ │ │ │ │ │ │ │ (1x, e.g. │
+ │ │ │ │ │ │ │ │ │ │ 4) │
+ ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
+ │ status │ 80.00% │ │ │ │ │ │ │ │ "c" -> │
+ │ │ │ │ │ │ │ │ │ │ "x" (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. 3) │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┴───────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..418475e
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,29 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ Diffly Summary ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: id
+
+ Schemas
+ ▔▔▔▔▔▔▔
+ Schemas match exactly (column count: 4).
+
+ Rows
+ ▔▔▔▔
+ Left count Right count
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ median ┃ min ┃ max ┃ std ┃ mad ┃ mrd ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │
+ │ status │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
new file mode 100644
index 0000000..418475e
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
@@ -0,0 +1,29 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ Diffly Summary ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: id
+
+ Schemas
+ ▔▔▔▔▔▔▔
+ Schemas match exactly (column count: 4).
+
+ Rows
+ ▔▔▔▔
+ Left count Right count
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ median ┃ min ┃ max ┃ std ┃ mad ┃ mrd ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │
+ │ status │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..84cbcd7
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,17 @@
+ Rows
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ median ┃ min ┃ max ┃ std ┃ mad ┃ mrd ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │
+ │ status │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
new file mode 100644
index 0000000..84cbcd7
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
@@ -0,0 +1,17 @@
+ Rows
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┓
+ ┃ Column ┃ Match Rate ┃ mean ┃ median ┃ min ┃ max ┃ std ┃ mad ┃ mrd ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │
+ │ status │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..588bb44
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,37 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ Diffly Summary ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: id
+
+ Schemas
+ ▔▔▔▔▔▔▔
+ Schemas match exactly (column count: 4).
+
+ Rows
+ ▔▔▔▔
+ Left count Right count
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┳━━━━━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Top ┃
+ ┃ Column ┃ Match Rate ┃ mean ┃ median ┃ min ┃ max ┃ std ┃ mad ┃ mrd ┃ Changes ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━╇━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │ 40.0 -> │
+ │ │ │ │ │ │ │ │ │ │ 42.0 (1x) │
+ │ │ │ │ │ │ │ │ │ │ 20.0 -> │
+ │ │ │ │ │ │ │ │ │ │ 21.0 (1x) │
+ ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │ 4 -> 5 │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
+ │ status │ 80.00% │ │ │ │ │ │ │ │ "c" -> │
+ │ │ │ │ │ │ │ │ │ │ "x" (1x) │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┴───────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
new file mode 100644
index 0000000..a91325f
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
@@ -0,0 +1,41 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ Diffly Summary ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: id
+
+ Schemas
+ ▔▔▔▔▔▔▔
+ Schemas match exactly (column count: 4).
+
+ Rows
+ ▔▔▔▔
+ Left count Right count
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┳━━━━━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Top ┃
+ ┃ Column ┃ Match Rate ┃ mean ┃ median ┃ min ┃ max ┃ std ┃ mad ┃ mrd ┃ Changes ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━╇━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │ 40.0 -> │
+ │ │ │ │ │ │ │ │ │ │ 42.0 (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. 4) │
+ │ │ │ │ │ │ │ │ │ │ 20.0 -> │
+ │ │ │ │ │ │ │ │ │ │ 21.0 (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. 2) │
+ ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │ 4 -> 5 │
+ │ │ │ │ │ │ │ │ │ │ (1x, e.g. │
+ │ │ │ │ │ │ │ │ │ │ 4) │
+ ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
+ │ status │ 80.00% │ │ │ │ │ │ │ │ "c" -> │
+ │ │ │ │ │ │ │ │ │ │ "x" (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. 3) │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┴───────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..811a324
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,25 @@
+ Rows
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┳━━━━━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Top ┃
+ ┃ Column ┃ Match Rate ┃ mean ┃ median ┃ min ┃ max ┃ std ┃ mad ┃ mrd ┃ Changes ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━╇━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │ 40.0 -> │
+ │ │ │ │ │ │ │ │ │ │ 42.0 (1x) │
+ │ │ │ │ │ │ │ │ │ │ 20.0 -> │
+ │ │ │ │ │ │ │ │ │ │ 21.0 (1x) │
+ ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │ 4 -> 5 │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
+ │ status │ 80.00% │ │ │ │ │ │ │ │ "c" -> │
+ │ │ │ │ │ │ │ │ │ │ "x" (1x) │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┴───────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
new file mode 100644
index 0000000..09353d4
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
@@ -0,0 +1,29 @@
+ Rows
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┳━━━━━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Top ┃
+ ┃ Column ┃ Match Rate ┃ mean ┃ median ┃ min ┃ max ┃ std ┃ mad ┃ mrd ┃ Changes ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━╇━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │ 40.0 -> │
+ │ │ │ │ │ │ │ │ │ │ 42.0 (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. 4) │
+ │ │ │ │ │ │ │ │ │ │ 20.0 -> │
+ │ │ │ │ │ │ │ │ │ │ 21.0 (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. 2) │
+ ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │ 4 -> 5 │
+ │ │ │ │ │ │ │ │ │ │ (1x, e.g. │
+ │ │ │ │ │ │ │ │ │ │ 4) │
+ ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
+ │ status │ 80.00% │ │ │ │ │ │ │ │ "c" -> │
+ │ │ │ │ │ │ │ │ │ │ "x" (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. 3) │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┴───────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..3726ca2
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,29 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ [1m Diffly Summary [0m ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: [36mid[0m
+
+ [1mSchemas[0m
+ ▔▔▔▔▔▔▔
+ [3mSchemas match exactly (column count: 4).[0m
+
+ [1mRows[0m
+ ▔▔▔▔
+ [1mLeft count[0m[1m [0m[1mRight count[0m
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmedian[0m[1m [0m┃[1m [0m[1mmin[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m std[0m[1m [0m┃[1m [0m[1mmad[0m[1m [0m┃[1m [0m[1m mrd[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
new file mode 100644
index 0000000..3726ca2
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
@@ -0,0 +1,29 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ [1m Diffly Summary [0m ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: [36mid[0m
+
+ [1mSchemas[0m
+ ▔▔▔▔▔▔▔
+ [3mSchemas match exactly (column count: 4).[0m
+
+ [1mRows[0m
+ ▔▔▔▔
+ [1mLeft count[0m[1m [0m[1mRight count[0m
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmedian[0m[1m [0m┃[1m [0m[1mmin[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m std[0m[1m [0m┃[1m [0m[1mmad[0m[1m [0m┃[1m [0m[1m mrd[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..610dfc4
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,17 @@
+ [1mRows[0m
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmedian[0m[1m [0m┃[1m [0m[1mmin[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m std[0m[1m [0m┃[1m [0m[1mmad[0m[1m [0m┃[1m [0m[1m mrd[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
new file mode 100644
index 0000000..610dfc4
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
@@ -0,0 +1,17 @@
+ [1mRows[0m
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmedian[0m[1m [0m┃[1m [0m[1mmin[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m std[0m[1m [0m┃[1m [0m[1mmad[0m[1m [0m┃[1m [0m[1m mrd[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..e6c2d3a
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,37 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ [1m Diffly Summary [0m ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: [36mid[0m
+
+ [1mSchemas[0m
+ ▔▔▔▔▔▔▔
+ [3mSchemas match exactly (column count: 4).[0m
+
+ [1mRows[0m
+ ▔▔▔▔
+ [1mLeft count[0m[1m [0m[1mRight count[0m
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┳━━━━━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Top[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmedian[0m[1m [0m┃[1m [0m[1mmin[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m std[0m[1m [0m┃[1m [0m[1mmad[0m[1m [0m┃[1m [0m[1m mrd[0m[1m [0m┃[1m [0m[1m Changes[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━╇━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │ [33m40.0[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m42.0[0m (1x) │
+ │ │ │ │ │ │ │ │ │ │ [33m20.0[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m21.0[0m (1x) │
+ ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │ [33m4[0m -> [33m5[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │ [33m"c"[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m"x"[0m (1x) │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┴───────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
new file mode 100644
index 0000000..d310b83
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
@@ -0,0 +1,41 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ [1m Diffly Summary [0m ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: [36mid[0m
+
+ [1mSchemas[0m
+ ▔▔▔▔▔▔▔
+ [3mSchemas match exactly (column count: 4).[0m
+
+ [1mRows[0m
+ ▔▔▔▔
+ [1mLeft count[0m[1m [0m[1mRight count[0m
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┳━━━━━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Top[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmedian[0m[1m [0m┃[1m [0m[1mmin[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m std[0m[1m [0m┃[1m [0m[1mmad[0m[1m [0m┃[1m [0m[1m mrd[0m[1m [0m┃[1m [0m[1m Changes[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━╇━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │ [33m40.0[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m42.0[0m (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. [33m4[0m) │
+ │ │ │ │ │ │ │ │ │ │ [33m20.0[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m21.0[0m (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. [33m2[0m) │
+ ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │ [33m4[0m -> [33m5[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, e.g. │
+ │ │ │ │ │ │ │ │ │ │ [33m4[0m) │
+ ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │ [33m"c"[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m"x"[0m (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. [33m3[0m) │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┴───────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..d979e56
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,25 @@
+ [1mRows[0m
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┳━━━━━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Top[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmedian[0m[1m [0m┃[1m [0m[1mmin[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m std[0m[1m [0m┃[1m [0m[1mmad[0m[1m [0m┃[1m [0m[1m mrd[0m[1m [0m┃[1m [0m[1m Changes[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━╇━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │ [33m40.0[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m42.0[0m (1x) │
+ │ │ │ │ │ │ │ │ │ │ [33m20.0[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m21.0[0m (1x) │
+ ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │ [33m4[0m -> [33m5[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │ [33m"c"[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m"x"[0m (1x) │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┴───────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
new file mode 100644
index 0000000..f0ab5a8
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
@@ -0,0 +1,29 @@
+ [1mRows[0m
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┳━━━━━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Top[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmedian[0m[1m [0m┃[1m [0m[1mmin[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m std[0m[1m [0m┃[1m [0m[1mmad[0m[1m [0m┃[1m [0m[1m mrd[0m[1m [0m┃[1m [0m[1m Changes[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━╇━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │ [33m40.0[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m42.0[0m (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. [33m4[0m) │
+ │ │ │ │ │ │ │ │ │ │ [33m20.0[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m21.0[0m (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. [33m2[0m) │
+ ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │ [33m4[0m -> [33m5[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, e.g. │
+ │ │ │ │ │ │ │ │ │ │ [33m4[0m) │
+ ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │ [33m"c"[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m"x"[0m (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. [33m3[0m) │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┴───────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..3726ca2
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,29 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ [1m Diffly Summary [0m ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: [36mid[0m
+
+ [1mSchemas[0m
+ ▔▔▔▔▔▔▔
+ [3mSchemas match exactly (column count: 4).[0m
+
+ [1mRows[0m
+ ▔▔▔▔
+ [1mLeft count[0m[1m [0m[1mRight count[0m
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmedian[0m[1m [0m┃[1m [0m[1mmin[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m std[0m[1m [0m┃[1m [0m[1mmad[0m[1m [0m┃[1m [0m[1m mrd[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
new file mode 100644
index 0000000..3726ca2
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
@@ -0,0 +1,29 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ [1m Diffly Summary [0m ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: [36mid[0m
+
+ [1mSchemas[0m
+ ▔▔▔▔▔▔▔
+ [3mSchemas match exactly (column count: 4).[0m
+
+ [1mRows[0m
+ ▔▔▔▔
+ [1mLeft count[0m[1m [0m[1mRight count[0m
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmedian[0m[1m [0m┃[1m [0m[1mmin[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m std[0m[1m [0m┃[1m [0m[1mmad[0m[1m [0m┃[1m [0m[1m mrd[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..610dfc4
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,17 @@
+ [1mRows[0m
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmedian[0m[1m [0m┃[1m [0m[1mmin[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m std[0m[1m [0m┃[1m [0m[1mmad[0m[1m [0m┃[1m [0m[1m mrd[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
new file mode 100644
index 0000000..610dfc4
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
@@ -0,0 +1,17 @@
+ [1mRows[0m
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┓
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmedian[0m[1m [0m┃[1m [0m[1mmin[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m std[0m[1m [0m┃[1m [0m[1mmad[0m[1m [0m┃[1m [0m[1m mrd[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..e6c2d3a
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,37 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ [1m Diffly Summary [0m ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: [36mid[0m
+
+ [1mSchemas[0m
+ ▔▔▔▔▔▔▔
+ [3mSchemas match exactly (column count: 4).[0m
+
+ [1mRows[0m
+ ▔▔▔▔
+ [1mLeft count[0m[1m [0m[1mRight count[0m
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┳━━━━━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Top[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmedian[0m[1m [0m┃[1m [0m[1mmin[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m std[0m[1m [0m┃[1m [0m[1mmad[0m[1m [0m┃[1m [0m[1m mrd[0m[1m [0m┃[1m [0m[1m Changes[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━╇━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │ [33m40.0[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m42.0[0m (1x) │
+ │ │ │ │ │ │ │ │ │ │ [33m20.0[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m21.0[0m (1x) │
+ ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │ [33m4[0m -> [33m5[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │ [33m"c"[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m"x"[0m (1x) │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┴───────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
new file mode 100644
index 0000000..d310b83
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
@@ -0,0 +1,41 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ [1m Diffly Summary [0m ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: [36mid[0m
+
+ [1mSchemas[0m
+ ▔▔▔▔▔▔▔
+ [3mSchemas match exactly (column count: 4).[0m
+
+ [1mRows[0m
+ ▔▔▔▔
+ [1mLeft count[0m[1m [0m[1mRight count[0m
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┳━━━━━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Top[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmedian[0m[1m [0m┃[1m [0m[1mmin[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m std[0m[1m [0m┃[1m [0m[1mmad[0m[1m [0m┃[1m [0m[1m mrd[0m[1m [0m┃[1m [0m[1m Changes[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━╇━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │ [33m40.0[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m42.0[0m (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. [33m4[0m) │
+ │ │ │ │ │ │ │ │ │ │ [33m20.0[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m21.0[0m (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. [33m2[0m) │
+ ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │ [33m4[0m -> [33m5[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, e.g. │
+ │ │ │ │ │ │ │ │ │ │ [33m4[0m) │
+ ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │ [33m"c"[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m"x"[0m (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. [33m3[0m) │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┴───────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..d979e56
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,25 @@
+ [1mRows[0m
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┳━━━━━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Top[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmedian[0m[1m [0m┃[1m [0m[1mmin[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m std[0m[1m [0m┃[1m [0m[1mmad[0m[1m [0m┃[1m [0m[1m mrd[0m[1m [0m┃[1m [0m[1m Changes[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━╇━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │ [33m40.0[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m42.0[0m (1x) │
+ │ │ │ │ │ │ │ │ │ │ [33m20.0[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m21.0[0m (1x) │
+ ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │ [33m4[0m -> [33m5[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │ [33m"c"[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m"x"[0m (1x) │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┴───────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
new file mode 100644
index 0000000..f0ab5a8
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
@@ -0,0 +1,29 @@
+ [1mRows[0m
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┳━━━━━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Top[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmedian[0m[1m [0m┃[1m [0m[1mmin[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m std[0m[1m [0m┃[1m [0m[1mmad[0m[1m [0m┃[1m [0m[1m mrd[0m[1m [0m┃[1m [0m[1m Changes[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━╇━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │ [33m40.0[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m42.0[0m (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. [33m4[0m) │
+ │ │ │ │ │ │ │ │ │ │ [33m20.0[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m21.0[0m (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. [33m2[0m) │
+ ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │ [33m4[0m -> [33m5[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, e.g. │
+ │ │ │ │ │ │ │ │ │ │ [33m4[0m) │
+ ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │ [33m"c"[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m"x"[0m (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. [33m3[0m) │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┴───────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/test_metrics_presets_many.py b/tests/summary/fixtures/metrics_presets_many/test_metrics_presets_many.py
new file mode 100644
index 0000000..c15162b
--- /dev/null
+++ b/tests/summary/fixtures/metrics_presets_many/test_metrics_presets_many.py
@@ -0,0 +1,41 @@
+# Copyright (c) QuantCo 2025-2026
+# SPDX-License-Identifier: BSD-3-Clause
+
+import polars as pl
+import pytest
+
+from diffly import compare_frames, metrics
+from tests.utils import generate_summaries
+
+
+@pytest.mark.generate
+def test_generate() -> None:
+ left = pl.DataFrame(
+ {
+ "id": [1, 2, 3, 4, 5],
+ "price": [10.0, 20.0, 30.0, 40.0, 50.0],
+ "qty": [1, 2, 3, 4, 5],
+ "status": ["a", "b", "c", "d", "e"],
+ }
+ )
+ right = pl.DataFrame(
+ {
+ "id": [1, 2, 3, 4, 5],
+ "price": [10.0, 21.0, 30.0, 42.0, 50.0],
+ "qty": [1, 2, 3, 5, 5],
+ "status": ["a", "b", "x", "d", "e"],
+ }
+ )
+ comp = compare_frames(left, right, primary_key=["id"])
+ generate_summaries(
+ comp,
+ metrics={
+ "mean": metrics.mean,
+ "median": metrics.median,
+ "min": metrics.min,
+ "max": metrics.max,
+ "std": metrics.std,
+ "mad": metrics.mean_absolute_deviation,
+ "mrd": metrics.mean_relative_deviation,
+ },
+ )
diff --git a/tests/test_metrics.py b/tests/test_metrics.py
index 2239510..4c5e7cc 100644
--- a/tests/test_metrics.py
+++ b/tests/test_metrics.py
@@ -7,6 +7,7 @@
import pytest
from diffly import metrics
+from diffly.metrics import Metric
@pytest.fixture
@@ -15,7 +16,7 @@ def frame() -> pl.DataFrame:
return pl.DataFrame({"l": [1, 2, 3, None], "r": [1, 2, 5, 4]})
-def _apply(metric, frame: pl.DataFrame) -> float:
+def _apply(metric: Metric, frame: pl.DataFrame) -> float:
return frame.select(metric(pl.col("l"), pl.col("r"))).item()
From a1a854e5945e51dce7a299f1b0f8e7164812d80d Mon Sep 17 00:00:00 2001
From: EgeKaraismailogluQC
<128645043+EgeKaraismailogluQC@users.noreply.github.com>
Date: Thu, 30 Apr 2026 17:40:04 +0200
Subject: [PATCH 04/19] DAB-1463
---
diffly/comparison.py | 3 +-
diffly/metrics.py | 20 +++++++
diffly/summary.py | 42 +++++++++------
...alse_sample_rows_False_sample_pk_False.txt | 31 +++++++++++
...False_sample_rows_True_sample_pk_False.txt | 31 +++++++++++
...True_sample_rows_False_sample_pk_False.txt | 19 +++++++
..._True_sample_rows_True_sample_pk_False.txt | 19 +++++++
...alse_sample_rows_False_sample_pk_False.txt | 45 ++++++++++++++++
..._False_sample_rows_True_sample_pk_True.txt | 53 +++++++++++++++++++
...True_sample_rows_False_sample_pk_False.txt | 33 ++++++++++++
...m_True_sample_rows_True_sample_pk_True.txt | 41 ++++++++++++++
...alse_sample_rows_False_sample_pk_False.txt | 31 +++++++++++
...False_sample_rows_True_sample_pk_False.txt | 31 +++++++++++
...True_sample_rows_False_sample_pk_False.txt | 19 +++++++
..._True_sample_rows_True_sample_pk_False.txt | 19 +++++++
...alse_sample_rows_False_sample_pk_False.txt | 45 ++++++++++++++++
..._False_sample_rows_True_sample_pk_True.txt | 53 +++++++++++++++++++
...True_sample_rows_False_sample_pk_False.txt | 33 ++++++++++++
...m_True_sample_rows_True_sample_pk_True.txt | 41 ++++++++++++++
...alse_sample_rows_False_sample_pk_False.txt | 31 +++++++++++
...False_sample_rows_True_sample_pk_False.txt | 31 +++++++++++
...True_sample_rows_False_sample_pk_False.txt | 19 +++++++
..._True_sample_rows_True_sample_pk_False.txt | 19 +++++++
...alse_sample_rows_False_sample_pk_False.txt | 45 ++++++++++++++++
..._False_sample_rows_True_sample_pk_True.txt | 53 +++++++++++++++++++
...True_sample_rows_False_sample_pk_False.txt | 33 ++++++++++++
...m_True_sample_rows_True_sample_pk_True.txt | 41 ++++++++++++++
...alse_sample_rows_False_sample_pk_False.txt | 31 +++++++++++
...False_sample_rows_True_sample_pk_False.txt | 31 +++++++++++
...True_sample_rows_False_sample_pk_False.txt | 19 +++++++
..._True_sample_rows_True_sample_pk_False.txt | 19 +++++++
...alse_sample_rows_False_sample_pk_False.txt | 45 ++++++++++++++++
..._False_sample_rows_True_sample_pk_True.txt | 53 +++++++++++++++++++
...True_sample_rows_False_sample_pk_False.txt | 33 ++++++++++++
...m_True_sample_rows_True_sample_pk_True.txt | 41 ++++++++++++++
.../test_metrics_long_labels.py | 42 +++++++++++++++
tests/summary/test_summary.py | 39 +++++++++++++-
37 files changed, 1215 insertions(+), 19 deletions(-)
create mode 100644 tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
create mode 100644 tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
create mode 100644 tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
create mode 100644 tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
create mode 100644 tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
create mode 100644 tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
create mode 100644 tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
create mode 100644 tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
create mode 100644 tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
create mode 100644 tests/summary/fixtures/metrics_long_labels/test_metrics_long_labels.py
diff --git a/diffly/comparison.py b/diffly/comparison.py
index 6a500a2..562f495 100644
--- a/diffly/comparison.py
+++ b/diffly/comparison.py
@@ -956,7 +956,8 @@ def summary(
numerical column across all joined rows, and must return a scalar
aggregation expression. See :mod:`diffly.metrics` for presets
(``mean``, ``median``, ``mean_absolute_deviation`` etc.). Metrics are
- only computed for numerical columns.
+ only computed for numerical columns. Prefer short labels — the
+ summary has a fixed width and many or long labels degrade rendering.
Returns:
A summary which can be printed or written to a file.
diff --git a/diffly/metrics.py b/diffly/metrics.py
index fd2f8a2..54fca0c 100644
--- a/diffly/metrics.py
+++ b/diffly/metrics.py
@@ -4,8 +4,10 @@
from __future__ import annotations
from collections.abc import Callable
+from dataclasses import dataclass
import polars as pl
+import polars.selectors as cs
Metric = Callable[[pl.Expr, pl.Expr], pl.Expr]
"""A metric is a callable mapping ``(left_expr, right_expr)`` to a scalar aggregation
@@ -16,6 +18,24 @@
"""
+@dataclass(frozen=True)
+class _Metric:
+ """A metric paired with a column-applicability selector.
+
+ Internal only.
+ """
+
+ fn: Metric
+ selector: pl.Expr
+
+ def __call__(self, left: pl.Expr, right: pl.Expr) -> pl.Expr:
+ return self.fn(left, right)
+
+
+def _make_numeric_metric(metric: Metric) -> _Metric:
+ return _Metric(fn=metric, selector=cs.numeric())
+
+
def mean(left: pl.Expr, right: pl.Expr) -> pl.Expr:
"""Mean of ``right - left``."""
return (right - left).mean()
diff --git a/diffly/summary.py b/diffly/summary.py
index 0d3148b..a68ab8a 100644
--- a/diffly/summary.py
+++ b/diffly/summary.py
@@ -13,6 +13,7 @@
from typing import TYPE_CHECKING, Any, Literal, cast
import polars as pl
+import polars.selectors as cs
from rich import box
from rich.columns import Columns as RichColumns
from rich.console import Console, Group, RenderableType
@@ -22,7 +23,7 @@
from rich.text import Text
from ._utils import Side, capitalize_first
-from .metrics import Metric
+from .metrics import Metric, _make_numeric_metric
if TYPE_CHECKING: # pragma: no cover
from .comparison import DataFrameComparison
@@ -940,28 +941,35 @@ def _compute_column_metrics(
if comp.primary_key is None or comp.num_rows_joined() == 0:
return {}
- numeric_cols = [
- c
- for c in comp._other_common_columns
- if comp.left_schema[c].is_numeric() and comp.right_schema[c].is_numeric()
- ]
- out: dict[str, dict[str, Any]] = {c: {} for c in numeric_cols}
- if not numeric_cols:
+ _metrics = {label: _make_numeric_metric(m) for label, m in metrics.items()}
+
+ def select_columns(selector: pl.Expr) -> set[str]:
+ left = set(cs.expand_selector(comp.left_schema, selector))
+ right = set(cs.expand_selector(comp.right_schema, selector))
+ return (left & right) & set(comp._other_common_columns)
+
+ metric_to_columns = {
+ label: select_columns(m.selector) for label, m in _metrics.items()
+ }
+
+ all_columns = sorted(set().union(*metric_to_columns.values()))
+ out: dict[str, dict[str, Any]] = {c: {} for c in all_columns}
+ if not all_columns:
return out
joined = comp.joined(lazy=True)
agg_exprs = [
- metric(
- pl.col(f"{c}_{Side.LEFT}"),
- pl.col(f"{c}_{Side.RIGHT}"),
- ).alias(f"{label}__{c}")
- for label, metric in metrics.items()
- for c in numeric_cols
+ metric.fn(
+ pl.col(f"{column}_{Side.LEFT}"),
+ pl.col(f"{column}_{Side.RIGHT}"),
+ ).alias(f"{label}__{column}")
+ for label, metric in _metrics.items()
+ for column in sorted(metric_to_columns[label])
]
row = joined.select(agg_exprs).collect().row(0, named=True)
- for c in numeric_cols:
- for label in metrics:
- out[c][label] = row[f"{label}__{c}"]
+ for label, columns in metric_to_columns.items():
+ for column in columns:
+ out[column][label] = row[f"{label}__{column}"]
return out
diff --git a/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..1677141
--- /dev/null
+++ b/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,31 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ Diffly Summary ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: id
+
+ Schemas
+ ▔▔▔▔▔▔▔
+ Schemas match exactly (column count: 4).
+
+ Rows
+ ▔▔▔▔
+ Left count Right count
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Mean ┃ Mean ┃
+ ┃ ┃ Match ┃ Mean ┃ Median ┃ Minimum ┃ Maxim… ┃ Standa… ┃ Absol… ┃ Relati… ┃
+ ┃ Column ┃ Rate ┃ Delta ┃ Delta ┃ Delta ┃ Delta ┃ Deviat… ┃ Devia… ┃ Deviat… ┃
+ ┡━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │
+ │ status │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────┴────────┴────────┴─────────┴────────┴─────────┴────────┴─────────┘
diff --git a/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
new file mode 100644
index 0000000..1677141
--- /dev/null
+++ b/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
@@ -0,0 +1,31 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ Diffly Summary ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: id
+
+ Schemas
+ ▔▔▔▔▔▔▔
+ Schemas match exactly (column count: 4).
+
+ Rows
+ ▔▔▔▔
+ Left count Right count
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Mean ┃ Mean ┃
+ ┃ ┃ Match ┃ Mean ┃ Median ┃ Minimum ┃ Maxim… ┃ Standa… ┃ Absol… ┃ Relati… ┃
+ ┃ Column ┃ Rate ┃ Delta ┃ Delta ┃ Delta ┃ Delta ┃ Deviat… ┃ Devia… ┃ Deviat… ┃
+ ┡━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │
+ │ status │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────┴────────┴────────┴─────────┴────────┴─────────┴────────┴─────────┘
diff --git a/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..ecf5309
--- /dev/null
+++ b/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,19 @@
+ Rows
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Mean ┃ Mean ┃
+ ┃ ┃ Match ┃ Mean ┃ Median ┃ Minimum ┃ Maxim… ┃ Standa… ┃ Absol… ┃ Relati… ┃
+ ┃ Column ┃ Rate ┃ Delta ┃ Delta ┃ Delta ┃ Delta ┃ Deviat… ┃ Devia… ┃ Deviat… ┃
+ ┡━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │
+ │ status │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────┴────────┴────────┴─────────┴────────┴─────────┴────────┴─────────┘
diff --git a/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
new file mode 100644
index 0000000..ecf5309
--- /dev/null
+++ b/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
@@ -0,0 +1,19 @@
+ Rows
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Mean ┃ Mean ┃
+ ┃ ┃ Match ┃ Mean ┃ Median ┃ Minimum ┃ Maxim… ┃ Standa… ┃ Absol… ┃ Relati… ┃
+ ┃ Column ┃ Rate ┃ Delta ┃ Delta ┃ Delta ┃ Delta ┃ Deviat… ┃ Devia… ┃ Deviat… ┃
+ ┡━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │
+ │ status │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────┴────────┴────────┴─────────┴────────┴─────────┴────────┴─────────┘
diff --git a/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..588b710
--- /dev/null
+++ b/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,45 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ Diffly Summary ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: id
+
+ Schemas
+ ▔▔▔▔▔▔▔
+ Schemas match exactly (column count: 4).
+
+ Rows
+ ▔▔▔▔
+ Left count Right count
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Mean ┃ Mean ┃ ┃
+ ┃ Colum ┃ Match ┃ Mean ┃ Medi… ┃ Minim… ┃ Maxi… ┃ Stand… ┃ Abso… ┃ Relat… ┃ Top ┃
+ ┃ n ┃ Rate ┃ Delta ┃ Delta ┃ Delta ┃ Delta ┃ Devia… ┃ Devi… ┃ Devia… ┃ Chan… ┃
+ ┡━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━┩
+ │ price │ 60.0… │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │ 40.0 │
+ │ │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ 42.0 │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ │ │ │ │ │ │ │ │ │ │ 20.0 │
+ │ │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ 21.0 │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├───────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┤
+ │ qty │ 80.0… │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │ 4 -> │
+ │ │ │ │ │ │ │ │ │ │ 5 │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├───────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┤
+ │ statu │ 80.0… │ │ │ │ │ │ │ │ "c" │
+ │ s │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ "x" │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ └───────┴───────┴────────┴───────┴────────┴───────┴────────┴───────┴────────┴───────┘
diff --git a/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
new file mode 100644
index 0000000..5e06e38
--- /dev/null
+++ b/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
@@ -0,0 +1,53 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ Diffly Summary ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: id
+
+ Schemas
+ ▔▔▔▔▔▔▔
+ Schemas match exactly (column count: 4).
+
+ Rows
+ ▔▔▔▔
+ Left count Right count
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Mean ┃ Mean ┃ ┃
+ ┃ Colum ┃ Match ┃ Mean ┃ Medi… ┃ Minim… ┃ Maxi… ┃ Stand… ┃ Abso… ┃ Relat… ┃ Top ┃
+ ┃ n ┃ Rate ┃ Delta ┃ Delta ┃ Delta ┃ Delta ┃ Devia… ┃ Devi… ┃ Devia… ┃ Chan… ┃
+ ┡━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━┩
+ │ price │ 60.0… │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │ 40.0 │
+ │ │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ 42.0 │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. │
+ │ │ │ │ │ │ │ │ │ │ 4) │
+ │ │ │ │ │ │ │ │ │ │ 20.0 │
+ │ │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ 21.0 │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. │
+ │ │ │ │ │ │ │ │ │ │ 2) │
+ ├───────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┤
+ │ qty │ 80.0… │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │ 4 -> │
+ │ │ │ │ │ │ │ │ │ │ 5 │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. │
+ │ │ │ │ │ │ │ │ │ │ 4) │
+ ├───────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┤
+ │ statu │ 80.0… │ │ │ │ │ │ │ │ "c" │
+ │ s │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ "x" │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. │
+ │ │ │ │ │ │ │ │ │ │ 3) │
+ └───────┴───────┴────────┴───────┴────────┴───────┴────────┴───────┴────────┴───────┘
diff --git a/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..c0d57e5
--- /dev/null
+++ b/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,33 @@
+ Rows
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Mean ┃ Mean ┃ ┃
+ ┃ Colum ┃ Match ┃ Mean ┃ Medi… ┃ Minim… ┃ Maxi… ┃ Stand… ┃ Abso… ┃ Relat… ┃ Top ┃
+ ┃ n ┃ Rate ┃ Delta ┃ Delta ┃ Delta ┃ Delta ┃ Devia… ┃ Devi… ┃ Devia… ┃ Chan… ┃
+ ┡━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━┩
+ │ price │ 60.0… │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │ 40.0 │
+ │ │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ 42.0 │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ │ │ │ │ │ │ │ │ │ │ 20.0 │
+ │ │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ 21.0 │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├───────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┤
+ │ qty │ 80.0… │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │ 4 -> │
+ │ │ │ │ │ │ │ │ │ │ 5 │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├───────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┤
+ │ statu │ 80.0… │ │ │ │ │ │ │ │ "c" │
+ │ s │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ "x" │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ └───────┴───────┴────────┴───────┴────────┴───────┴────────┴───────┴────────┴───────┘
diff --git a/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
new file mode 100644
index 0000000..4b4140c
--- /dev/null
+++ b/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
@@ -0,0 +1,41 @@
+ Rows
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Mean ┃ Mean ┃ ┃
+ ┃ Colum ┃ Match ┃ Mean ┃ Medi… ┃ Minim… ┃ Maxi… ┃ Stand… ┃ Abso… ┃ Relat… ┃ Top ┃
+ ┃ n ┃ Rate ┃ Delta ┃ Delta ┃ Delta ┃ Delta ┃ Devia… ┃ Devi… ┃ Devia… ┃ Chan… ┃
+ ┡━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━┩
+ │ price │ 60.0… │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │ 40.0 │
+ │ │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ 42.0 │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. │
+ │ │ │ │ │ │ │ │ │ │ 4) │
+ │ │ │ │ │ │ │ │ │ │ 20.0 │
+ │ │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ 21.0 │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. │
+ │ │ │ │ │ │ │ │ │ │ 2) │
+ ├───────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┤
+ │ qty │ 80.0… │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │ 4 -> │
+ │ │ │ │ │ │ │ │ │ │ 5 │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. │
+ │ │ │ │ │ │ │ │ │ │ 4) │
+ ├───────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┤
+ │ statu │ 80.0… │ │ │ │ │ │ │ │ "c" │
+ │ s │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ "x" │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. │
+ │ │ │ │ │ │ │ │ │ │ 3) │
+ └───────┴───────┴────────┴───────┴────────┴───────┴────────┴───────┴────────┴───────┘
diff --git a/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..1677141
--- /dev/null
+++ b/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,31 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ Diffly Summary ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: id
+
+ Schemas
+ ▔▔▔▔▔▔▔
+ Schemas match exactly (column count: 4).
+
+ Rows
+ ▔▔▔▔
+ Left count Right count
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Mean ┃ Mean ┃
+ ┃ ┃ Match ┃ Mean ┃ Median ┃ Minimum ┃ Maxim… ┃ Standa… ┃ Absol… ┃ Relati… ┃
+ ┃ Column ┃ Rate ┃ Delta ┃ Delta ┃ Delta ┃ Delta ┃ Deviat… ┃ Devia… ┃ Deviat… ┃
+ ┡━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │
+ │ status │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────┴────────┴────────┴─────────┴────────┴─────────┴────────┴─────────┘
diff --git a/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
new file mode 100644
index 0000000..1677141
--- /dev/null
+++ b/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
@@ -0,0 +1,31 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ Diffly Summary ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: id
+
+ Schemas
+ ▔▔▔▔▔▔▔
+ Schemas match exactly (column count: 4).
+
+ Rows
+ ▔▔▔▔
+ Left count Right count
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Mean ┃ Mean ┃
+ ┃ ┃ Match ┃ Mean ┃ Median ┃ Minimum ┃ Maxim… ┃ Standa… ┃ Absol… ┃ Relati… ┃
+ ┃ Column ┃ Rate ┃ Delta ┃ Delta ┃ Delta ┃ Delta ┃ Deviat… ┃ Devia… ┃ Deviat… ┃
+ ┡━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │
+ │ status │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────┴────────┴────────┴─────────┴────────┴─────────┴────────┴─────────┘
diff --git a/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..ecf5309
--- /dev/null
+++ b/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,19 @@
+ Rows
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Mean ┃ Mean ┃
+ ┃ ┃ Match ┃ Mean ┃ Median ┃ Minimum ┃ Maxim… ┃ Standa… ┃ Absol… ┃ Relati… ┃
+ ┃ Column ┃ Rate ┃ Delta ┃ Delta ┃ Delta ┃ Delta ┃ Deviat… ┃ Devia… ┃ Deviat… ┃
+ ┡━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │
+ │ status │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────┴────────┴────────┴─────────┴────────┴─────────┴────────┴─────────┘
diff --git a/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
new file mode 100644
index 0000000..ecf5309
--- /dev/null
+++ b/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
@@ -0,0 +1,19 @@
+ Rows
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Mean ┃ Mean ┃
+ ┃ ┃ Match ┃ Mean ┃ Median ┃ Minimum ┃ Maxim… ┃ Standa… ┃ Absol… ┃ Relati… ┃
+ ┃ Column ┃ Rate ┃ Delta ┃ Delta ┃ Delta ┃ Delta ┃ Deviat… ┃ Devia… ┃ Deviat… ┃
+ ┡━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │
+ │ status │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────┴────────┴────────┴─────────┴────────┴─────────┴────────┴─────────┘
diff --git a/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..588b710
--- /dev/null
+++ b/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,45 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ Diffly Summary ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: id
+
+ Schemas
+ ▔▔▔▔▔▔▔
+ Schemas match exactly (column count: 4).
+
+ Rows
+ ▔▔▔▔
+ Left count Right count
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Mean ┃ Mean ┃ ┃
+ ┃ Colum ┃ Match ┃ Mean ┃ Medi… ┃ Minim… ┃ Maxi… ┃ Stand… ┃ Abso… ┃ Relat… ┃ Top ┃
+ ┃ n ┃ Rate ┃ Delta ┃ Delta ┃ Delta ┃ Delta ┃ Devia… ┃ Devi… ┃ Devia… ┃ Chan… ┃
+ ┡━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━┩
+ │ price │ 60.0… │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │ 40.0 │
+ │ │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ 42.0 │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ │ │ │ │ │ │ │ │ │ │ 20.0 │
+ │ │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ 21.0 │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├───────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┤
+ │ qty │ 80.0… │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │ 4 -> │
+ │ │ │ │ │ │ │ │ │ │ 5 │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├───────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┤
+ │ statu │ 80.0… │ │ │ │ │ │ │ │ "c" │
+ │ s │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ "x" │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ └───────┴───────┴────────┴───────┴────────┴───────┴────────┴───────┴────────┴───────┘
diff --git a/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
new file mode 100644
index 0000000..5e06e38
--- /dev/null
+++ b/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
@@ -0,0 +1,53 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ Diffly Summary ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: id
+
+ Schemas
+ ▔▔▔▔▔▔▔
+ Schemas match exactly (column count: 4).
+
+ Rows
+ ▔▔▔▔
+ Left count Right count
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Mean ┃ Mean ┃ ┃
+ ┃ Colum ┃ Match ┃ Mean ┃ Medi… ┃ Minim… ┃ Maxi… ┃ Stand… ┃ Abso… ┃ Relat… ┃ Top ┃
+ ┃ n ┃ Rate ┃ Delta ┃ Delta ┃ Delta ┃ Delta ┃ Devia… ┃ Devi… ┃ Devia… ┃ Chan… ┃
+ ┡━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━┩
+ │ price │ 60.0… │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │ 40.0 │
+ │ │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ 42.0 │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. │
+ │ │ │ │ │ │ │ │ │ │ 4) │
+ │ │ │ │ │ │ │ │ │ │ 20.0 │
+ │ │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ 21.0 │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. │
+ │ │ │ │ │ │ │ │ │ │ 2) │
+ ├───────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┤
+ │ qty │ 80.0… │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │ 4 -> │
+ │ │ │ │ │ │ │ │ │ │ 5 │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. │
+ │ │ │ │ │ │ │ │ │ │ 4) │
+ ├───────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┤
+ │ statu │ 80.0… │ │ │ │ │ │ │ │ "c" │
+ │ s │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ "x" │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. │
+ │ │ │ │ │ │ │ │ │ │ 3) │
+ └───────┴───────┴────────┴───────┴────────┴───────┴────────┴───────┴────────┴───────┘
diff --git a/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..c0d57e5
--- /dev/null
+++ b/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,33 @@
+ Rows
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Mean ┃ Mean ┃ ┃
+ ┃ Colum ┃ Match ┃ Mean ┃ Medi… ┃ Minim… ┃ Maxi… ┃ Stand… ┃ Abso… ┃ Relat… ┃ Top ┃
+ ┃ n ┃ Rate ┃ Delta ┃ Delta ┃ Delta ┃ Delta ┃ Devia… ┃ Devi… ┃ Devia… ┃ Chan… ┃
+ ┡━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━┩
+ │ price │ 60.0… │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │ 40.0 │
+ │ │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ 42.0 │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ │ │ │ │ │ │ │ │ │ │ 20.0 │
+ │ │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ 21.0 │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├───────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┤
+ │ qty │ 80.0… │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │ 4 -> │
+ │ │ │ │ │ │ │ │ │ │ 5 │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├───────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┤
+ │ statu │ 80.0… │ │ │ │ │ │ │ │ "c" │
+ │ s │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ "x" │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ └───────┴───────┴────────┴───────┴────────┴───────┴────────┴───────┴────────┴───────┘
diff --git a/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
new file mode 100644
index 0000000..4b4140c
--- /dev/null
+++ b/tests/summary/fixtures/metrics_long_labels/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
@@ -0,0 +1,41 @@
+ Rows
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃ = ┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃ ≠ ┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ Columns
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Mean ┃ Mean ┃ ┃
+ ┃ Colum ┃ Match ┃ Mean ┃ Medi… ┃ Minim… ┃ Maxi… ┃ Stand… ┃ Abso… ┃ Relat… ┃ Top ┃
+ ┃ n ┃ Rate ┃ Delta ┃ Delta ┃ Delta ┃ Delta ┃ Devia… ┃ Devi… ┃ Devia… ┃ Chan… ┃
+ ┡━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━┩
+ │ price │ 60.0… │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │ 40.0 │
+ │ │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ 42.0 │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. │
+ │ │ │ │ │ │ │ │ │ │ 4) │
+ │ │ │ │ │ │ │ │ │ │ 20.0 │
+ │ │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ 21.0 │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. │
+ │ │ │ │ │ │ │ │ │ │ 2) │
+ ├───────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┤
+ │ qty │ 80.0… │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │ 4 -> │
+ │ │ │ │ │ │ │ │ │ │ 5 │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. │
+ │ │ │ │ │ │ │ │ │ │ 4) │
+ ├───────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┤
+ │ statu │ 80.0… │ │ │ │ │ │ │ │ "c" │
+ │ s │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ "x" │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. │
+ │ │ │ │ │ │ │ │ │ │ 3) │
+ └───────┴───────┴────────┴───────┴────────┴───────┴────────┴───────┴────────┴───────┘
diff --git a/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..0bf5291
--- /dev/null
+++ b/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,31 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ [1m Diffly Summary [0m ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: [36mid[0m
+
+ [1mSchemas[0m
+ ▔▔▔▔▔▔▔
+ [3mSchemas match exactly (column count: 4).[0m
+
+ [1mRows[0m
+ ▔▔▔▔
+ [1mLeft count[0m[1m [0m[1mRight count[0m
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃
+ ┃[1m [0m┃[1m [0m[1m Match[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1mMedian[0m[1m [0m┃[1m [0m[1mMinimum[0m[1m [0m┃[1m [0m[1mMaxim…[0m[1m [0m┃[1m [0m[1mStanda…[0m[1m [0m┃[1m [0m[1mAbsol…[0m[1m [0m┃[1m [0m[1mRelati…[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1m Rate[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1mDeviat…[0m[1m [0m┃[1m [0m[1mDevia…[0m[1m [0m┃[1m [0m[1mDeviat…[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────┴────────┴────────┴─────────┴────────┴─────────┴────────┴─────────┘
diff --git a/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
new file mode 100644
index 0000000..0bf5291
--- /dev/null
+++ b/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
@@ -0,0 +1,31 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ [1m Diffly Summary [0m ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: [36mid[0m
+
+ [1mSchemas[0m
+ ▔▔▔▔▔▔▔
+ [3mSchemas match exactly (column count: 4).[0m
+
+ [1mRows[0m
+ ▔▔▔▔
+ [1mLeft count[0m[1m [0m[1mRight count[0m
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃
+ ┃[1m [0m┃[1m [0m[1m Match[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1mMedian[0m[1m [0m┃[1m [0m[1mMinimum[0m[1m [0m┃[1m [0m[1mMaxim…[0m[1m [0m┃[1m [0m[1mStanda…[0m[1m [0m┃[1m [0m[1mAbsol…[0m[1m [0m┃[1m [0m[1mRelati…[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1m Rate[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1mDeviat…[0m[1m [0m┃[1m [0m[1mDevia…[0m[1m [0m┃[1m [0m[1mDeviat…[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────┴────────┴────────┴─────────┴────────┴─────────┴────────┴─────────┘
diff --git a/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..3e5b24e
--- /dev/null
+++ b/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,19 @@
+ [1mRows[0m
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃
+ ┃[1m [0m┃[1m [0m[1m Match[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1mMedian[0m[1m [0m┃[1m [0m[1mMinimum[0m[1m [0m┃[1m [0m[1mMaxim…[0m[1m [0m┃[1m [0m[1mStanda…[0m[1m [0m┃[1m [0m[1mAbsol…[0m[1m [0m┃[1m [0m[1mRelati…[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1m Rate[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1mDeviat…[0m[1m [0m┃[1m [0m[1mDevia…[0m[1m [0m┃[1m [0m[1mDeviat…[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────┴────────┴────────┴─────────┴────────┴─────────┴────────┴─────────┘
diff --git a/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
new file mode 100644
index 0000000..3e5b24e
--- /dev/null
+++ b/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
@@ -0,0 +1,19 @@
+ [1mRows[0m
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃
+ ┃[1m [0m┃[1m [0m[1m Match[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1mMedian[0m[1m [0m┃[1m [0m[1mMinimum[0m[1m [0m┃[1m [0m[1mMaxim…[0m[1m [0m┃[1m [0m[1mStanda…[0m[1m [0m┃[1m [0m[1mAbsol…[0m[1m [0m┃[1m [0m[1mRelati…[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1m Rate[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1mDeviat…[0m[1m [0m┃[1m [0m[1mDevia…[0m[1m [0m┃[1m [0m[1mDeviat…[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────┴────────┴────────┴─────────┴────────┴─────────┴────────┴─────────┘
diff --git a/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..e51a097
--- /dev/null
+++ b/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,45 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ [1m Diffly Summary [0m ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: [36mid[0m
+
+ [1mSchemas[0m
+ ▔▔▔▔▔▔▔
+ [3mSchemas match exactly (column count: 4).[0m
+
+ [1mRows[0m
+ ▔▔▔▔
+ [1mLeft count[0m[1m [0m[1mRight count[0m
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m┃
+ ┃[1m [0m[1mColum[0m[1m [0m┃[1m [0m[1mMatch[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1mMedi…[0m[1m [0m┃[1m [0m[1mMinim…[0m[1m [0m┃[1m [0m[1mMaxi…[0m[1m [0m┃[1m [0m[1mStand…[0m[1m [0m┃[1m [0m[1mAbso…[0m[1m [0m┃[1m [0m[1mRelat…[0m[1m [0m┃[1m [0m[1m Top[0m[1m [0m┃
+ ┃[1m [0m[1mn [0m[1m [0m┃[1m [0m[1m Rate[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1mDelta[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1mDelta[0m[1m [0m┃[1m [0m[1mDevia…[0m[1m [0m┃[1m [0m[1mDevi…[0m[1m [0m┃[1m [0m[1mDevia…[0m[1m [0m┃[1m [0m[1mChan…[0m[1m [0m┃
+ ┡━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━┩
+ │ [36mprice[0m │ 60.0… │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │ [33m40.0[0m │
+ │ │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ [33m42.0[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ │ │ │ │ │ │ │ │ │ │ [33m20.0[0m │
+ │ │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ [33m21.0[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├───────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┤
+ │ [36mqty [0m │ 80.0… │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │ [33m4[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m5[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├───────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┤
+ │ [36mstatu[0m │ 80.0… │ │ │ │ │ │ │ │ [33m"c"[0m │
+ │ [36ms [0m │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ [33m"x"[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ └───────┴───────┴────────┴───────┴────────┴───────┴────────┴───────┴────────┴───────┘
diff --git a/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
new file mode 100644
index 0000000..6b0fcb3
--- /dev/null
+++ b/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
@@ -0,0 +1,53 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ [1m Diffly Summary [0m ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: [36mid[0m
+
+ [1mSchemas[0m
+ ▔▔▔▔▔▔▔
+ [3mSchemas match exactly (column count: 4).[0m
+
+ [1mRows[0m
+ ▔▔▔▔
+ [1mLeft count[0m[1m [0m[1mRight count[0m
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m┃
+ ┃[1m [0m[1mColum[0m[1m [0m┃[1m [0m[1mMatch[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1mMedi…[0m[1m [0m┃[1m [0m[1mMinim…[0m[1m [0m┃[1m [0m[1mMaxi…[0m[1m [0m┃[1m [0m[1mStand…[0m[1m [0m┃[1m [0m[1mAbso…[0m[1m [0m┃[1m [0m[1mRelat…[0m[1m [0m┃[1m [0m[1m Top[0m[1m [0m┃
+ ┃[1m [0m[1mn [0m[1m [0m┃[1m [0m[1m Rate[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1mDelta[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1mDelta[0m[1m [0m┃[1m [0m[1mDevia…[0m[1m [0m┃[1m [0m[1mDevi…[0m[1m [0m┃[1m [0m[1mDevia…[0m[1m [0m┃[1m [0m[1mChan…[0m[1m [0m┃
+ ┡━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━┩
+ │ [36mprice[0m │ 60.0… │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │ [33m40.0[0m │
+ │ │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ [33m42.0[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. │
+ │ │ │ │ │ │ │ │ │ │ [33m4[0m) │
+ │ │ │ │ │ │ │ │ │ │ [33m20.0[0m │
+ │ │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ [33m21.0[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. │
+ │ │ │ │ │ │ │ │ │ │ [33m2[0m) │
+ ├───────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┤
+ │ [36mqty [0m │ 80.0… │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │ [33m4[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m5[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. │
+ │ │ │ │ │ │ │ │ │ │ [33m4[0m) │
+ ├───────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┤
+ │ [36mstatu[0m │ 80.0… │ │ │ │ │ │ │ │ [33m"c"[0m │
+ │ [36ms [0m │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ [33m"x"[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. │
+ │ │ │ │ │ │ │ │ │ │ [33m3[0m) │
+ └───────┴───────┴────────┴───────┴────────┴───────┴────────┴───────┴────────┴───────┘
diff --git a/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..48b4417
--- /dev/null
+++ b/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,33 @@
+ [1mRows[0m
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m┃
+ ┃[1m [0m[1mColum[0m[1m [0m┃[1m [0m[1mMatch[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1mMedi…[0m[1m [0m┃[1m [0m[1mMinim…[0m[1m [0m┃[1m [0m[1mMaxi…[0m[1m [0m┃[1m [0m[1mStand…[0m[1m [0m┃[1m [0m[1mAbso…[0m[1m [0m┃[1m [0m[1mRelat…[0m[1m [0m┃[1m [0m[1m Top[0m[1m [0m┃
+ ┃[1m [0m[1mn [0m[1m [0m┃[1m [0m[1m Rate[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1mDelta[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1mDelta[0m[1m [0m┃[1m [0m[1mDevia…[0m[1m [0m┃[1m [0m[1mDevi…[0m[1m [0m┃[1m [0m[1mDevia…[0m[1m [0m┃[1m [0m[1mChan…[0m[1m [0m┃
+ ┡━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━┩
+ │ [36mprice[0m │ 60.0… │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │ [33m40.0[0m │
+ │ │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ [33m42.0[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ │ │ │ │ │ │ │ │ │ │ [33m20.0[0m │
+ │ │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ [33m21.0[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├───────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┤
+ │ [36mqty [0m │ 80.0… │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │ [33m4[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m5[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├───────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┤
+ │ [36mstatu[0m │ 80.0… │ │ │ │ │ │ │ │ [33m"c"[0m │
+ │ [36ms [0m │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ [33m"x"[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ └───────┴───────┴────────┴───────┴────────┴───────┴────────┴───────┴────────┴───────┘
diff --git a/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
new file mode 100644
index 0000000..c77a139
--- /dev/null
+++ b/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
@@ -0,0 +1,41 @@
+ [1mRows[0m
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m┃
+ ┃[1m [0m[1mColum[0m[1m [0m┃[1m [0m[1mMatch[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1mMedi…[0m[1m [0m┃[1m [0m[1mMinim…[0m[1m [0m┃[1m [0m[1mMaxi…[0m[1m [0m┃[1m [0m[1mStand…[0m[1m [0m┃[1m [0m[1mAbso…[0m[1m [0m┃[1m [0m[1mRelat…[0m[1m [0m┃[1m [0m[1m Top[0m[1m [0m┃
+ ┃[1m [0m[1mn [0m[1m [0m┃[1m [0m[1m Rate[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1mDelta[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1mDelta[0m[1m [0m┃[1m [0m[1mDevia…[0m[1m [0m┃[1m [0m[1mDevi…[0m[1m [0m┃[1m [0m[1mDevia…[0m[1m [0m┃[1m [0m[1mChan…[0m[1m [0m┃
+ ┡━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━┩
+ │ [36mprice[0m │ 60.0… │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │ [33m40.0[0m │
+ │ │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ [33m42.0[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. │
+ │ │ │ │ │ │ │ │ │ │ [33m4[0m) │
+ │ │ │ │ │ │ │ │ │ │ [33m20.0[0m │
+ │ │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ [33m21.0[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. │
+ │ │ │ │ │ │ │ │ │ │ [33m2[0m) │
+ ├───────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┤
+ │ [36mqty [0m │ 80.0… │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │ [33m4[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m5[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. │
+ │ │ │ │ │ │ │ │ │ │ [33m4[0m) │
+ ├───────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┤
+ │ [36mstatu[0m │ 80.0… │ │ │ │ │ │ │ │ [33m"c"[0m │
+ │ [36ms [0m │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ [33m"x"[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. │
+ │ │ │ │ │ │ │ │ │ │ [33m3[0m) │
+ └───────┴───────┴────────┴───────┴────────┴───────┴────────┴───────┴────────┴───────┘
diff --git a/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..0bf5291
--- /dev/null
+++ b/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,31 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ [1m Diffly Summary [0m ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: [36mid[0m
+
+ [1mSchemas[0m
+ ▔▔▔▔▔▔▔
+ [3mSchemas match exactly (column count: 4).[0m
+
+ [1mRows[0m
+ ▔▔▔▔
+ [1mLeft count[0m[1m [0m[1mRight count[0m
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃
+ ┃[1m [0m┃[1m [0m[1m Match[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1mMedian[0m[1m [0m┃[1m [0m[1mMinimum[0m[1m [0m┃[1m [0m[1mMaxim…[0m[1m [0m┃[1m [0m[1mStanda…[0m[1m [0m┃[1m [0m[1mAbsol…[0m[1m [0m┃[1m [0m[1mRelati…[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1m Rate[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1mDeviat…[0m[1m [0m┃[1m [0m[1mDevia…[0m[1m [0m┃[1m [0m[1mDeviat…[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────┴────────┴────────┴─────────┴────────┴─────────┴────────┴─────────┘
diff --git a/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
new file mode 100644
index 0000000..0bf5291
--- /dev/null
+++ b/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
@@ -0,0 +1,31 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ [1m Diffly Summary [0m ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: [36mid[0m
+
+ [1mSchemas[0m
+ ▔▔▔▔▔▔▔
+ [3mSchemas match exactly (column count: 4).[0m
+
+ [1mRows[0m
+ ▔▔▔▔
+ [1mLeft count[0m[1m [0m[1mRight count[0m
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃
+ ┃[1m [0m┃[1m [0m[1m Match[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1mMedian[0m[1m [0m┃[1m [0m[1mMinimum[0m[1m [0m┃[1m [0m[1mMaxim…[0m[1m [0m┃[1m [0m[1mStanda…[0m[1m [0m┃[1m [0m[1mAbsol…[0m[1m [0m┃[1m [0m[1mRelati…[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1m Rate[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1mDeviat…[0m[1m [0m┃[1m [0m[1mDevia…[0m[1m [0m┃[1m [0m[1mDeviat…[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────┴────────┴────────┴─────────┴────────┴─────────┴────────┴─────────┘
diff --git a/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..3e5b24e
--- /dev/null
+++ b/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,19 @@
+ [1mRows[0m
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃
+ ┃[1m [0m┃[1m [0m[1m Match[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1mMedian[0m[1m [0m┃[1m [0m[1mMinimum[0m[1m [0m┃[1m [0m[1mMaxim…[0m[1m [0m┃[1m [0m[1mStanda…[0m[1m [0m┃[1m [0m[1mAbsol…[0m[1m [0m┃[1m [0m[1mRelati…[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1m Rate[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1mDeviat…[0m[1m [0m┃[1m [0m[1mDevia…[0m[1m [0m┃[1m [0m[1mDeviat…[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────┴────────┴────────┴─────────┴────────┴─────────┴────────┴─────────┘
diff --git a/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
new file mode 100644
index 0000000..3e5b24e
--- /dev/null
+++ b/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
@@ -0,0 +1,19 @@
+ [1mRows[0m
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃
+ ┃[1m [0m┃[1m [0m[1m Match[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1mMedian[0m[1m [0m┃[1m [0m[1mMinimum[0m[1m [0m┃[1m [0m[1mMaxim…[0m[1m [0m┃[1m [0m[1mStanda…[0m[1m [0m┃[1m [0m[1mAbsol…[0m[1m [0m┃[1m [0m[1mRelati…[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1m Rate[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1mDeviat…[0m[1m [0m┃[1m [0m[1mDevia…[0m[1m [0m┃[1m [0m[1mDeviat…[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────┴────────┴────────┴─────────┴────────┴─────────┴────────┴─────────┘
diff --git a/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..e51a097
--- /dev/null
+++ b/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,45 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ [1m Diffly Summary [0m ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: [36mid[0m
+
+ [1mSchemas[0m
+ ▔▔▔▔▔▔▔
+ [3mSchemas match exactly (column count: 4).[0m
+
+ [1mRows[0m
+ ▔▔▔▔
+ [1mLeft count[0m[1m [0m[1mRight count[0m
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m┃
+ ┃[1m [0m[1mColum[0m[1m [0m┃[1m [0m[1mMatch[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1mMedi…[0m[1m [0m┃[1m [0m[1mMinim…[0m[1m [0m┃[1m [0m[1mMaxi…[0m[1m [0m┃[1m [0m[1mStand…[0m[1m [0m┃[1m [0m[1mAbso…[0m[1m [0m┃[1m [0m[1mRelat…[0m[1m [0m┃[1m [0m[1m Top[0m[1m [0m┃
+ ┃[1m [0m[1mn [0m[1m [0m┃[1m [0m[1m Rate[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1mDelta[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1mDelta[0m[1m [0m┃[1m [0m[1mDevia…[0m[1m [0m┃[1m [0m[1mDevi…[0m[1m [0m┃[1m [0m[1mDevia…[0m[1m [0m┃[1m [0m[1mChan…[0m[1m [0m┃
+ ┡━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━┩
+ │ [36mprice[0m │ 60.0… │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │ [33m40.0[0m │
+ │ │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ [33m42.0[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ │ │ │ │ │ │ │ │ │ │ [33m20.0[0m │
+ │ │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ [33m21.0[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├───────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┤
+ │ [36mqty [0m │ 80.0… │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │ [33m4[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m5[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├───────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┤
+ │ [36mstatu[0m │ 80.0… │ │ │ │ │ │ │ │ [33m"c"[0m │
+ │ [36ms [0m │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ [33m"x"[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ └───────┴───────┴────────┴───────┴────────┴───────┴────────┴───────┴────────┴───────┘
diff --git a/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
new file mode 100644
index 0000000..6b0fcb3
--- /dev/null
+++ b/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
@@ -0,0 +1,53 @@
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃ [1m Diffly Summary [0m ┃
+┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ Primary key: [36mid[0m
+
+ [1mSchemas[0m
+ ▔▔▔▔▔▔▔
+ [3mSchemas match exactly (column count: 4).[0m
+
+ [1mRows[0m
+ ▔▔▔▔
+ [1mLeft count[0m[1m [0m[1mRight count[0m
+ 5 (no change) 5
+
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m┃
+ ┃[1m [0m[1mColum[0m[1m [0m┃[1m [0m[1mMatch[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1mMedi…[0m[1m [0m┃[1m [0m[1mMinim…[0m[1m [0m┃[1m [0m[1mMaxi…[0m[1m [0m┃[1m [0m[1mStand…[0m[1m [0m┃[1m [0m[1mAbso…[0m[1m [0m┃[1m [0m[1mRelat…[0m[1m [0m┃[1m [0m[1m Top[0m[1m [0m┃
+ ┃[1m [0m[1mn [0m[1m [0m┃[1m [0m[1m Rate[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1mDelta[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1mDelta[0m[1m [0m┃[1m [0m[1mDevia…[0m[1m [0m┃[1m [0m[1mDevi…[0m[1m [0m┃[1m [0m[1mDevia…[0m[1m [0m┃[1m [0m[1mChan…[0m[1m [0m┃
+ ┡━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━┩
+ │ [36mprice[0m │ 60.0… │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │ [33m40.0[0m │
+ │ │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ [33m42.0[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. │
+ │ │ │ │ │ │ │ │ │ │ [33m4[0m) │
+ │ │ │ │ │ │ │ │ │ │ [33m20.0[0m │
+ │ │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ [33m21.0[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. │
+ │ │ │ │ │ │ │ │ │ │ [33m2[0m) │
+ ├───────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┤
+ │ [36mqty [0m │ 80.0… │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │ [33m4[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m5[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. │
+ │ │ │ │ │ │ │ │ │ │ [33m4[0m) │
+ ├───────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┤
+ │ [36mstatu[0m │ 80.0… │ │ │ │ │ │ │ │ [33m"c"[0m │
+ │ [36ms [0m │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ [33m"x"[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. │
+ │ │ │ │ │ │ │ │ │ │ [33m3[0m) │
+ └───────┴───────┴────────┴───────┴────────┴───────┴────────┴───────┴────────┴───────┘
diff --git a/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
new file mode 100644
index 0000000..48b4417
--- /dev/null
+++ b/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
@@ -0,0 +1,33 @@
+ [1mRows[0m
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m┃
+ ┃[1m [0m[1mColum[0m[1m [0m┃[1m [0m[1mMatch[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1mMedi…[0m[1m [0m┃[1m [0m[1mMinim…[0m[1m [0m┃[1m [0m[1mMaxi…[0m[1m [0m┃[1m [0m[1mStand…[0m[1m [0m┃[1m [0m[1mAbso…[0m[1m [0m┃[1m [0m[1mRelat…[0m[1m [0m┃[1m [0m[1m Top[0m[1m [0m┃
+ ┃[1m [0m[1mn [0m[1m [0m┃[1m [0m[1m Rate[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1mDelta[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1mDelta[0m[1m [0m┃[1m [0m[1mDevia…[0m[1m [0m┃[1m [0m[1mDevi…[0m[1m [0m┃[1m [0m[1mDevia…[0m[1m [0m┃[1m [0m[1mChan…[0m[1m [0m┃
+ ┡━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━┩
+ │ [36mprice[0m │ 60.0… │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │ [33m40.0[0m │
+ │ │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ [33m42.0[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ │ │ │ │ │ │ │ │ │ │ [33m20.0[0m │
+ │ │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ [33m21.0[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├───────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┤
+ │ [36mqty [0m │ 80.0… │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │ [33m4[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m5[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├───────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┤
+ │ [36mstatu[0m │ 80.0… │ │ │ │ │ │ │ │ [33m"c"[0m │
+ │ [36ms [0m │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ [33m"x"[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ └───────┴───────┴────────┴───────┴────────┴───────┴────────┴───────┴────────┴───────┘
diff --git a/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
new file mode 100644
index 0000000..c77a139
--- /dev/null
+++ b/tests/summary/fixtures/metrics_long_labels/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
@@ -0,0 +1,41 @@
+ [1mRows[0m
+ ▔▔▔▔
+ ┏━┯━┯━┯━┯━┓╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
+ ┃ │ │ │ │ ┃[1m = [0m┃ │ │ │ │ ┃ 2 equal (40.00%) │
+ ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 5 joined
+ ┃ │ │ │ │ ┃[1m ≠ [0m┃ │ │ │ │ ┃ 3 unequal (60.00%) │
+ ┗━┷━┷━┷━┷━┛╌╌╌┗━┷━┷━┷━┷━┛╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯
+
+ [1mColumns[0m
+ ▔▔▔▔▔▔▔
+ ┏━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m┃
+ ┃[1m [0m[1mColum[0m[1m [0m┃[1m [0m[1mMatch[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1mMedi…[0m[1m [0m┃[1m [0m[1mMinim…[0m[1m [0m┃[1m [0m[1mMaxi…[0m[1m [0m┃[1m [0m[1mStand…[0m[1m [0m┃[1m [0m[1mAbso…[0m[1m [0m┃[1m [0m[1mRelat…[0m[1m [0m┃[1m [0m[1m Top[0m[1m [0m┃
+ ┃[1m [0m[1mn [0m[1m [0m┃[1m [0m[1m Rate[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1mDelta[0m[1m [0m┃[1m [0m[1m Delta[0m[1m [0m┃[1m [0m[1mDelta[0m[1m [0m┃[1m [0m[1mDevia…[0m[1m [0m┃[1m [0m[1mDevi…[0m[1m [0m┃[1m [0m[1mDevia…[0m[1m [0m┃[1m [0m[1mChan…[0m[1m [0m┃
+ ┡━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━┩
+ │ [36mprice[0m │ 60.0… │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │ [33m40.0[0m │
+ │ │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ [33m42.0[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. │
+ │ │ │ │ │ │ │ │ │ │ [33m4[0m) │
+ │ │ │ │ │ │ │ │ │ │ [33m20.0[0m │
+ │ │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ [33m21.0[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. │
+ │ │ │ │ │ │ │ │ │ │ [33m2[0m) │
+ ├───────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┤
+ │ [36mqty [0m │ 80.0… │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │ [33m4[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m5[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. │
+ │ │ │ │ │ │ │ │ │ │ [33m4[0m) │
+ ├───────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┼────────┼───────┤
+ │ [36mstatu[0m │ 80.0… │ │ │ │ │ │ │ │ [33m"c"[0m │
+ │ [36ms [0m │ │ │ │ │ │ │ │ │ -> │
+ │ │ │ │ │ │ │ │ │ │ [33m"x"[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. │
+ │ │ │ │ │ │ │ │ │ │ [33m3[0m) │
+ └───────┴───────┴────────┴───────┴────────┴───────┴────────┴───────┴────────┴───────┘
diff --git a/tests/summary/fixtures/metrics_long_labels/test_metrics_long_labels.py b/tests/summary/fixtures/metrics_long_labels/test_metrics_long_labels.py
new file mode 100644
index 0000000..1972e22
--- /dev/null
+++ b/tests/summary/fixtures/metrics_long_labels/test_metrics_long_labels.py
@@ -0,0 +1,42 @@
+# Copyright (c) QuantCo 2025-2026
+# SPDX-License-Identifier: BSD-3-Clause
+
+import polars as pl
+import pytest
+
+from diffly import compare_frames, metrics
+from tests.utils import generate_summaries
+
+
+@pytest.mark.generate
+def test_generate() -> None:
+ # Stress test: many long metric labels squeezed into a fixed-width summary.
+ left = pl.DataFrame(
+ {
+ "id": [1, 2, 3, 4, 5],
+ "price": [10.0, 20.0, 30.0, 40.0, 50.0],
+ "qty": [1, 2, 3, 4, 5],
+ "status": ["a", "b", "c", "d", "e"],
+ }
+ )
+ right = pl.DataFrame(
+ {
+ "id": [1, 2, 3, 4, 5],
+ "price": [10.0, 21.0, 30.0, 42.0, 50.0],
+ "qty": [1, 2, 3, 5, 5],
+ "status": ["a", "b", "x", "d", "e"],
+ }
+ )
+ comp = compare_frames(left, right, primary_key=["id"])
+ generate_summaries(
+ comp,
+ metrics={
+ "Mean Delta": metrics.mean,
+ "Median Delta": metrics.median,
+ "Minimum Delta": metrics.min,
+ "Maximum Delta": metrics.max,
+ "Standard Deviation": metrics.std,
+ "Mean Absolute Deviation": metrics.mean_absolute_deviation,
+ "Mean Relative Deviation": metrics.mean_relative_deviation,
+ },
+ )
diff --git a/tests/summary/test_summary.py b/tests/summary/test_summary.py
index a4e3fdb..2de2184 100644
--- a/tests/summary/test_summary.py
+++ b/tests/summary/test_summary.py
@@ -11,7 +11,7 @@
import polars as pl
import pytest
-from diffly import compare_frames
+from diffly import compare_frames, metrics
from diffly.comparison import DataFrameComparison
from diffly.summary import _format_fraction_as_percentage, to_json_safe
@@ -253,6 +253,43 @@ def test_summary_data_parametrized(
assert result == expected
+@pytest.mark.parametrize("show_perfect_column_matches", [True, False])
+def test_summary_data_metrics(show_perfect_column_matches: bool) -> None:
+ comp = _make_comparison()
+ summary = comp.summary(
+ show_perfect_column_matches=show_perfect_column_matches,
+ metrics={"mean": metrics.mean, "max": metrics.max},
+ )
+ result = json.loads(summary.to_json())
+
+ # Joined rows (id=1,2,3): value deltas = [0, 5, 0].
+ value_metrics = {"mean": pytest.approx(5 / 3), "max": 5.0}
+
+ expected_columns = []
+ if show_perfect_column_matches:
+ # status is non-numeric → metrics stays None.
+ expected_columns.append(
+ {
+ "name": "status",
+ "match_rate": 1.0,
+ "n_total_changes": 0,
+ "changes": None,
+ "metrics": None,
+ }
+ )
+ expected_columns.append(
+ {
+ "name": "value",
+ "match_rate": pytest.approx(2 / 3),
+ "n_total_changes": 0,
+ "changes": None,
+ "metrics": value_metrics,
+ }
+ )
+
+ assert result["columns"] == expected_columns
+
+
@pytest.mark.parametrize(
"input, expected",
[
From d3cc4f1226e33b016dcb0265a41529cff7dd087a Mon Sep 17 00:00:00 2001
From: EgeKaraismailogluQC
<128645043+EgeKaraismailogluQC@users.noreply.github.com>
Date: Thu, 30 Apr 2026 17:53:35 +0200
Subject: [PATCH 05/19] wip
---
docs/api/summary.rst | 24 +
docs/guides/features/summary.ipynb | 1227 ++++++++++++++--------------
docs/index.md | 1 +
3 files changed, 653 insertions(+), 599 deletions(-)
diff --git a/docs/api/summary.rst b/docs/api/summary.rst
index 8729319..e4eb358 100644
--- a/docs/api/summary.rst
+++ b/docs/api/summary.rst
@@ -19,3 +19,27 @@ Summary
Summary.format
Summary.to_json
+
+Metrics
+=======
+
+.. currentmodule:: diffly.metrics
+
+The ``metrics`` argument of :meth:`~diffly.comparison.DataFrameComparison.summary`
+accepts a mapping from display label to a :data:`Metric` callable. :mod:`diffly.metrics`
+ships a set of presets.
+
+.. autodata:: Metric
+ :no-value:
+
+.. autosummary::
+ :toctree: _gen/
+
+ mean
+ median
+ min
+ max
+ std
+ mean_absolute_deviation
+ mean_relative_deviation
+ quantile
diff --git a/docs/guides/features/summary.ipynb b/docs/guides/features/summary.ipynb
index a55b519..524e7b2 100644
--- a/docs/guides/features/summary.ipynb
+++ b/docs/guides/features/summary.ipynb
@@ -1,613 +1,642 @@
{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Customizing the Summary\n",
- "\n",
- "The `summary()` method provides several options to control what information is displayed.\n",
- "\n",
- "## Scenario\n",
- "\n",
- "We continue with the supermarket data pipeline scenario from the [quickstart](../quickstart.ipynb). To validate a new load, we compare it against the previous one and check that:\n",
- "\n",
- "- New transactions have been added (`right_only` is non-empty).\n",
- "- Old or incorrect entries have been removed (`left_only` is non-empty).\n",
- "- The new entries are the most recent (their timestamps are ≥ everything else).\n",
- "- Existing transactions that appear in both loads still match.\n",
- "\n",
- "Let's set up two data frames representing consecutive data loads:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [],
- "source": [
- "import polars as pl\n",
- "\n",
- "df_previous = pl.read_csv(\"data/previous_load.csv\", try_parse_dates=True)\n",
- "df_current = pl.read_csv(\"data/current_load.csv\", try_parse_dates=True)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Here's a preview of the previous load:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/html": [
- "
\n",
- "
shape: (5, 10)| transaction_id | timestamp | store_id | register_id | product | quantity | unit_price | discount | total | loyalty_card_id |
|---|
| str | datetime[μs] | str | str | str | i64 | f64 | f64 | f64 | str |
| "TXN-001" | 2024-03-01 09:01:00 | "S1" | "R1" | "Milk" | 2 | 1.5 | 0.0 | 3.0 | "LC-1001" |
| "TXN-002" | 2024-03-01 09:15:00 | "S1" | "R1" | "Bread" | 1 | 2.8 | 0.0 | 2.8 | null |
| "TXN-003" | 2024-03-01 10:02:00 | "S1" | "R2" | "Eggs" | 1 | 3.2 | 0.0 | 3.2 | "LC-1003" |
| "TXN-004" | 2024-03-01 10:30:00 | "S1" | "R2" | "Butter" | 3 | 1.9 | 0.1 | 5.6 | "LC-1004" |
| "TXN-005" | 2024-03-01 11:00:00 | "S1" | "R1" | "Cheese" | 1 | 4.5 | 0.0 | 4.5 | null |
"
- ],
- "text/plain": [
- "shape: (5, 10)\n",
- "┌────────────┬────────────┬──────────┬────────────┬───┬────────────┬──────────┬───────┬────────────┐\n",
- "│ transactio ┆ timestamp ┆ store_id ┆ register_i ┆ … ┆ unit_price ┆ discount ┆ total ┆ loyalty_ca │\n",
- "│ n_id ┆ --- ┆ --- ┆ d ┆ ┆ --- ┆ --- ┆ --- ┆ rd_id │\n",
- "│ --- ┆ datetime[μ ┆ str ┆ --- ┆ ┆ f64 ┆ f64 ┆ f64 ┆ --- │\n",
- "│ str ┆ s] ┆ ┆ str ┆ ┆ ┆ ┆ ┆ str │\n",
- "╞════════════╪════════════╪══════════╪════════════╪═══╪════════════╪══════════╪═══════╪════════════╡\n",
- "│ TXN-001 ┆ 2024-03-01 ┆ S1 ┆ R1 ┆ … ┆ 1.5 ┆ 0.0 ┆ 3.0 ┆ LC-1001 │\n",
- "│ ┆ 09:01:00 ┆ ┆ ┆ ┆ ┆ ┆ ┆ │\n",
- "│ TXN-002 ┆ 2024-03-01 ┆ S1 ┆ R1 ┆ … ┆ 2.8 ┆ 0.0 ┆ 2.8 ┆ null │\n",
- "│ ┆ 09:15:00 ┆ ┆ ┆ ┆ ┆ ┆ ┆ │\n",
- "│ TXN-003 ┆ 2024-03-01 ┆ S1 ┆ R2 ┆ … ┆ 3.2 ┆ 0.0 ┆ 3.2 ┆ LC-1003 │\n",
- "│ ┆ 10:02:00 ┆ ┆ ┆ ┆ ┆ ┆ ┆ │\n",
- "│ TXN-004 ┆ 2024-03-01 ┆ S1 ┆ R2 ┆ … ┆ 1.9 ┆ 0.1 ┆ 5.6 ┆ LC-1004 │\n",
- "│ ┆ 10:30:00 ┆ ┆ ┆ ┆ ┆ ┆ ┆ │\n",
- "│ TXN-005 ┆ 2024-03-01 ┆ S1 ┆ R1 ┆ … ┆ 4.5 ┆ 0.0 ┆ 4.5 ┆ null │\n",
- "│ ┆ 11:00:00 ┆ ┆ ┆ ┆ ┆ ┆ ┆ │\n",
- "└────────────┴────────────┴──────────┴────────────┴───┴────────────┴──────────┴───────┴────────────┘"
- ]
- },
- "execution_count": 2,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "df_previous.head()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Now let's compare the two loads:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {},
- "outputs": [],
- "source": [
- "from diffly import compare_frames\n",
- "\n",
- "comparison = compare_frames(df_previous, df_current, primary_key=\"transaction_id\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Default summary\n",
- "\n",
- "The first step in any investigation is looking at the summary. It tells us:\n",
- "\n",
- "- **Schemas match** — both data frames have the same columns and types.\n",
- "- **Rows**: 2 left-only, 2 right-only, 10 joined (6 equal, 4 unequal).\n",
- "- **Column match rates**: `unit_price`, `discount`, `timestamp`, and `total` at 70%, `loyalty_card_id` at 90%. Other columns like `product`, `quantity`, `store_id`, and `register_id` are at 100%."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
- "┃ \u001b[1m Diffly Summary \u001b[0m ┃\n",
- "┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n",
- " Primary key: \u001b[36mtransaction_id\u001b[0m\n",
- "\n",
- " \u001b[1mSchemas\u001b[0m\n",
- " ▔▔▔▔▔▔▔\n",
- " \u001b[3mSchemas match exactly (column count: 10).\u001b[0m\n",
- "\n",
- " \u001b[1mRows\u001b[0m\n",
- " ▔▔▔▔\n",
- " \u001b[1mLeft count\u001b[0m\u001b[1m \u001b[0m\u001b[1mRight count\u001b[0m\n",
- " 12 (no change) 12\n",
- "\n",
- " ┏━┯━┯━┯━┯━┓\n",
- " ┃\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m┃ 2 left only (16.67%)\n",
- " ┠─┼─┼─┼─┼─┨╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮\n",
- " ┃ │ │ │ │ ┃\u001b[1m = \u001b[0m┃ │ │ │ │ ┃ 6 equal (60.00%) │\n",
- " ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 10 joined\n",
- " ┃ │ │ │ │ ┃\u001b[1m ≠ \u001b[0m┃ │ │ │ │ ┃ 4 unequal (40.00%) │\n",
- " ┗━┷━┷━┷━┷━┛╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯\n",
- " ┃\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m┃ 2 right only (16.67%)\n",
- " ┗━┷━┷━┷━┷━┛\n",
- "\n",
- " \u001b[1mColumns\u001b[0m\n",
- " ▔▔▔▔▔▔▔\n",
- " ┌─────────────────┬─────────┐\n",
- " │ \u001b[36mdiscount \u001b[0m │ 70.00% │\n",
- " │ \u001b[36mloyalty_card_id\u001b[0m │ 90.00% │\n",
- " │ \u001b[36mproduct \u001b[0m │ 100.00% │\n",
- " │ \u001b[36mquantity \u001b[0m │ 100.00% │\n",
- " │ \u001b[36mregister_id \u001b[0m │ 100.00% │\n",
- " │ \u001b[36mstore_id \u001b[0m │ 100.00% │\n",
- " │ \u001b[36mtimestamp \u001b[0m │ 70.00% │\n",
- " │ \u001b[36mtotal \u001b[0m │ 70.00% │\n",
- " │ \u001b[36munit_price \u001b[0m │ 70.00% │\n",
- " └─────────────────┴─────────┘\n",
- "\n"
- ]
- }
- ],
- "source": [
- "print(comparison.summary())"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Display options\n",
- "\n",
- "### Custom names\n",
- "\n",
- "Use `left_name` and `right_name` to give meaningful labels to each DataFrame. For our scenario, we're comparing data loads:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
- "┃ \u001b[1m Diffly Summary \u001b[0m ┃\n",
- "┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n",
- " Primary key: \u001b[36mtransaction_id\u001b[0m\n",
- "\n",
- " \u001b[1mSchemas\u001b[0m\n",
- " ▔▔▔▔▔▔▔\n",
- " \u001b[3mSchemas match exactly (column count: 10).\u001b[0m\n",
- "\n",
- " \u001b[1mRows\u001b[0m\n",
- " ▔▔▔▔\n",
- " \u001b[1mPrevious Load count\u001b[0m\u001b[1m \u001b[0m\u001b[1mCurrent Load count\u001b[0m\n",
- " 12 (no change) 12\n",
- "\n",
- " ┏━┯━┯━┯━┯━┓\n",
- " ┃\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m┃ 2 Previous Load only (16.67%)\n",
- " ┠─┼─┼─┼─┼─┨╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮\n",
- " ┃ │ │ │ │ ┃\u001b[1m = \u001b[0m┃ │ │ │ │ ┃ 6 equal (60.00%) │\n",
- " ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 10 joined\n",
- " ┃ │ │ │ │ ┃\u001b[1m ≠ \u001b[0m┃ │ │ │ │ ┃ 4 unequal (40.00%) │\n",
- " ┗━┷━┷━┷━┷━┛╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯\n",
- " ┃\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m┃ 2 Current Load only (16.67%)\n",
- " ┗━┷━┷━┷━┷━┛\n",
- "\n",
- " \u001b[1mColumns\u001b[0m\n",
- " ▔▔▔▔▔▔▔\n",
- " ┌─────────────────┬─────────┐\n",
- " │ \u001b[36mdiscount \u001b[0m │ 70.00% │\n",
- " │ \u001b[36mloyalty_card_id\u001b[0m │ 90.00% │\n",
- " │ \u001b[36mproduct \u001b[0m │ 100.00% │\n",
- " │ \u001b[36mquantity \u001b[0m │ 100.00% │\n",
- " │ \u001b[36mregister_id \u001b[0m │ 100.00% │\n",
- " │ \u001b[36mstore_id \u001b[0m │ 100.00% │\n",
- " │ \u001b[36mtimestamp \u001b[0m │ 70.00% │\n",
- " │ \u001b[36mtotal \u001b[0m │ 70.00% │\n",
- " │ \u001b[36munit_price \u001b[0m │ 70.00% │\n",
- " └─────────────────┴─────────┘\n",
- "\n"
- ]
- }
- ],
- "source": [
- "print(comparison.summary(left_name=\"Previous Load\", right_name=\"Current Load\"))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Showing column value changes\n",
- "\n",
- "Use `top_k_column_changes` to display specific value changes for columns with mismatches:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
- "┃ \u001b[1m Diffly Summary \u001b[0m ┃\n",
- "┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n",
- " Primary key: \u001b[36mtransaction_id\u001b[0m\n",
- "\n",
- " \u001b[1mSchemas\u001b[0m\n",
- " ▔▔▔▔▔▔▔\n",
- " \u001b[3mSchemas match exactly (column count: 10).\u001b[0m\n",
- "\n",
- " \u001b[1mRows\u001b[0m\n",
- " ▔▔▔▔\n",
- " \u001b[1mLeft count\u001b[0m\u001b[1m \u001b[0m\u001b[1mRight count\u001b[0m\n",
- " 12 (no change) 12\n",
- "\n",
- " ┏━┯━┯━┯━┯━┓\n",
- " ┃\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m┃ 2 left only (16.67%)\n",
- " ┠─┼─┼─┼─┼─┨╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮\n",
- " ┃ │ │ │ │ ┃\u001b[1m = \u001b[0m┃ │ │ │ │ ┃ 6 equal (60.00%) │\n",
- " ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 10 joined\n",
- " ┃ │ │ │ │ ┃\u001b[1m ≠ \u001b[0m┃ │ │ │ │ ┃ 4 unequal (40.00%) │\n",
- " ┗━┷━┷━┷━┷━┛╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯\n",
- " ┃\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m┃ 2 right only (16.67%)\n",
- " ┗━┷━┷━┷━┷━┛\n",
- "\n",
- " \u001b[1mColumns\u001b[0m\n",
- " ▔▔▔▔▔▔▔\n",
- " ┌─────────────────┬─────────┬─────────────────────────────────────────────────┐\n",
- " │ \u001b[36mdiscount \u001b[0m │ 70.00% │ \u001b[33m0.0\u001b[0m -> \u001b[33m0.2\u001b[0m (2x) │\n",
- " │ │ │ \u001b[33m0.5\u001b[0m -> \u001b[33m1.0\u001b[0m (1x) │\n",
- " ├─────────────────┼─────────┼─────────────────────────────────────────────────┤\n",
- " │ \u001b[36mloyalty_card_id\u001b[0m │ 90.00% │ \u001b[33m\"LC-1004\"\u001b[0m -> \u001b[33m\"LC-9999\"\u001b[0m (1x) │\n",
- " ├─────────────────┼─────────┼─────────────────────────────────────────────────┤\n",
- " │ \u001b[36mproduct \u001b[0m │ 100.00% │ │\n",
- " ├─────────────────┼─────────┼─────────────────────────────────────────────────┤\n",
- " │ \u001b[36mquantity \u001b[0m │ 100.00% │ │\n",
- " ├─────────────────┼─────────┼─────────────────────────────────────────────────┤\n",
- " │ \u001b[36mregister_id \u001b[0m │ 100.00% │ │\n",
- " ├─────────────────┼─────────┼─────────────────────────────────────────────────┤\n",
- " │ \u001b[36mstore_id \u001b[0m │ 100.00% │ │\n",
- " ├─────────────────┼─────────┼─────────────────────────────────────────────────┤\n",
- " │ \u001b[36mtimestamp \u001b[0m │ 70.00% │ \u001b[33m2024-03-01 12:10:00\u001b[0m -> \u001b[33m2024-03-01 12:10:02\u001b[0m (1x) │\n",
- " │ │ │ \u001b[33m2024-03-01 11:45:00\u001b[0m -> \u001b[33m2024-03-01 11:45:03\u001b[0m (1x) │\n",
- " │ │ │ \u001b[33m2024-03-01 11:20:00\u001b[0m -> \u001b[33m2024-03-01 11:20:02\u001b[0m (1x) │\n",
- " ├─────────────────┼─────────┼─────────────────────────────────────────────────┤\n",
- " │ \u001b[36mtotal \u001b[0m │ 70.00% │ \u001b[33m10.3\u001b[0m -> \u001b[33m20.6\u001b[0m (1x) │\n",
- " │ │ │ \u001b[33m3.0\u001b[0m -> \u001b[33m5.8\u001b[0m (1x) │\n",
- " │ │ │ \u001b[33m0.75\u001b[0m -> \u001b[33m1.3\u001b[0m (1x) │\n",
- " ├─────────────────┼─────────┼─────────────────────────────────────────────────┤\n",
- " │ \u001b[36munit_price \u001b[0m │ 70.00% │ \u001b[33m0.75\u001b[0m -> \u001b[33m1.5\u001b[0m (2x) │\n",
- " │ │ │ \u001b[33m5.4\u001b[0m -> \u001b[33m10.8\u001b[0m (1x) │\n",
- " └─────────────────┴─────────┴─────────────────────────────────────────────────┘\n",
- "\n"
- ]
- }
- ],
- "source": [
- "print(comparison.summary(top_k_column_changes=3))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Hidden columns\n",
- "\n",
- "Use `hidden_columns` to prevent sensitive data from being displayed in the summary. For example, loyalty card IDs should not appear in logs or reports shared across teams:"
- ]
- },
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Customizing the Summary\n",
+ "\n",
+ "The `summary()` method provides several options to control what information is displayed.\n",
+ "\n",
+ "## Scenario\n",
+ "\n",
+ "We continue with the supermarket data pipeline scenario from the [quickstart](../quickstart.ipynb). To validate a new load, we compare it against the previous one and check that:\n",
+ "\n",
+ "- New transactions have been added (`right_only` is non-empty).\n",
+ "- Old or incorrect entries have been removed (`left_only` is non-empty).\n",
+ "- The new entries are the most recent (their timestamps are ≥ everything else).\n",
+ "- Existing transactions that appear in both loads still match.\n",
+ "\n",
+ "Let's set up two data frames representing consecutive data loads:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import polars as pl\n",
+ "\n",
+ "df_previous = pl.read_csv(\"data/previous_load.csv\", try_parse_dates=True)\n",
+ "df_current = pl.read_csv(\"data/current_load.csv\", try_parse_dates=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Here's a preview of the previous load:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
{
- "cell_type": "code",
- "execution_count": 7,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
- "┃ \u001b[1m Diffly Summary \u001b[0m ┃\n",
- "┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n",
- " Primary key: \u001b[36mtransaction_id\u001b[0m\n",
- "\n",
- " \u001b[1mSchemas\u001b[0m\n",
- " ▔▔▔▔▔▔▔\n",
- " \u001b[3mSchemas match exactly (column count: 10).\u001b[0m\n",
- "\n",
- " \u001b[1mRows\u001b[0m\n",
- " ▔▔▔▔\n",
- " \u001b[1mLeft count\u001b[0m\u001b[1m \u001b[0m\u001b[1mRight count\u001b[0m\n",
- " 12 (no change) 12\n",
- "\n",
- " ┏━┯━┯━┯━┯━┓\n",
- " ┃\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m┃ 2 left only (16.67%)\n",
- " ┠─┼─┼─┼─┼─┨╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮\n",
- " ┃ │ │ │ │ ┃\u001b[1m = \u001b[0m┃ │ │ │ │ ┃ 6 equal (60.00%) │\n",
- " ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 10 joined\n",
- " ┃ │ │ │ │ ┃\u001b[1m ≠ \u001b[0m┃ │ │ │ │ ┃ 4 unequal (40.00%) │\n",
- " ┗━┷━┷━┷━┷━┛╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯\n",
- " ┃\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m┃ 2 right only (16.67%)\n",
- " ┗━┷━┷━┷━┷━┛\n",
- "\n",
- " \u001b[1mColumns\u001b[0m\n",
- " ▔▔▔▔▔▔▔\n",
- " ┌─────────────────┬─────────┬─────────────────────────────────────────────────┐\n",
- " │ \u001b[36mdiscount \u001b[0m │ 70.00% │ \u001b[33m0.0\u001b[0m -> \u001b[33m0.2\u001b[0m (2x) │\n",
- " │ │ │ \u001b[33m0.5\u001b[0m -> \u001b[33m1.0\u001b[0m (1x) │\n",
- " ├─────────────────┼─────────┼─────────────────────────────────────────────────┤\n",
- " │ \u001b[36mloyalty_card_id\u001b[0m │ 90.00% │ │\n",
- " ├─────────────────┼─────────┼─────────────────────────────────────────────────┤\n",
- " │ \u001b[36mproduct \u001b[0m │ 100.00% │ │\n",
- " ├─────────────────┼─────────┼─────────────────────────────────────────────────┤\n",
- " │ \u001b[36mquantity \u001b[0m │ 100.00% │ │\n",
- " ├─────────────────┼─────────┼─────────────────────────────────────────────────┤\n",
- " │ \u001b[36mregister_id \u001b[0m │ 100.00% │ │\n",
- " ├─────────────────┼─────────┼─────────────────────────────────────────────────┤\n",
- " │ \u001b[36mstore_id \u001b[0m │ 100.00% │ │\n",
- " ├─────────────────┼─────────┼─────────────────────────────────────────────────┤\n",
- " │ \u001b[36mtimestamp \u001b[0m │ 70.00% │ \u001b[33m2024-03-01 12:10:00\u001b[0m -> \u001b[33m2024-03-01 12:10:02\u001b[0m (1x) │\n",
- " │ │ │ \u001b[33m2024-03-01 11:45:00\u001b[0m -> \u001b[33m2024-03-01 11:45:03\u001b[0m (1x) │\n",
- " │ │ │ \u001b[33m2024-03-01 11:20:00\u001b[0m -> \u001b[33m2024-03-01 11:20:02\u001b[0m (1x) │\n",
- " ├─────────────────┼─────────┼─────────────────────────────────────────────────┤\n",
- " │ \u001b[36mtotal \u001b[0m │ 70.00% │ \u001b[33m10.3\u001b[0m -> \u001b[33m20.6\u001b[0m (1x) │\n",
- " │ │ │ \u001b[33m3.0\u001b[0m -> \u001b[33m5.8\u001b[0m (1x) │\n",
- " │ │ │ \u001b[33m0.75\u001b[0m -> \u001b[33m1.3\u001b[0m (1x) │\n",
- " ├─────────────────┼─────────┼─────────────────────────────────────────────────┤\n",
- " │ \u001b[36munit_price \u001b[0m │ 70.00% │ \u001b[33m0.75\u001b[0m -> \u001b[33m1.5\u001b[0m (2x) │\n",
- " │ │ │ \u001b[33m5.4\u001b[0m -> \u001b[33m10.8\u001b[0m (1x) │\n",
- " └─────────────────┴─────────┴─────────────────────────────────────────────────┘\n",
- "\n"
- ]
- }
+ "data": {
+ "text/html": [
+ "\n",
+ "
shape: (5, 10)| transaction_id | timestamp | store_id | register_id | product | quantity | unit_price | discount | total | loyalty_card_id |
|---|
| str | datetime[μs] | str | str | str | i64 | f64 | f64 | f64 | str |
| "TXN-001" | 2024-03-01 09:01:00 | "S1" | "R1" | "Milk" | 2 | 1.5 | 0.0 | 3.0 | "LC-1001" |
| "TXN-002" | 2024-03-01 09:15:00 | "S1" | "R1" | "Bread" | 1 | 2.8 | 0.0 | 2.8 | null |
| "TXN-003" | 2024-03-01 10:02:00 | "S1" | "R2" | "Eggs" | 1 | 3.2 | 0.0 | 3.2 | "LC-1003" |
| "TXN-004" | 2024-03-01 10:30:00 | "S1" | "R2" | "Butter" | 3 | 1.9 | 0.1 | 5.6 | "LC-1004" |
| "TXN-005" | 2024-03-01 11:00:00 | "S1" | "R1" | "Cheese" | 1 | 4.5 | 0.0 | 4.5 | null |
"
],
- "source": [
- "print(comparison.summary(top_k_column_changes=3, hidden_columns=[\"loyalty_card_id\"]))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Showing sample rows\n",
- "\n",
- "Use `sample_k_rows_only` to display sample primary keys for rows that exist only in one DataFrame:"
+ "text/plain": [
+ "shape: (5, 10)\n",
+ "┌────────────┬────────────┬──────────┬────────────┬───┬────────────┬──────────┬───────┬────────────┐\n",
+ "│ transactio ┆ timestamp ┆ store_id ┆ register_i ┆ … ┆ unit_price ┆ discount ┆ total ┆ loyalty_ca │\n",
+ "│ n_id ┆ --- ┆ --- ┆ d ┆ ┆ --- ┆ --- ┆ --- ┆ rd_id │\n",
+ "│ --- ┆ datetime[μ ┆ str ┆ --- ┆ ┆ f64 ┆ f64 ┆ f64 ┆ --- │\n",
+ "│ str ┆ s] ┆ ┆ str ┆ ┆ ┆ ┆ ┆ str │\n",
+ "╞════════════╪════════════╪══════════╪════════════╪═══╪════════════╪══════════╪═══════╪════════════╡\n",
+ "│ TXN-001 ┆ 2024-03-01 ┆ S1 ┆ R1 ┆ … ┆ 1.5 ┆ 0.0 ┆ 3.0 ┆ LC-1001 │\n",
+ "│ ┆ 09:01:00 ┆ ┆ ┆ ┆ ┆ ┆ ┆ │\n",
+ "│ TXN-002 ┆ 2024-03-01 ┆ S1 ┆ R1 ┆ … ┆ 2.8 ┆ 0.0 ┆ 2.8 ┆ null │\n",
+ "│ ┆ 09:15:00 ┆ ┆ ┆ ┆ ┆ ┆ ┆ │\n",
+ "│ TXN-003 ┆ 2024-03-01 ┆ S1 ┆ R2 ┆ … ┆ 3.2 ┆ 0.0 ┆ 3.2 ┆ LC-1003 │\n",
+ "│ ┆ 10:02:00 ┆ ┆ ┆ ┆ ┆ ┆ ┆ │\n",
+ "│ TXN-004 ┆ 2024-03-01 ┆ S1 ┆ R2 ┆ … ┆ 1.9 ┆ 0.1 ┆ 5.6 ┆ LC-1004 │\n",
+ "│ ┆ 10:30:00 ┆ ┆ ┆ ┆ ┆ ┆ ┆ │\n",
+ "│ TXN-005 ┆ 2024-03-01 ┆ S1 ┆ R1 ┆ … ┆ 4.5 ┆ 0.0 ┆ 4.5 ┆ null │\n",
+ "│ ┆ 11:00:00 ┆ ┆ ┆ ┆ ┆ ┆ ┆ │\n",
+ "└────────────┴────────────┴──────────┴────────────┴───┴────────────┴──────────┴───────┴────────────┘"
]
- },
+ },
+ "execution_count": 2,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_previous.head()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now let's compare the two loads:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from diffly import compare_frames\n",
+ "\n",
+ "comparison = compare_frames(df_previous, df_current, primary_key=\"transaction_id\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Default summary\n",
+ "\n",
+ "The first step in any investigation is looking at the summary. It tells us:\n",
+ "\n",
+ "- **Schemas match** — both data frames have the same columns and types.\n",
+ "- **Rows**: 2 left-only, 2 right-only, 10 joined (6 equal, 4 unequal).\n",
+ "- **Column match rates**: `unit_price`, `discount`, `timestamp`, and `total` at 70%, `loyalty_card_id` at 90%. Other columns like `product`, `quantity`, `store_id`, and `register_id` are at 100%."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
{
- "cell_type": "code",
- "execution_count": 8,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
- "┃ \u001b[1m Diffly Summary \u001b[0m ┃\n",
- "┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n",
- " Primary key: \u001b[36mtransaction_id\u001b[0m\n",
- "\n",
- " \u001b[1mSchemas\u001b[0m\n",
- " ▔▔▔▔▔▔▔\n",
- " \u001b[3mSchemas match exactly (column count: 10).\u001b[0m\n",
- "\n",
- " \u001b[1mRows\u001b[0m\n",
- " ▔▔▔▔\n",
- " \u001b[1mLeft count\u001b[0m\u001b[1m \u001b[0m\u001b[1mRight count\u001b[0m\n",
- " 12 (no change) 12\n",
- "\n",
- " ┏━┯━┯━┯━┯━┓\n",
- " ┃\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m┃ 2 left only (16.67%)\n",
- " ┠─┼─┼─┼─┼─┨╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮\n",
- " ┃ │ │ │ │ ┃\u001b[1m = \u001b[0m┃ │ │ │ │ ┃ 6 equal (60.00%) │\n",
- " ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 10 joined\n",
- " ┃ │ │ │ │ ┃\u001b[1m ≠ \u001b[0m┃ │ │ │ │ ┃ 4 unequal (40.00%) │\n",
- " ┗━┷━┷━┷━┷━┛╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯\n",
- " ┃\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m┃ 2 right only (16.67%)\n",
- " ┗━┷━┷━┷━┷━┛\n",
- "\n",
- " \u001b[1mColumns\u001b[0m\n",
- " ▔▔▔▔▔▔▔\n",
- " ┌─────────────────┬─────────┐\n",
- " │ \u001b[36mdiscount \u001b[0m │ 70.00% │\n",
- " │ \u001b[36mloyalty_card_id\u001b[0m │ 90.00% │\n",
- " │ \u001b[36mproduct \u001b[0m │ 100.00% │\n",
- " │ \u001b[36mquantity \u001b[0m │ 100.00% │\n",
- " │ \u001b[36mregister_id \u001b[0m │ 100.00% │\n",
- " │ \u001b[36mstore_id \u001b[0m │ 100.00% │\n",
- " │ \u001b[36mtimestamp \u001b[0m │ 70.00% │\n",
- " │ \u001b[36mtotal \u001b[0m │ 70.00% │\n",
- " │ \u001b[36munit_price \u001b[0m │ 70.00% │\n",
- " └─────────────────┴─────────┘\n",
- "\n",
- " \u001b[1mRows left only\u001b[0m\n",
- " ▔▔▔▔▔▔▔▔▔▔▔▔▔▔\n",
- " ┏━━━━━━━━━━━━━━━━┓\n",
- " ┃\u001b[1m \u001b[0m\u001b[1mtransaction_id\u001b[0m\u001b[1m \u001b[0m┃\n",
- " ┡━━━━━━━━━━━━━━━━┩\n",
- " │ TXN-011 │\n",
- " │ TXN-012 │\n",
- " └────────────────┘\n",
- "\n",
- " \u001b[1mRows right only\u001b[0m\n",
- " ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\n",
- " ┏━━━━━━━━━━━━━━━━┓\n",
- " ┃\u001b[1m \u001b[0m\u001b[1mtransaction_id\u001b[0m\u001b[1m \u001b[0m┃\n",
- " ┡━━━━━━━━━━━━━━━━┩\n",
- " │ TXN-013 │\n",
- " │ TXN-014 │\n",
- " └────────────────┘\n",
- "\n"
- ]
- }
- ],
- "source": [
- "print(comparison.summary(sample_k_rows_only=5))"
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
+ "┃ \u001b[1m Diffly Summary \u001b[0m ┃\n",
+ "┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n",
+ " Primary key: \u001b[36mtransaction_id\u001b[0m\n",
+ "\n",
+ " \u001b[1mSchemas\u001b[0m\n",
+ " ▔▔▔▔▔▔▔\n",
+ " \u001b[3mSchemas match exactly (column count: 10).\u001b[0m\n",
+ "\n",
+ " \u001b[1mRows\u001b[0m\n",
+ " ▔▔▔▔\n",
+ " \u001b[1mLeft count\u001b[0m\u001b[1m \u001b[0m\u001b[1mRight count\u001b[0m\n",
+ " 12 (no change) 12\n",
+ "\n",
+ " ┏━┯━┯━┯━┯━┓\n",
+ " ┃\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m┃ 2 left only (16.67%)\n",
+ " ┠─┼─┼─┼─┼─┨╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮\n",
+ " ┃ │ │ │ │ ┃\u001b[1m = \u001b[0m┃ │ │ │ │ ┃ 6 equal (60.00%) │\n",
+ " ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 10 joined\n",
+ " ┃ │ │ │ │ ┃\u001b[1m ≠ \u001b[0m┃ │ │ │ │ ┃ 4 unequal (40.00%) │\n",
+ " ┗━┷━┷━┷━┷━┛╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯\n",
+ " ┃\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m┃ 2 right only (16.67%)\n",
+ " ┗━┷━┷━┷━┷━┛\n",
+ "\n",
+ " \u001b[1mColumns\u001b[0m\n",
+ " ▔▔▔▔▔▔▔\n",
+ " ┌─────────────────┬─────────┐\n",
+ " │ \u001b[36mdiscount \u001b[0m │ 70.00% │\n",
+ " │ \u001b[36mloyalty_card_id\u001b[0m │ 90.00% │\n",
+ " │ \u001b[36mproduct \u001b[0m │ 100.00% │\n",
+ " │ \u001b[36mquantity \u001b[0m │ 100.00% │\n",
+ " │ \u001b[36mregister_id \u001b[0m │ 100.00% │\n",
+ " │ \u001b[36mstore_id \u001b[0m │ 100.00% │\n",
+ " │ \u001b[36mtimestamp \u001b[0m │ 70.00% │\n",
+ " │ \u001b[36mtotal \u001b[0m │ 70.00% │\n",
+ " │ \u001b[36munit_price \u001b[0m │ 70.00% │\n",
+ " └─────────────────┴─────────┘\n",
+ "\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(comparison.summary())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Display options\n",
+ "\n",
+ "### Custom names\n",
+ "\n",
+ "Use `left_name` and `right_name` to give meaningful labels to each DataFrame. For our scenario, we're comparing data loads:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
{
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Hiding perfect matches\n",
- "\n",
- "For wide data frames, set `show_perfect_column_matches=False` to hide columns with 100% match rate:"
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
+ "┃ \u001b[1m Diffly Summary \u001b[0m ┃\n",
+ "┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n",
+ " Primary key: \u001b[36mtransaction_id\u001b[0m\n",
+ "\n",
+ " \u001b[1mSchemas\u001b[0m\n",
+ " ▔▔▔▔▔▔▔\n",
+ " \u001b[3mSchemas match exactly (column count: 10).\u001b[0m\n",
+ "\n",
+ " \u001b[1mRows\u001b[0m\n",
+ " ▔▔▔▔\n",
+ " \u001b[1mPrevious Load count\u001b[0m\u001b[1m \u001b[0m\u001b[1mCurrent Load count\u001b[0m\n",
+ " 12 (no change) 12\n",
+ "\n",
+ " ┏━┯━┯━┯━┯━┓\n",
+ " ┃\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m┃ 2 Previous Load only (16.67%)\n",
+ " ┠─┼─┼─┼─┼─┨╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮\n",
+ " ┃ │ │ │ │ ┃\u001b[1m = \u001b[0m┃ │ │ │ │ ┃ 6 equal (60.00%) │\n",
+ " ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 10 joined\n",
+ " ┃ │ │ │ │ ┃\u001b[1m ≠ \u001b[0m┃ │ │ │ │ ┃ 4 unequal (40.00%) │\n",
+ " ┗━┷━┷━┷━┷━┛╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯\n",
+ " ┃\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m┃ 2 Current Load only (16.67%)\n",
+ " ┗━┷━┷━┷━┷━┛\n",
+ "\n",
+ " \u001b[1mColumns\u001b[0m\n",
+ " ▔▔▔▔▔▔▔\n",
+ " ┌─────────────────┬─────────┐\n",
+ " │ \u001b[36mdiscount \u001b[0m │ 70.00% │\n",
+ " │ \u001b[36mloyalty_card_id\u001b[0m │ 90.00% │\n",
+ " │ \u001b[36mproduct \u001b[0m │ 100.00% │\n",
+ " │ \u001b[36mquantity \u001b[0m │ 100.00% │\n",
+ " │ \u001b[36mregister_id \u001b[0m │ 100.00% │\n",
+ " │ \u001b[36mstore_id \u001b[0m │ 100.00% │\n",
+ " │ \u001b[36mtimestamp \u001b[0m │ 70.00% │\n",
+ " │ \u001b[36mtotal \u001b[0m │ 70.00% │\n",
+ " │ \u001b[36munit_price \u001b[0m │ 70.00% │\n",
+ " └─────────────────┴─────────┘\n",
+ "\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(comparison.summary(left_name=\"Previous Load\", right_name=\"Current Load\"))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Showing column value changes\n",
+ "\n",
+ "Use `top_k_column_changes` to display specific value changes for columns with mismatches:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [
{
- "cell_type": "code",
- "execution_count": 9,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
- "┃ \u001b[1m Diffly Summary \u001b[0m ┃\n",
- "┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n",
- " Primary key: \u001b[36mtransaction_id\u001b[0m\n",
- "\n",
- " \u001b[1mSchemas\u001b[0m\n",
- " ▔▔▔▔▔▔▔\n",
- " \u001b[3mSchemas match exactly (column count: 10).\u001b[0m\n",
- "\n",
- " \u001b[1mRows\u001b[0m\n",
- " ▔▔▔▔\n",
- " \u001b[1mLeft count\u001b[0m\u001b[1m \u001b[0m\u001b[1mRight count\u001b[0m\n",
- " 12 (no change) 12\n",
- "\n",
- " ┏━┯━┯━┯━┯━┓\n",
- " ┃\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m┃ 2 left only (16.67%)\n",
- " ┠─┼─┼─┼─┼─┨╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮\n",
- " ┃ │ │ │ │ ┃\u001b[1m = \u001b[0m┃ │ │ │ │ ┃ 6 equal (60.00%) │\n",
- " ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 10 joined\n",
- " ┃ │ │ │ │ ┃\u001b[1m ≠ \u001b[0m┃ │ │ │ │ ┃ 4 unequal (40.00%) │\n",
- " ┗━┷━┷━┷━┷━┛╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯\n",
- " ┃\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m┃ 2 right only (16.67%)\n",
- " ┗━┷━┷━┷━┷━┛\n",
- "\n",
- " \u001b[1mColumns\u001b[0m\n",
- " ▔▔▔▔▔▔▔\n",
- " ┌─────────────────┬────────┐\n",
- " │ \u001b[36mdiscount \u001b[0m │ 70.00% │\n",
- " │ \u001b[36mloyalty_card_id\u001b[0m │ 90.00% │\n",
- " │ \u001b[36mtimestamp \u001b[0m │ 70.00% │\n",
- " │ \u001b[36mtotal \u001b[0m │ 70.00% │\n",
- " │ \u001b[36munit_price \u001b[0m │ 70.00% │\n",
- " └─────────────────┴────────┘\n",
- "\n"
- ]
- }
- ],
- "source": [
- "print(comparison.summary(show_perfect_column_matches=False))"
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
+ "┃ \u001b[1m Diffly Summary \u001b[0m ┃\n",
+ "┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n",
+ " Primary key: \u001b[36mtransaction_id\u001b[0m\n",
+ "\n",
+ " \u001b[1mSchemas\u001b[0m\n",
+ " ▔▔▔▔▔▔▔\n",
+ " \u001b[3mSchemas match exactly (column count: 10).\u001b[0m\n",
+ "\n",
+ " \u001b[1mRows\u001b[0m\n",
+ " ▔▔▔▔\n",
+ " \u001b[1mLeft count\u001b[0m\u001b[1m \u001b[0m\u001b[1mRight count\u001b[0m\n",
+ " 12 (no change) 12\n",
+ "\n",
+ " ┏━┯━┯━┯━┯━┓\n",
+ " ┃\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m┃ 2 left only (16.67%)\n",
+ " ┠─┼─┼─┼─┼─┨╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮\n",
+ " ┃ │ │ │ │ ┃\u001b[1m = \u001b[0m┃ │ │ │ │ ┃ 6 equal (60.00%) │\n",
+ " ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 10 joined\n",
+ " ┃ │ │ │ │ ┃\u001b[1m ≠ \u001b[0m┃ │ │ │ │ ┃ 4 unequal (40.00%) │\n",
+ " ┗━┷━┷━┷━┷━┛╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯\n",
+ " ┃\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m┃ 2 right only (16.67%)\n",
+ " ┗━┷━┷━┷━┷━┛\n",
+ "\n",
+ " \u001b[1mColumns\u001b[0m\n",
+ " ▔▔▔▔▔▔▔\n",
+ " ┌─────────────────┬─────────┬─────────────────────────────────────────────────┐\n",
+ " │ \u001b[36mdiscount \u001b[0m │ 70.00% │ \u001b[33m0.0\u001b[0m -> \u001b[33m0.2\u001b[0m (2x) │\n",
+ " │ │ │ \u001b[33m0.5\u001b[0m -> \u001b[33m1.0\u001b[0m (1x) │\n",
+ " ├─────────────────┼─────────┼─────────────────────────────────────────────────┤\n",
+ " │ \u001b[36mloyalty_card_id\u001b[0m │ 90.00% │ \u001b[33m\"LC-1004\"\u001b[0m -> \u001b[33m\"LC-9999\"\u001b[0m (1x) │\n",
+ " ├─────────────────┼─────────┼─────────────────────────────────────────────────┤\n",
+ " │ \u001b[36mproduct \u001b[0m │ 100.00% │ │\n",
+ " ├─────────────────┼─────────┼─────────────────────────────────────────────────┤\n",
+ " │ \u001b[36mquantity \u001b[0m │ 100.00% │ │\n",
+ " ├─────────────────┼─────────┼─────────────────────────────────────────────────┤\n",
+ " │ \u001b[36mregister_id \u001b[0m │ 100.00% │ │\n",
+ " ├─────────────────┼─────────┼─────────────────────────────────────────────────┤\n",
+ " │ \u001b[36mstore_id \u001b[0m │ 100.00% │ │\n",
+ " ├─────────────────┼─────────┼─────────────────────────────────────────────────┤\n",
+ " │ \u001b[36mtimestamp \u001b[0m │ 70.00% │ \u001b[33m2024-03-01 12:10:00\u001b[0m -> \u001b[33m2024-03-01 12:10:02\u001b[0m (1x) │\n",
+ " │ │ │ \u001b[33m2024-03-01 11:45:00\u001b[0m -> \u001b[33m2024-03-01 11:45:03\u001b[0m (1x) │\n",
+ " │ │ │ \u001b[33m2024-03-01 11:20:00\u001b[0m -> \u001b[33m2024-03-01 11:20:02\u001b[0m (1x) │\n",
+ " ├─────────────────┼─────────┼─────────────────────────────────────────────────┤\n",
+ " │ \u001b[36mtotal \u001b[0m │ 70.00% │ \u001b[33m10.3\u001b[0m -> \u001b[33m20.6\u001b[0m (1x) │\n",
+ " │ │ │ \u001b[33m3.0\u001b[0m -> \u001b[33m5.8\u001b[0m (1x) │\n",
+ " │ │ │ \u001b[33m0.75\u001b[0m -> \u001b[33m1.3\u001b[0m (1x) │\n",
+ " ├─────────────────┼─────────┼─────────────────────────────────────────────────┤\n",
+ " │ \u001b[36munit_price \u001b[0m │ 70.00% │ \u001b[33m0.75\u001b[0m -> \u001b[33m1.5\u001b[0m (2x) │\n",
+ " │ │ │ \u001b[33m5.4\u001b[0m -> \u001b[33m10.8\u001b[0m (1x) │\n",
+ " └─────────────────┴─────────┴─────────────────────────────────────────────────┘\n",
+ "\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(comparison.summary(top_k_column_changes=3))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Hidden columns\n",
+ "\n",
+ "Use `hidden_columns` to prevent sensitive data from being displayed in the summary. For example, loyalty card IDs should not appear in logs or reports shared across teams:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [
{
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Slim mode\n",
- "\n",
- "Slim mode generates a minimal summary showing only sections that contain differences:"
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
+ "┃ \u001b[1m Diffly Summary \u001b[0m ┃\n",
+ "┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n",
+ " Primary key: \u001b[36mtransaction_id\u001b[0m\n",
+ "\n",
+ " \u001b[1mSchemas\u001b[0m\n",
+ " ▔▔▔▔▔▔▔\n",
+ " \u001b[3mSchemas match exactly (column count: 10).\u001b[0m\n",
+ "\n",
+ " \u001b[1mRows\u001b[0m\n",
+ " ▔▔▔▔\n",
+ " \u001b[1mLeft count\u001b[0m\u001b[1m \u001b[0m\u001b[1mRight count\u001b[0m\n",
+ " 12 (no change) 12\n",
+ "\n",
+ " ┏━┯━┯━┯━┯━┓\n",
+ " ┃\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m┃ 2 left only (16.67%)\n",
+ " ┠─┼─┼─┼─┼─┨╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮\n",
+ " ┃ │ │ │ │ ┃\u001b[1m = \u001b[0m┃ │ │ │ │ ┃ 6 equal (60.00%) │\n",
+ " ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 10 joined\n",
+ " ┃ │ │ │ │ ┃\u001b[1m ≠ \u001b[0m┃ │ │ │ │ ┃ 4 unequal (40.00%) │\n",
+ " ┗━┷━┷━┷━┷━┛╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯\n",
+ " ┃\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m┃ 2 right only (16.67%)\n",
+ " ┗━┷━┷━┷━┷━┛\n",
+ "\n",
+ " \u001b[1mColumns\u001b[0m\n",
+ " ▔▔▔▔▔▔▔\n",
+ " ┌─────────────────┬─────────┬─────────────────────────────────────────────────┐\n",
+ " │ \u001b[36mdiscount \u001b[0m │ 70.00% │ \u001b[33m0.0\u001b[0m -> \u001b[33m0.2\u001b[0m (2x) │\n",
+ " │ │ │ \u001b[33m0.5\u001b[0m -> \u001b[33m1.0\u001b[0m (1x) │\n",
+ " ├─────────────────┼─────────┼─────────────────────────────────────────────────┤\n",
+ " │ \u001b[36mloyalty_card_id\u001b[0m │ 90.00% │ │\n",
+ " ├─────────────────┼─────────┼─────────────────────────────────────────────────┤\n",
+ " │ \u001b[36mproduct \u001b[0m │ 100.00% │ │\n",
+ " ├─────────────────┼─────────┼─────────────────────────────────────────────────┤\n",
+ " │ \u001b[36mquantity \u001b[0m │ 100.00% │ │\n",
+ " ├─────────────────┼─────────┼─────────────────────────────────────────────────┤\n",
+ " │ \u001b[36mregister_id \u001b[0m │ 100.00% │ │\n",
+ " ├─────────────────┼─────────┼─────────────────────────────────────────────────┤\n",
+ " │ \u001b[36mstore_id \u001b[0m │ 100.00% │ │\n",
+ " ├─────────────────┼─────────┼─────────────────────────────────────────────────┤\n",
+ " │ \u001b[36mtimestamp \u001b[0m │ 70.00% │ \u001b[33m2024-03-01 12:10:00\u001b[0m -> \u001b[33m2024-03-01 12:10:02\u001b[0m (1x) │\n",
+ " │ │ │ \u001b[33m2024-03-01 11:45:00\u001b[0m -> \u001b[33m2024-03-01 11:45:03\u001b[0m (1x) │\n",
+ " │ │ │ \u001b[33m2024-03-01 11:20:00\u001b[0m -> \u001b[33m2024-03-01 11:20:02\u001b[0m (1x) │\n",
+ " ├─────────────────┼─────────┼─────────────────────────────────────────────────┤\n",
+ " │ \u001b[36mtotal \u001b[0m │ 70.00% │ \u001b[33m10.3\u001b[0m -> \u001b[33m20.6\u001b[0m (1x) │\n",
+ " │ │ │ \u001b[33m3.0\u001b[0m -> \u001b[33m5.8\u001b[0m (1x) │\n",
+ " │ │ │ \u001b[33m0.75\u001b[0m -> \u001b[33m1.3\u001b[0m (1x) │\n",
+ " ├─────────────────┼─────────┼─────────────────────────────────────────────────┤\n",
+ " │ \u001b[36munit_price \u001b[0m │ 70.00% │ \u001b[33m0.75\u001b[0m -> \u001b[33m1.5\u001b[0m (2x) │\n",
+ " │ │ │ \u001b[33m5.4\u001b[0m -> \u001b[33m10.8\u001b[0m (1x) │\n",
+ " └─────────────────┴─────────┴─────────────────────────────────────────────────┘\n",
+ "\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(comparison.summary(top_k_column_changes=3, hidden_columns=[\"loyalty_card_id\"]))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Showing sample rows\n",
+ "\n",
+ "Use `sample_k_rows_only` to display sample primary keys for rows that exist only in one DataFrame:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [
{
- "cell_type": "code",
- "execution_count": 10,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- " \u001b[1mRows\u001b[0m\n",
- " ▔▔▔▔\n",
- " ┏━┯━┯━┯━┯━┓\n",
- " ┃\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m┃ 2 left only (16.67%)\n",
- " ┠─┼─┼─┼─┼─┨╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮\n",
- " ┃ │ │ │ │ ┃\u001b[1m = \u001b[0m┃ │ │ │ │ ┃ 6 equal (60.00%) │\n",
- " ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 10 joined\n",
- " ┃ │ │ │ │ ┃\u001b[1m ≠ \u001b[0m┃ │ │ │ │ ┃ 4 unequal (40.00%) │\n",
- " ┗━┷━┷━┷━┷━┛╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯\n",
- " ┃\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m┃ 2 right only (16.67%)\n",
- " ┗━┷━┷━┷━┷━┛\n",
- "\n",
- " \u001b[1mColumns\u001b[0m\n",
- " ▔▔▔▔▔▔▔\n",
- " ┌─────────────────┬─────────┐\n",
- " │ \u001b[36mdiscount \u001b[0m │ 70.00% │\n",
- " │ \u001b[36mloyalty_card_id\u001b[0m │ 90.00% │\n",
- " │ \u001b[36mproduct \u001b[0m │ 100.00% │\n",
- " │ \u001b[36mquantity \u001b[0m │ 100.00% │\n",
- " │ \u001b[36mregister_id \u001b[0m │ 100.00% │\n",
- " │ \u001b[36mstore_id \u001b[0m │ 100.00% │\n",
- " │ \u001b[36mtimestamp \u001b[0m │ 70.00% │\n",
- " │ \u001b[36mtotal \u001b[0m │ 70.00% │\n",
- " │ \u001b[36munit_price \u001b[0m │ 70.00% │\n",
- " └─────────────────┴─────────┘\n",
- "\n"
- ]
- }
- ],
- "source": [
- "print(comparison.summary(slim=True))"
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
+ "┃ \u001b[1m Diffly Summary \u001b[0m ┃\n",
+ "┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n",
+ " Primary key: \u001b[36mtransaction_id\u001b[0m\n",
+ "\n",
+ " \u001b[1mSchemas\u001b[0m\n",
+ " ▔▔▔▔▔▔▔\n",
+ " \u001b[3mSchemas match exactly (column count: 10).\u001b[0m\n",
+ "\n",
+ " \u001b[1mRows\u001b[0m\n",
+ " ▔▔▔▔\n",
+ " \u001b[1mLeft count\u001b[0m\u001b[1m \u001b[0m\u001b[1mRight count\u001b[0m\n",
+ " 12 (no change) 12\n",
+ "\n",
+ " ┏━┯━┯━┯━┯━┓\n",
+ " ┃\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m┃ 2 left only (16.67%)\n",
+ " ┠─┼─┼─┼─┼─┨╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮\n",
+ " ┃ │ │ │ │ ┃\u001b[1m = \u001b[0m┃ │ │ │ │ ┃ 6 equal (60.00%) │\n",
+ " ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 10 joined\n",
+ " ┃ │ │ │ │ ┃\u001b[1m ≠ \u001b[0m┃ │ │ │ │ ┃ 4 unequal (40.00%) │\n",
+ " ┗━┷━┷━┷━┷━┛╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯\n",
+ " ┃\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m┃ 2 right only (16.67%)\n",
+ " ┗━┷━┷━┷━┷━┛\n",
+ "\n",
+ " \u001b[1mColumns\u001b[0m\n",
+ " ▔▔▔▔▔▔▔\n",
+ " ┌─────────────────┬─────────┐\n",
+ " │ \u001b[36mdiscount \u001b[0m │ 70.00% │\n",
+ " │ \u001b[36mloyalty_card_id\u001b[0m │ 90.00% │\n",
+ " │ \u001b[36mproduct \u001b[0m │ 100.00% │\n",
+ " │ \u001b[36mquantity \u001b[0m │ 100.00% │\n",
+ " │ \u001b[36mregister_id \u001b[0m │ 100.00% │\n",
+ " │ \u001b[36mstore_id \u001b[0m │ 100.00% │\n",
+ " │ \u001b[36mtimestamp \u001b[0m │ 70.00% │\n",
+ " │ \u001b[36mtotal \u001b[0m │ 70.00% │\n",
+ " │ \u001b[36munit_price \u001b[0m │ 70.00% │\n",
+ " └─────────────────┴─────────┘\n",
+ "\n",
+ " \u001b[1mRows left only\u001b[0m\n",
+ " ▔▔▔▔▔▔▔▔▔▔▔▔▔▔\n",
+ " ┏━━━━━━━━━━━━━━━━┓\n",
+ " ┃\u001b[1m \u001b[0m\u001b[1mtransaction_id\u001b[0m\u001b[1m \u001b[0m┃\n",
+ " ┡━━━━━━━━━━━━━━━━┩\n",
+ " │ TXN-011 │\n",
+ " │ TXN-012 │\n",
+ " └────────────────┘\n",
+ "\n",
+ " \u001b[1mRows right only\u001b[0m\n",
+ " ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\n",
+ " ┏━━━━━━━━━━━━━━━━┓\n",
+ " ┃\u001b[1m \u001b[0m\u001b[1mtransaction_id\u001b[0m\u001b[1m \u001b[0m┃\n",
+ " ┡━━━━━━━━━━━━━━━━┩\n",
+ " │ TXN-013 │\n",
+ " │ TXN-014 │\n",
+ " └────────────────┘\n",
+ "\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(comparison.summary(sample_k_rows_only=5))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Hiding perfect matches\n",
+ "\n",
+ "For wide data frames, set `show_perfect_column_matches=False` to hide columns with 100% match rate:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [
{
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": []
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
+ "┃ \u001b[1m Diffly Summary \u001b[0m ┃\n",
+ "┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n",
+ " Primary key: \u001b[36mtransaction_id\u001b[0m\n",
+ "\n",
+ " \u001b[1mSchemas\u001b[0m\n",
+ " ▔▔▔▔▔▔▔\n",
+ " \u001b[3mSchemas match exactly (column count: 10).\u001b[0m\n",
+ "\n",
+ " \u001b[1mRows\u001b[0m\n",
+ " ▔▔▔▔\n",
+ " \u001b[1mLeft count\u001b[0m\u001b[1m \u001b[0m\u001b[1mRight count\u001b[0m\n",
+ " 12 (no change) 12\n",
+ "\n",
+ " ┏━┯━┯━┯━┯━┓\n",
+ " ┃\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m┃ 2 left only (16.67%)\n",
+ " ┠─┼─┼─┼─┼─┨╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮\n",
+ " ┃ │ │ │ │ ┃\u001b[1m = \u001b[0m┃ │ │ │ │ ┃ 6 equal (60.00%) │\n",
+ " ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 10 joined\n",
+ " ┃ │ │ │ │ ┃\u001b[1m ≠ \u001b[0m┃ │ │ │ │ ┃ 4 unequal (40.00%) │\n",
+ " ┗━┷━┷━┷━┷━┛╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯\n",
+ " ┃\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m┃ 2 right only (16.67%)\n",
+ " ┗━┷━┷━┷━┷━┛\n",
+ "\n",
+ " \u001b[1mColumns\u001b[0m\n",
+ " ▔▔▔▔▔▔▔\n",
+ " ┌─────────────────┬────────┐\n",
+ " │ \u001b[36mdiscount \u001b[0m │ 70.00% │\n",
+ " │ \u001b[36mloyalty_card_id\u001b[0m │ 90.00% │\n",
+ " │ \u001b[36mtimestamp \u001b[0m │ 70.00% │\n",
+ " │ \u001b[36mtotal \u001b[0m │ 70.00% │\n",
+ " │ \u001b[36munit_price \u001b[0m │ 70.00% │\n",
+ " └─────────────────┴────────┘\n",
+ "\n"
+ ]
}
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.14.3"
+ ],
+ "source": [
+ "print(comparison.summary(show_perfect_column_matches=False))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Numerical metrics\n",
+ "\n",
+ "Use `metrics` to display per-column aggregations over `right - left` for numerical columns. Pass a mapping from display label to a callable; the built-in presets live in `diffly.metrics`. You can also pass your own callable taking two Polars expressions (left and right) and returning a scalar aggregation."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from diffly import metrics\n",
+ "\n",
+ "print(\n",
+ " comparison.summary(\n",
+ " top_k_column_changes=3,\n",
+ " metrics={\n",
+ " \"mean\": metrics.mean,\n",
+ " \"mrd\": metrics.mean_relative_deviation,\n",
+ " \"max_abs\": lambda l, r: (r - l).abs().max(),\n",
+ " },\n",
+ " )\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Slim mode\n",
+ "\n",
+ "Slim mode generates a minimal summary showing only sections that contain differences:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ " \u001b[1mRows\u001b[0m\n",
+ " ▔▔▔▔\n",
+ " ┏━┯━┯━┯━┯━┓\n",
+ " ┃\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m│\u001b[31m-\u001b[0m┃ 2 left only (16.67%)\n",
+ " ┠─┼─┼─┼─┼─┨╌╌╌┏━┯━┯━┯━┯━┓╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮\n",
+ " ┃ │ │ │ │ ┃\u001b[1m = \u001b[0m┃ │ │ │ │ ┃ 6 equal (60.00%) │\n",
+ " ┠─┼─┼─┼─┼─┨╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌├╴ 10 joined\n",
+ " ┃ │ │ │ │ ┃\u001b[1m ≠ \u001b[0m┃ │ │ │ │ ┃ 4 unequal (40.00%) │\n",
+ " ┗━┷━┷━┷━┷━┛╌╌╌┠─┼─┼─┼─┼─┨╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯\n",
+ " ┃\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m│\u001b[32m+\u001b[0m┃ 2 right only (16.67%)\n",
+ " ┗━┷━┷━┷━┷━┛\n",
+ "\n",
+ " \u001b[1mColumns\u001b[0m\n",
+ " ▔▔▔▔▔▔▔\n",
+ " ┌─────────────────┬─────────┐\n",
+ " │ \u001b[36mdiscount \u001b[0m │ 70.00% │\n",
+ " │ \u001b[36mloyalty_card_id\u001b[0m │ 90.00% │\n",
+ " │ \u001b[36mproduct \u001b[0m │ 100.00% │\n",
+ " │ \u001b[36mquantity \u001b[0m │ 100.00% │\n",
+ " │ \u001b[36mregister_id \u001b[0m │ 100.00% │\n",
+ " │ \u001b[36mstore_id \u001b[0m │ 100.00% │\n",
+ " │ \u001b[36mtimestamp \u001b[0m │ 70.00% │\n",
+ " │ \u001b[36mtotal \u001b[0m │ 70.00% │\n",
+ " │ \u001b[36munit_price \u001b[0m │ 70.00% │\n",
+ " └─────────────────┴─────────┘\n",
+ "\n"
+ ]
}
+ ],
+ "source": [
+ "print(comparison.summary(slim=True))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
},
- "nbformat": 4,
- "nbformat_minor": 4
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.14.3"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
}
diff --git a/docs/index.md b/docs/index.md
index 86adade..1b8c63f 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -27,6 +27,7 @@ Diffly is a utility package for comparing `polars` data frames and lazy frames w
- **Lazy evaluation**: Uses `polars` lazy frames internally for efficient computation
- **Temporal tolerance**: Support for comparing temporal types (dates, datetimes) with configurable tolerances
- **Per-column tolerances**: Fine-grained control over comparison tolerances for each column
+- **Per-column metrics**: Compute `mean`, `quantile`, `mean_relative_deviation`, and custom aggregations over `right - left` for numerical columns
- **Method caching**: Automatically caches comparison results to avoid recomputation
- **Testing utilities**: Built-in assertion functions for data frame and collection equality in tests
From 82611686248a5ffd3c92b37fb094881fa934a2fc Mon Sep 17 00:00:00 2001
From: EgeKaraismailogluQC
<128645043+EgeKaraismailogluQC@users.noreply.github.com>
Date: Thu, 30 Apr 2026 18:19:37 +0200
Subject: [PATCH 06/19] coverage
---
diffly/metrics.py | 3 ---
tests/cli/test_cli.py | 21 ++++++++++++++++++++
tests/summary/test_summary.py | 36 +++++++++++++++++++++++++++++++++++
3 files changed, 57 insertions(+), 3 deletions(-)
diff --git a/diffly/metrics.py b/diffly/metrics.py
index 54fca0c..e2f72ad 100644
--- a/diffly/metrics.py
+++ b/diffly/metrics.py
@@ -28,9 +28,6 @@ class _Metric:
fn: Metric
selector: pl.Expr
- def __call__(self, left: pl.Expr, right: pl.Expr) -> pl.Expr:
- return self.fn(left, right)
-
def _make_numeric_metric(metric: Metric) -> _Metric:
return _Metric(fn=metric, selector=cs.numeric())
diff --git a/tests/cli/test_cli.py b/tests/cli/test_cli.py
index 63926d4..84de072 100644
--- a/tests/cli/test_cli.py
+++ b/tests/cli/test_cli.py
@@ -56,3 +56,24 @@ def test_cli_smoke(tmp_path: Path, output_json: bool) -> None:
assert result.output == comparison.summary().to_json() + "\n"
else:
assert result.output == comparison.summary().format(pretty=True) + "\n"
+
+
+def test_cli_unknown_metric(tmp_path: Path) -> None:
+ left = pl.DataFrame({"id": [1, 2], "x": [1.0, 2.0]})
+ right = pl.DataFrame({"id": [1, 2], "x": [1.0, 3.0]})
+ left.write_parquet(tmp_path / "left.parquet")
+ right.write_parquet(tmp_path / "right.parquet")
+
+ result = runner.invoke(
+ app,
+ [
+ str(tmp_path / "left.parquet"),
+ str(tmp_path / "right.parquet"),
+ "--primary-key",
+ "id",
+ "--metric",
+ "bogus",
+ ],
+ )
+ assert result.exit_code != 0
+ assert "Unknown metric" in result.output
diff --git a/tests/summary/test_summary.py b/tests/summary/test_summary.py
index 2de2184..4faebc9 100644
--- a/tests/summary/test_summary.py
+++ b/tests/summary/test_summary.py
@@ -290,6 +290,42 @@ def test_summary_data_metrics(show_perfect_column_matches: bool) -> None:
assert result["columns"] == expected_columns
+def test_summary_format_with_metrics() -> None:
+ comp = _make_comparison()
+ rendered = comp.summary(
+ metrics={"mean": metrics.mean},
+ ).format(pretty=False)
+ # Header is shown when metrics are present; "mean" appears as a column header.
+ assert "mean" in rendered
+ # Numeric column gets a rendered metric value; expected mean delta is 5/3.
+ assert "1.667" in rendered
+
+
+def test_summary_metrics_no_joined_rows() -> None:
+ # Disjoint primary keys -> num_rows_joined == 0.
+ left = pl.DataFrame({"id": [1, 2], "value": [1.0, 2.0]})
+ right = pl.DataFrame({"id": [10, 20], "value": [5.0, 6.0]})
+ comp = compare_frames(left, right, primary_key="id")
+ summary = comp.summary(metrics={"mean": metrics.mean})
+ result = json.loads(summary.to_json())
+ # With no joined rows, the columns section is skipped entirely.
+ assert result["columns"] is None
+
+
+def test_summary_metrics_no_numeric_columns() -> None:
+ # Non-PK common columns are all non-numeric.
+ left = pl.DataFrame({"id": [1, 2, 3], "status": ["a", "b", "c"]})
+ right = pl.DataFrame({"id": [1, 2, 3], "status": ["a", "b", "x"]})
+ comp = compare_frames(left, right, primary_key="id")
+ summary = comp.summary(
+ show_perfect_column_matches=True,
+ metrics={"mean": metrics.mean},
+ )
+ result = json.loads(summary.to_json())
+ # status appears but has no metric values (non-numeric column).
+ assert all(col["metrics"] is None for col in result["columns"])
+
+
@pytest.mark.parametrize(
"input, expected",
[
From 584ba71bfbf4a125b91dc0ba84b3ca2070eff198 Mon Sep 17 00:00:00 2001
From: EgeKaraismailogluQC
<128645043+EgeKaraismailogluQC@users.noreply.github.com>
Date: Mon, 4 May 2026 10:45:30 +0200
Subject: [PATCH 07/19] review
---
diffly/metrics.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/diffly/metrics.py b/diffly/metrics.py
index e2f72ad..7ba3fc1 100644
--- a/diffly/metrics.py
+++ b/diffly/metrics.py
@@ -72,6 +72,8 @@ def mean_relative_deviation(left: pl.Expr, right: pl.Expr) -> pl.Expr:
def quantile(q: float) -> Metric:
"""Factory returning a metric that computes the ``q``-quantile of
``right - left``."""
+ if not 0 <= q <= 1:
+ raise ValueError(f"q must be in [0, 1], got {q}")
def _quantile(left: pl.Expr, right: pl.Expr) -> pl.Expr:
return (right - left).quantile(q)
From f0e78b736bd6170727ca3d7f0b15955c552d0080 Mon Sep 17 00:00:00 2001
From: EgeKaraismailogluQC
<128645043+EgeKaraismailogluQC@users.noreply.github.com>
Date: Mon, 4 May 2026 10:51:35 +0200
Subject: [PATCH 08/19] review
---
diffly/summary.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/diffly/summary.py b/diffly/summary.py
index a68ab8a..c044f84 100644
--- a/diffly/summary.py
+++ b/diffly/summary.py
@@ -733,7 +733,7 @@ class SummaryData:
_other_common_columns: list[str]
_truncated_left_name: str
_truncated_right_name: str
- _metric_labels: list[str] = dataclasses.field(default_factory=list)
+ _metric_labels: list[str]
def to_dict(self) -> dict[str, Any]:
def _convert(obj: Any) -> Any:
@@ -836,6 +836,7 @@ def _validate_primary_key_hidden_columns() -> None:
_other_common_columns=comp._other_common_columns,
_truncated_left_name=truncated_left,
_truncated_right_name=truncated_right,
+ _metric_labels=[],
)
metrics_resolved: dict[str, Metric] = dict(metrics or {})
From 025d8ff2f07c847e8a87d3b76c8b90a3cf55f12a Mon Sep 17 00:00:00 2001
From: EgeKaraismailogluQC
<128645043+EgeKaraismailogluQC@users.noreply.github.com>
Date: Mon, 4 May 2026 10:52:44 +0200
Subject: [PATCH 09/19] review
---
diffly/summary.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/diffly/summary.py b/diffly/summary.py
index c044f84..2cda56f 100644
--- a/diffly/summary.py
+++ b/diffly/summary.py
@@ -715,7 +715,7 @@ class SummaryDataColumn:
match_rate: float
n_total_changes: int
changes: list[SummaryDataColumnChange] | None
- metrics: dict[str, Any] | None = None
+ metrics: dict[str, Any] | None
@dataclass
From 5f5e0e53a7e6718d8184fd4a65ad67dae1edcfa0 Mon Sep 17 00:00:00 2001
From: EgeKaraismailogluQC
<128645043+EgeKaraismailogluQC@users.noreply.github.com>
Date: Mon, 4 May 2026 11:03:48 +0200
Subject: [PATCH 10/19] review
---
diffly/summary.py | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/diffly/summary.py b/diffly/summary.py
index 2cda56f..12be481 100644
--- a/diffly/summary.py
+++ b/diffly/summary.py
@@ -1107,9 +1107,13 @@ def _yellow(raw: Any) -> str:
return f"[yellow]{raw}[/yellow]"
-def _format_value(value: Any) -> str:
+def _format_value(value: Any, *, float_format: str | None = None) -> str:
+ """Format a raw cell value for display, wrapped in the yellow highlight style.
+
+ Floats are shown with their default repr unless ``float_format`` is set.
+ """
if isinstance(value, list):
- formatted = [_format_value(x) for x in value]
+ formatted = [_format_value(x, float_format=float_format) for x in value]
if len(formatted) > 5:
return f"[{', '.join(formatted[:2])}, ..., {', '.join(formatted[-2:])}]"
return f"[{', '.join(formatted)}]"
@@ -1121,17 +1125,21 @@ def _format_value(value: Any) -> str:
raw = f'"{value}"'
elif isinstance(value, date | datetime):
raw = str(value)
+ elif isinstance(value, float) and float_format is not None:
+ raw = format(value, float_format)
else:
raw = value
return _yellow(raw)
def _format_metric_value(value: Any) -> str:
+ """Format a metric value for the column summary.
+
+ Blanks out ``None`` and renders floats with ``.4g`` precision.
+ """
if value is None:
return ""
- if isinstance(value, float):
- return _yellow(f"{value:.4g}")
- return _format_value(value)
+ return _format_value(value, float_format=".4g")
def _trim_whitespaces(s: str) -> str:
From 4d79870709cef0adf70395944f8d70c03421ced4 Mon Sep 17 00:00:00 2001
From: EgeKaraismailogluQC
<128645043+EgeKaraismailogluQC@users.noreply.github.com>
Date: Mon, 4 May 2026 11:11:31 +0200
Subject: [PATCH 11/19] review
---
diffly/summary.py | 2 --
1 file changed, 2 deletions(-)
diff --git a/diffly/summary.py b/diffly/summary.py
index 12be481..8f0dcb7 100644
--- a/diffly/summary.py
+++ b/diffly/summary.py
@@ -937,8 +937,6 @@ def _compute_column_metrics(
comp: DataFrameComparison,
metrics: Mapping[str, Metric],
) -> dict[str, dict[str, Any]]:
- if not metrics:
- return {}
if comp.primary_key is None or comp.num_rows_joined() == 0:
return {}
From 266bebdf4043720b91ac92d4deaae97b4f6fcd7c Mon Sep 17 00:00:00 2001
From: EgeKaraismailogluQC
<128645043+EgeKaraismailogluQC@users.noreply.github.com>
Date: Mon, 4 May 2026 11:12:49 +0200
Subject: [PATCH 12/19] Update diffly/summary.py
Co-authored-by: Marius Merkle <122545105+MariusMerkleQC@users.noreply.github.com>
---
diffly/summary.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/diffly/summary.py b/diffly/summary.py
index 8f0dcb7..a8f608c 100644
--- a/diffly/summary.py
+++ b/diffly/summary.py
@@ -952,9 +952,9 @@ def select_columns(selector: pl.Expr) -> set[str]:
}
all_columns = sorted(set().union(*metric_to_columns.values()))
- out: dict[str, dict[str, Any]] = {c: {} for c in all_columns}
if not all_columns:
- return out
+ return {}
+ out: dict[str, dict[str, Any]] = {c: {} for c in all_columns}
joined = comp.joined(lazy=True)
agg_exprs = [
From f32f793487867ff5810165fdef66003dfc3584bd Mon Sep 17 00:00:00 2001
From: EgeKaraismailogluQC
<128645043+EgeKaraismailogluQC@users.noreply.github.com>
Date: Wed, 6 May 2026 09:13:48 +0200
Subject: [PATCH 13/19] review
---
diffly/metrics.py | 14 +++++++-------
.../test_metrics_presets_few.py | 2 +-
.../test_metrics_presets_many.py | 14 +++++++-------
3 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/diffly/metrics.py b/diffly/metrics.py
index 7ba3fc1..cc53a41 100644
--- a/diffly/metrics.py
+++ b/diffly/metrics.py
@@ -82,11 +82,11 @@ def _quantile(left: pl.Expr, right: pl.Expr) -> pl.Expr:
_PRESETS: dict[str, Metric] = {
- "mean": mean,
- "median": median,
- "min": min,
- "max": max,
- "std": std,
- "mean_absolute_deviation": mean_absolute_deviation,
- "mean_relative_deviation": mean_relative_deviation,
+ "Mean": mean,
+ "Median": median,
+ "Min": min,
+ "Max": max,
+ "Std": std,
+ "Mean absolute deviation": mean_absolute_deviation,
+ "Mean relative deviation": mean_relative_deviation,
}
diff --git a/tests/summary/fixtures/metrics_presets_few/test_metrics_presets_few.py b/tests/summary/fixtures/metrics_presets_few/test_metrics_presets_few.py
index 7556fec..4fcd6d1 100644
--- a/tests/summary/fixtures/metrics_presets_few/test_metrics_presets_few.py
+++ b/tests/summary/fixtures/metrics_presets_few/test_metrics_presets_few.py
@@ -29,5 +29,5 @@ def test_generate() -> None:
comp = compare_frames(left, right, primary_key=["id"])
generate_summaries(
comp,
- metrics={"mean": metrics.mean, "max": metrics.max},
+ metrics={"Mean": metrics.mean, "Max": metrics.max},
)
diff --git a/tests/summary/fixtures/metrics_presets_many/test_metrics_presets_many.py b/tests/summary/fixtures/metrics_presets_many/test_metrics_presets_many.py
index c15162b..ede52cc 100644
--- a/tests/summary/fixtures/metrics_presets_many/test_metrics_presets_many.py
+++ b/tests/summary/fixtures/metrics_presets_many/test_metrics_presets_many.py
@@ -30,12 +30,12 @@ def test_generate() -> None:
generate_summaries(
comp,
metrics={
- "mean": metrics.mean,
- "median": metrics.median,
- "min": metrics.min,
- "max": metrics.max,
- "std": metrics.std,
- "mad": metrics.mean_absolute_deviation,
- "mrd": metrics.mean_relative_deviation,
+ "Mean": metrics.mean,
+ "Median": metrics.median,
+ "Min": metrics.min,
+ "Max": metrics.max,
+ "Std": metrics.std,
+ "Mean absolute deviation": metrics.mean_absolute_deviation,
+ "Mean relative deviation": metrics.mean_relative_deviation,
},
)
From 8bca8d66c4ca23e193d0ce9f983108609ff79109 Mon Sep 17 00:00:00 2001
From: EgeKaraismailogluQC
<128645043+EgeKaraismailogluQC@users.noreply.github.com>
Date: Wed, 6 May 2026 09:20:13 +0200
Subject: [PATCH 14/19] review
---
...alse_sample_rows_False_sample_pk_False.txt | 2 +-
...False_sample_rows_True_sample_pk_False.txt | 2 +-
...True_sample_rows_False_sample_pk_False.txt | 2 +-
..._True_sample_rows_True_sample_pk_False.txt | 2 +-
...alse_sample_rows_False_sample_pk_False.txt | 2 +-
..._False_sample_rows_True_sample_pk_True.txt | 2 +-
...True_sample_rows_False_sample_pk_False.txt | 2 +-
...m_True_sample_rows_True_sample_pk_True.txt | 2 +-
...alse_sample_rows_False_sample_pk_False.txt | 2 +-
...False_sample_rows_True_sample_pk_False.txt | 2 +-
...True_sample_rows_False_sample_pk_False.txt | 2 +-
..._True_sample_rows_True_sample_pk_False.txt | 2 +-
...alse_sample_rows_False_sample_pk_False.txt | 2 +-
..._False_sample_rows_True_sample_pk_True.txt | 2 +-
...True_sample_rows_False_sample_pk_False.txt | 2 +-
...m_True_sample_rows_True_sample_pk_True.txt | 2 +-
...alse_sample_rows_False_sample_pk_False.txt | 2 +-
...False_sample_rows_True_sample_pk_False.txt | 2 +-
...True_sample_rows_False_sample_pk_False.txt | 2 +-
..._True_sample_rows_True_sample_pk_False.txt | 2 +-
...alse_sample_rows_False_sample_pk_False.txt | 2 +-
..._False_sample_rows_True_sample_pk_True.txt | 2 +-
...True_sample_rows_False_sample_pk_False.txt | 2 +-
...m_True_sample_rows_True_sample_pk_True.txt | 2 +-
...alse_sample_rows_False_sample_pk_False.txt | 2 +-
...False_sample_rows_True_sample_pk_False.txt | 2 +-
...True_sample_rows_False_sample_pk_False.txt | 2 +-
..._True_sample_rows_True_sample_pk_False.txt | 2 +-
...alse_sample_rows_False_sample_pk_False.txt | 2 +-
..._False_sample_rows_True_sample_pk_True.txt | 2 +-
...True_sample_rows_False_sample_pk_False.txt | 2 +-
...m_True_sample_rows_True_sample_pk_True.txt | 2 +-
...alse_sample_rows_False_sample_pk_False.txt | 16 +++----
...False_sample_rows_True_sample_pk_False.txt | 16 +++----
...True_sample_rows_False_sample_pk_False.txt | 16 +++----
..._True_sample_rows_True_sample_pk_False.txt | 16 +++----
...alse_sample_rows_False_sample_pk_False.txt | 34 ++++++++-------
..._False_sample_rows_True_sample_pk_True.txt | 42 ++++++++++---------
...True_sample_rows_False_sample_pk_False.txt | 34 ++++++++-------
...m_True_sample_rows_True_sample_pk_True.txt | 42 ++++++++++---------
...alse_sample_rows_False_sample_pk_False.txt | 16 +++----
...False_sample_rows_True_sample_pk_False.txt | 16 +++----
...True_sample_rows_False_sample_pk_False.txt | 16 +++----
..._True_sample_rows_True_sample_pk_False.txt | 16 +++----
...alse_sample_rows_False_sample_pk_False.txt | 34 ++++++++-------
..._False_sample_rows_True_sample_pk_True.txt | 42 ++++++++++---------
...True_sample_rows_False_sample_pk_False.txt | 34 ++++++++-------
...m_True_sample_rows_True_sample_pk_True.txt | 42 ++++++++++---------
...alse_sample_rows_False_sample_pk_False.txt | 16 +++----
...False_sample_rows_True_sample_pk_False.txt | 16 +++----
...True_sample_rows_False_sample_pk_False.txt | 16 +++----
..._True_sample_rows_True_sample_pk_False.txt | 16 +++----
...alse_sample_rows_False_sample_pk_False.txt | 34 ++++++++-------
..._False_sample_rows_True_sample_pk_True.txt | 42 ++++++++++---------
...True_sample_rows_False_sample_pk_False.txt | 34 ++++++++-------
...m_True_sample_rows_True_sample_pk_True.txt | 42 ++++++++++---------
...alse_sample_rows_False_sample_pk_False.txt | 16 +++----
...False_sample_rows_True_sample_pk_False.txt | 16 +++----
...True_sample_rows_False_sample_pk_False.txt | 16 +++----
..._True_sample_rows_True_sample_pk_False.txt | 16 +++----
...alse_sample_rows_False_sample_pk_False.txt | 34 ++++++++-------
..._False_sample_rows_True_sample_pk_True.txt | 42 ++++++++++---------
...True_sample_rows_False_sample_pk_False.txt | 34 ++++++++-------
...m_True_sample_rows_True_sample_pk_True.txt | 42 ++++++++++---------
64 files changed, 512 insertions(+), 416 deletions(-)
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
index ce7fbde..93c45a8 100644
--- a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
@@ -21,7 +21,7 @@
Columns
▔▔▔▔▔▔▔
┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┓
- ┃ Column ┃ Match Rate ┃ mean ┃ max ┃
+ ┃ Column ┃ Match Rate ┃ Mean ┃ Max ┃
┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━┩
│ price │ 60.00% │ 0.6 │ 2 │
│ qty │ 80.00% │ 0.2 │ 1 │
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
index ce7fbde..93c45a8 100644
--- a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
@@ -21,7 +21,7 @@
Columns
▔▔▔▔▔▔▔
┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┓
- ┃ Column ┃ Match Rate ┃ mean ┃ max ┃
+ ┃ Column ┃ Match Rate ┃ Mean ┃ Max ┃
┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━┩
│ price │ 60.00% │ 0.6 │ 2 │
│ qty │ 80.00% │ 0.2 │ 1 │
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
index be0aae1..8d4e5fc 100644
--- a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
@@ -9,7 +9,7 @@
Columns
▔▔▔▔▔▔▔
┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┓
- ┃ Column ┃ Match Rate ┃ mean ┃ max ┃
+ ┃ Column ┃ Match Rate ┃ Mean ┃ Max ┃
┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━┩
│ price │ 60.00% │ 0.6 │ 2 │
│ qty │ 80.00% │ 0.2 │ 1 │
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
index be0aae1..8d4e5fc 100644
--- a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
@@ -9,7 +9,7 @@
Columns
▔▔▔▔▔▔▔
┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┓
- ┃ Column ┃ Match Rate ┃ mean ┃ max ┃
+ ┃ Column ┃ Match Rate ┃ Mean ┃ Max ┃
┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━┩
│ price │ 60.00% │ 0.6 │ 2 │
│ qty │ 80.00% │ 0.2 │ 1 │
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
index 74f09a6..78312e9 100644
--- a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
@@ -21,7 +21,7 @@
Columns
▔▔▔▔▔▔▔
┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━┓
- ┃ Column ┃ Match Rate ┃ mean ┃ max ┃ Top Changes ┃
+ ┃ Column ┃ Match Rate ┃ Mean ┃ Max ┃ Top Changes ┃
┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━┩
│ price │ 60.00% │ 0.6 │ 2 │ 40.0 -> 42.0 (1x) │
│ │ │ │ │ 20.0 -> 21.0 (1x) │
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
index bc6c74b..3f8a229 100644
--- a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
@@ -21,7 +21,7 @@
Columns
▔▔▔▔▔▔▔
┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
- ┃ Column ┃ Match Rate ┃ mean ┃ max ┃ Top Changes ┃
+ ┃ Column ┃ Match Rate ┃ Mean ┃ Max ┃ Top Changes ┃
┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ price │ 60.00% │ 0.6 │ 2 │ 40.0 -> 42.0 (1x, e.g. 4) │
│ │ │ │ │ 20.0 -> 21.0 (1x, e.g. 2) │
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
index 9af95d6..7218d6c 100644
--- a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
@@ -9,7 +9,7 @@
Columns
▔▔▔▔▔▔▔
┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━┓
- ┃ Column ┃ Match Rate ┃ mean ┃ max ┃ Top Changes ┃
+ ┃ Column ┃ Match Rate ┃ Mean ┃ Max ┃ Top Changes ┃
┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━┩
│ price │ 60.00% │ 0.6 │ 2 │ 40.0 -> 42.0 (1x) │
│ │ │ │ │ 20.0 -> 21.0 (1x) │
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
index 6d9581d..4c8c7ce 100644
--- a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
@@ -9,7 +9,7 @@
Columns
▔▔▔▔▔▔▔
┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
- ┃ Column ┃ Match Rate ┃ mean ┃ max ┃ Top Changes ┃
+ ┃ Column ┃ Match Rate ┃ Mean ┃ Max ┃ Top Changes ┃
┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ price │ 60.00% │ 0.6 │ 2 │ 40.0 -> 42.0 (1x, e.g. 4) │
│ │ │ │ │ 20.0 -> 21.0 (1x, e.g. 2) │
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
index ce7fbde..93c45a8 100644
--- a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
@@ -21,7 +21,7 @@
Columns
▔▔▔▔▔▔▔
┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┓
- ┃ Column ┃ Match Rate ┃ mean ┃ max ┃
+ ┃ Column ┃ Match Rate ┃ Mean ┃ Max ┃
┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━┩
│ price │ 60.00% │ 0.6 │ 2 │
│ qty │ 80.00% │ 0.2 │ 1 │
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
index ce7fbde..93c45a8 100644
--- a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
@@ -21,7 +21,7 @@
Columns
▔▔▔▔▔▔▔
┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┓
- ┃ Column ┃ Match Rate ┃ mean ┃ max ┃
+ ┃ Column ┃ Match Rate ┃ Mean ┃ Max ┃
┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━┩
│ price │ 60.00% │ 0.6 │ 2 │
│ qty │ 80.00% │ 0.2 │ 1 │
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
index be0aae1..8d4e5fc 100644
--- a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
@@ -9,7 +9,7 @@
Columns
▔▔▔▔▔▔▔
┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┓
- ┃ Column ┃ Match Rate ┃ mean ┃ max ┃
+ ┃ Column ┃ Match Rate ┃ Mean ┃ Max ┃
┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━┩
│ price │ 60.00% │ 0.6 │ 2 │
│ qty │ 80.00% │ 0.2 │ 1 │
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
index be0aae1..8d4e5fc 100644
--- a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
@@ -9,7 +9,7 @@
Columns
▔▔▔▔▔▔▔
┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┓
- ┃ Column ┃ Match Rate ┃ mean ┃ max ┃
+ ┃ Column ┃ Match Rate ┃ Mean ┃ Max ┃
┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━┩
│ price │ 60.00% │ 0.6 │ 2 │
│ qty │ 80.00% │ 0.2 │ 1 │
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
index 74f09a6..78312e9 100644
--- a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
@@ -21,7 +21,7 @@
Columns
▔▔▔▔▔▔▔
┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━┓
- ┃ Column ┃ Match Rate ┃ mean ┃ max ┃ Top Changes ┃
+ ┃ Column ┃ Match Rate ┃ Mean ┃ Max ┃ Top Changes ┃
┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━┩
│ price │ 60.00% │ 0.6 │ 2 │ 40.0 -> 42.0 (1x) │
│ │ │ │ │ 20.0 -> 21.0 (1x) │
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
index bc6c74b..3f8a229 100644
--- a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
@@ -21,7 +21,7 @@
Columns
▔▔▔▔▔▔▔
┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
- ┃ Column ┃ Match Rate ┃ mean ┃ max ┃ Top Changes ┃
+ ┃ Column ┃ Match Rate ┃ Mean ┃ Max ┃ Top Changes ┃
┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ price │ 60.00% │ 0.6 │ 2 │ 40.0 -> 42.0 (1x, e.g. 4) │
│ │ │ │ │ 20.0 -> 21.0 (1x, e.g. 2) │
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
index 9af95d6..7218d6c 100644
--- a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
@@ -9,7 +9,7 @@
Columns
▔▔▔▔▔▔▔
┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━┓
- ┃ Column ┃ Match Rate ┃ mean ┃ max ┃ Top Changes ┃
+ ┃ Column ┃ Match Rate ┃ Mean ┃ Max ┃ Top Changes ┃
┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━┩
│ price │ 60.00% │ 0.6 │ 2 │ 40.0 -> 42.0 (1x) │
│ │ │ │ │ 20.0 -> 21.0 (1x) │
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
index 6d9581d..4c8c7ce 100644
--- a/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
@@ -9,7 +9,7 @@
Columns
▔▔▔▔▔▔▔
┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
- ┃ Column ┃ Match Rate ┃ mean ┃ max ┃ Top Changes ┃
+ ┃ Column ┃ Match Rate ┃ Mean ┃ Max ┃ Top Changes ┃
┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ price │ 60.00% │ 0.6 │ 2 │ 40.0 -> 42.0 (1x, e.g. 4) │
│ │ │ │ │ 20.0 -> 21.0 (1x, e.g. 2) │
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
index 996d5c0..ee90b34 100644
--- a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
@@ -21,7 +21,7 @@
[1mColumns[0m
▔▔▔▔▔▔▔
┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┓
- ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mMean[0m[1m [0m┃[1m [0m[1mMax[0m[1m [0m┃
┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━┩
│ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │
│ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
index 996d5c0..ee90b34 100644
--- a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
@@ -21,7 +21,7 @@
[1mColumns[0m
▔▔▔▔▔▔▔
┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┓
- ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mMean[0m[1m [0m┃[1m [0m[1mMax[0m[1m [0m┃
┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━┩
│ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │
│ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
index a4fdffb..b106802 100644
--- a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
@@ -9,7 +9,7 @@
[1mColumns[0m
▔▔▔▔▔▔▔
┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┓
- ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mMean[0m[1m [0m┃[1m [0m[1mMax[0m[1m [0m┃
┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━┩
│ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │
│ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
index a4fdffb..b106802 100644
--- a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
@@ -9,7 +9,7 @@
[1mColumns[0m
▔▔▔▔▔▔▔
┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┓
- ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mMean[0m[1m [0m┃[1m [0m[1mMax[0m[1m [0m┃
┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━┩
│ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │
│ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
index a2e7af7..43bd1a8 100644
--- a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
@@ -21,7 +21,7 @@
[1mColumns[0m
▔▔▔▔▔▔▔
┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━┓
- ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m Top Changes[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mMean[0m[1m [0m┃[1m [0m[1mMax[0m[1m [0m┃[1m [0m[1m Top Changes[0m[1m [0m┃
┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━┩
│ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │ [33m40.0[0m -> [33m42.0[0m (1x) │
│ │ │ │ │ [33m20.0[0m -> [33m21.0[0m (1x) │
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
index b12c378..67c851c 100644
--- a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
@@ -21,7 +21,7 @@
[1mColumns[0m
▔▔▔▔▔▔▔
┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
- ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m Top Changes[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mMean[0m[1m [0m┃[1m [0m[1mMax[0m[1m [0m┃[1m [0m[1m Top Changes[0m[1m [0m┃
┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │ [33m40.0[0m -> [33m42.0[0m (1x, e.g. [33m4[0m) │
│ │ │ │ │ [33m20.0[0m -> [33m21.0[0m (1x, e.g. [33m2[0m) │
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
index de9090a..01d0278 100644
--- a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
@@ -9,7 +9,7 @@
[1mColumns[0m
▔▔▔▔▔▔▔
┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━┓
- ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m Top Changes[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mMean[0m[1m [0m┃[1m [0m[1mMax[0m[1m [0m┃[1m [0m[1m Top Changes[0m[1m [0m┃
┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━┩
│ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │ [33m40.0[0m -> [33m42.0[0m (1x) │
│ │ │ │ │ [33m20.0[0m -> [33m21.0[0m (1x) │
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
index 939518a..e4f58e8 100644
--- a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
@@ -9,7 +9,7 @@
[1mColumns[0m
▔▔▔▔▔▔▔
┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
- ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m Top Changes[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mMean[0m[1m [0m┃[1m [0m[1mMax[0m[1m [0m┃[1m [0m[1m Top Changes[0m[1m [0m┃
┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │ [33m40.0[0m -> [33m42.0[0m (1x, e.g. [33m4[0m) │
│ │ │ │ │ [33m20.0[0m -> [33m21.0[0m (1x, e.g. [33m2[0m) │
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
index 996d5c0..ee90b34 100644
--- a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
@@ -21,7 +21,7 @@
[1mColumns[0m
▔▔▔▔▔▔▔
┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┓
- ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mMean[0m[1m [0m┃[1m [0m[1mMax[0m[1m [0m┃
┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━┩
│ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │
│ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
index 996d5c0..ee90b34 100644
--- a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
@@ -21,7 +21,7 @@
[1mColumns[0m
▔▔▔▔▔▔▔
┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┓
- ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mMean[0m[1m [0m┃[1m [0m[1mMax[0m[1m [0m┃
┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━┩
│ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │
│ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
index a4fdffb..b106802 100644
--- a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
@@ -9,7 +9,7 @@
[1mColumns[0m
▔▔▔▔▔▔▔
┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┓
- ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mMean[0m[1m [0m┃[1m [0m[1mMax[0m[1m [0m┃
┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━┩
│ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │
│ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
index a4fdffb..b106802 100644
--- a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
@@ -9,7 +9,7 @@
[1mColumns[0m
▔▔▔▔▔▔▔
┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┓
- ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mMean[0m[1m [0m┃[1m [0m[1mMax[0m[1m [0m┃
┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━┩
│ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │
│ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m1[0m │
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
index a2e7af7..43bd1a8 100644
--- a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
@@ -21,7 +21,7 @@
[1mColumns[0m
▔▔▔▔▔▔▔
┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━┓
- ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m Top Changes[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mMean[0m[1m [0m┃[1m [0m[1mMax[0m[1m [0m┃[1m [0m[1m Top Changes[0m[1m [0m┃
┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━┩
│ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │ [33m40.0[0m -> [33m42.0[0m (1x) │
│ │ │ │ │ [33m20.0[0m -> [33m21.0[0m (1x) │
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
index b12c378..67c851c 100644
--- a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
@@ -21,7 +21,7 @@
[1mColumns[0m
▔▔▔▔▔▔▔
┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
- ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m Top Changes[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mMean[0m[1m [0m┃[1m [0m[1mMax[0m[1m [0m┃[1m [0m[1m Top Changes[0m[1m [0m┃
┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │ [33m40.0[0m -> [33m42.0[0m (1x, e.g. [33m4[0m) │
│ │ │ │ │ [33m20.0[0m -> [33m21.0[0m (1x, e.g. [33m2[0m) │
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
index de9090a..01d0278 100644
--- a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
@@ -9,7 +9,7 @@
[1mColumns[0m
▔▔▔▔▔▔▔
┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━┓
- ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m Top Changes[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mMean[0m[1m [0m┃[1m [0m[1mMax[0m[1m [0m┃[1m [0m[1m Top Changes[0m[1m [0m┃
┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━┩
│ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │ [33m40.0[0m -> [33m42.0[0m (1x) │
│ │ │ │ │ [33m20.0[0m -> [33m21.0[0m (1x) │
diff --git a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
index 939518a..e4f58e8 100644
--- a/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
+++ b/tests/summary/fixtures/metrics_presets_few/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
@@ -9,7 +9,7 @@
[1mColumns[0m
▔▔▔▔▔▔▔
┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
- ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m Top Changes[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mMean[0m[1m [0m┃[1m [0m[1mMax[0m[1m [0m┃[1m [0m[1m Top Changes[0m[1m [0m┃
┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m2[0m │ [33m40.0[0m -> [33m42.0[0m (1x, e.g. [33m4[0m) │
│ │ │ │ │ [33m20.0[0m -> [33m21.0[0m (1x, e.g. [33m2[0m) │
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
index 418475e..5893689 100644
--- a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
@@ -20,10 +20,12 @@
Columns
▔▔▔▔▔▔▔
- ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┓
- ┃ Column ┃ Match Rate ┃ mean ┃ median ┃ min ┃ max ┃ std ┃ mad ┃ mrd ┃
- ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━┩
- │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │
- │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │
- │ status │ 80.00% │ │ │ │ │ │ │ │
- └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┘
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Mean ┃ Mean ┃
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ absolute ┃ relative ┃
+ ┃ Column ┃ Match Rate ┃ Mean ┃ Median ┃ Min ┃ Max ┃ Std ┃ deviation ┃ deviation ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │
+ │ status │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴────────────┴───────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
index 418475e..5893689 100644
--- a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
@@ -20,10 +20,12 @@
Columns
▔▔▔▔▔▔▔
- ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┓
- ┃ Column ┃ Match Rate ┃ mean ┃ median ┃ min ┃ max ┃ std ┃ mad ┃ mrd ┃
- ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━┩
- │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │
- │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │
- │ status │ 80.00% │ │ │ │ │ │ │ │
- └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┘
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Mean ┃ Mean ┃
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ absolute ┃ relative ┃
+ ┃ Column ┃ Match Rate ┃ Mean ┃ Median ┃ Min ┃ Max ┃ Std ┃ deviation ┃ deviation ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │
+ │ status │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴────────────┴───────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
index 84cbcd7..dc9a50a 100644
--- a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
@@ -8,10 +8,12 @@
Columns
▔▔▔▔▔▔▔
- ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┓
- ┃ Column ┃ Match Rate ┃ mean ┃ median ┃ min ┃ max ┃ std ┃ mad ┃ mrd ┃
- ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━┩
- │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │
- │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │
- │ status │ 80.00% │ │ │ │ │ │ │ │
- └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┘
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Mean ┃ Mean ┃
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ absolute ┃ relative ┃
+ ┃ Column ┃ Match Rate ┃ Mean ┃ Median ┃ Min ┃ Max ┃ Std ┃ deviation ┃ deviation ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │
+ │ status │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴────────────┴───────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
index 84cbcd7..dc9a50a 100644
--- a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
@@ -8,10 +8,12 @@
Columns
▔▔▔▔▔▔▔
- ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┓
- ┃ Column ┃ Match Rate ┃ mean ┃ median ┃ min ┃ max ┃ std ┃ mad ┃ mrd ┃
- ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━┩
- │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │
- │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │
- │ status │ 80.00% │ │ │ │ │ │ │ │
- └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┘
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Mean ┃ Mean ┃
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ absolute ┃ relative ┃
+ ┃ Column ┃ Match Rate ┃ Mean ┃ Median ┃ Min ┃ Max ┃ Std ┃ deviation ┃ deviation ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │
+ │ status │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴────────────┴───────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
index 588bb44..0334464 100644
--- a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
@@ -20,18 +20,22 @@
Columns
▔▔▔▔▔▔▔
- ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┳━━━━━━━━━━━┓
- ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Top ┃
- ┃ Column ┃ Match Rate ┃ mean ┃ median ┃ min ┃ max ┃ std ┃ mad ┃ mrd ┃ Changes ┃
- ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━╇━━━━━━━━━━━┩
- │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │ 40.0 -> │
- │ │ │ │ │ │ │ │ │ │ 42.0 (1x) │
- │ │ │ │ │ │ │ │ │ │ 20.0 -> │
- │ │ │ │ │ │ │ │ │ │ 21.0 (1x) │
- ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
- │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │ 4 -> 5 │
- │ │ │ │ │ │ │ │ │ │ (1x) │
- ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
- │ status │ 80.00% │ │ │ │ │ │ │ │ "c" -> │
- │ │ │ │ │ │ │ │ │ │ "x" (1x) │
- └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┴───────────┘
+ ┏━━━━━━━━┳━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Mean ┃ Mean ┃ ┃
+ ┃ ┃ Match ┃ ┃ ┃ ┃ ┃ ┃ absolu… ┃ relat… ┃ Top ┃
+ ┃ Column ┃ Rate ┃ Mean ┃ Median ┃ Min ┃ Max ┃ Std ┃ deviat… ┃ devia… ┃ Changes ┃
+ ┡━━━━━━━━╇━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │ 40.0 -> │
+ │ │ │ │ │ │ │ │ │ │ 42.0 │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ │ │ │ │ │ │ │ │ │ │ 20.0 -> │
+ │ │ │ │ │ │ │ │ │ │ 21.0 │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├────────┼────────┼──────┼────────┼─────┼─────┼────────┼─────────┼────────┼─────────┤
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │ 4 -> 5 │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├────────┼────────┼──────┼────────┼─────┼─────┼────────┼─────────┼────────┼─────────┤
+ │ status │ 80.00% │ │ │ │ │ │ │ │ "c" -> │
+ │ │ │ │ │ │ │ │ │ │ "x" │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ └────────┴────────┴──────┴────────┴─────┴─────┴────────┴─────────┴────────┴─────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
index a91325f..2fd9096 100644
--- a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
@@ -20,22 +20,26 @@
Columns
▔▔▔▔▔▔▔
- ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┳━━━━━━━━━━━┓
- ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Top ┃
- ┃ Column ┃ Match Rate ┃ mean ┃ median ┃ min ┃ max ┃ std ┃ mad ┃ mrd ┃ Changes ┃
- ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━╇━━━━━━━━━━━┩
- │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │ 40.0 -> │
- │ │ │ │ │ │ │ │ │ │ 42.0 (1x, │
- │ │ │ │ │ │ │ │ │ │ e.g. 4) │
- │ │ │ │ │ │ │ │ │ │ 20.0 -> │
- │ │ │ │ │ │ │ │ │ │ 21.0 (1x, │
- │ │ │ │ │ │ │ │ │ │ e.g. 2) │
- ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
- │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │ 4 -> 5 │
- │ │ │ │ │ │ │ │ │ │ (1x, e.g. │
- │ │ │ │ │ │ │ │ │ │ 4) │
- ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
- │ status │ 80.00% │ │ │ │ │ │ │ │ "c" -> │
- │ │ │ │ │ │ │ │ │ │ "x" (1x, │
- │ │ │ │ │ │ │ │ │ │ e.g. 3) │
- └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┴───────────┘
+ ┏━━━━━━━━┳━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Mean ┃ Mean ┃ ┃
+ ┃ ┃ Match ┃ ┃ ┃ ┃ ┃ ┃ absolu… ┃ relat… ┃ Top ┃
+ ┃ Column ┃ Rate ┃ Mean ┃ Median ┃ Min ┃ Max ┃ Std ┃ deviat… ┃ devia… ┃ Changes ┃
+ ┡━━━━━━━━╇━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │ 40.0 -> │
+ │ │ │ │ │ │ │ │ │ │ 42.0 │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. 4) │
+ │ │ │ │ │ │ │ │ │ │ 20.0 -> │
+ │ │ │ │ │ │ │ │ │ │ 21.0 │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. 2) │
+ ├────────┼────────┼──────┼────────┼─────┼─────┼────────┼─────────┼────────┼─────────┤
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │ 4 -> 5 │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. 4) │
+ ├────────┼────────┼──────┼────────┼─────┼─────┼────────┼─────────┼────────┼─────────┤
+ │ status │ 80.00% │ │ │ │ │ │ │ │ "c" -> │
+ │ │ │ │ │ │ │ │ │ │ "x" │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. 3) │
+ └────────┴────────┴──────┴────────┴─────┴─────┴────────┴─────────┴────────┴─────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
index 811a324..eb395dd 100644
--- a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
@@ -8,18 +8,22 @@
Columns
▔▔▔▔▔▔▔
- ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┳━━━━━━━━━━━┓
- ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Top ┃
- ┃ Column ┃ Match Rate ┃ mean ┃ median ┃ min ┃ max ┃ std ┃ mad ┃ mrd ┃ Changes ┃
- ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━╇━━━━━━━━━━━┩
- │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │ 40.0 -> │
- │ │ │ │ │ │ │ │ │ │ 42.0 (1x) │
- │ │ │ │ │ │ │ │ │ │ 20.0 -> │
- │ │ │ │ │ │ │ │ │ │ 21.0 (1x) │
- ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
- │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │ 4 -> 5 │
- │ │ │ │ │ │ │ │ │ │ (1x) │
- ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
- │ status │ 80.00% │ │ │ │ │ │ │ │ "c" -> │
- │ │ │ │ │ │ │ │ │ │ "x" (1x) │
- └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┴───────────┘
+ ┏━━━━━━━━┳━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Mean ┃ Mean ┃ ┃
+ ┃ ┃ Match ┃ ┃ ┃ ┃ ┃ ┃ absolu… ┃ relat… ┃ Top ┃
+ ┃ Column ┃ Rate ┃ Mean ┃ Median ┃ Min ┃ Max ┃ Std ┃ deviat… ┃ devia… ┃ Changes ┃
+ ┡━━━━━━━━╇━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │ 40.0 -> │
+ │ │ │ │ │ │ │ │ │ │ 42.0 │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ │ │ │ │ │ │ │ │ │ │ 20.0 -> │
+ │ │ │ │ │ │ │ │ │ │ 21.0 │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├────────┼────────┼──────┼────────┼─────┼─────┼────────┼─────────┼────────┼─────────┤
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │ 4 -> 5 │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├────────┼────────┼──────┼────────┼─────┼─────┼────────┼─────────┼────────┼─────────┤
+ │ status │ 80.00% │ │ │ │ │ │ │ │ "c" -> │
+ │ │ │ │ │ │ │ │ │ │ "x" │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ └────────┴────────┴──────┴────────┴─────┴─────┴────────┴─────────┴────────┴─────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
index 09353d4..31d7344 100644
--- a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
@@ -8,22 +8,26 @@
Columns
▔▔▔▔▔▔▔
- ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┳━━━━━━━━━━━┓
- ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Top ┃
- ┃ Column ┃ Match Rate ┃ mean ┃ median ┃ min ┃ max ┃ std ┃ mad ┃ mrd ┃ Changes ┃
- ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━╇━━━━━━━━━━━┩
- │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │ 40.0 -> │
- │ │ │ │ │ │ │ │ │ │ 42.0 (1x, │
- │ │ │ │ │ │ │ │ │ │ e.g. 4) │
- │ │ │ │ │ │ │ │ │ │ 20.0 -> │
- │ │ │ │ │ │ │ │ │ │ 21.0 (1x, │
- │ │ │ │ │ │ │ │ │ │ e.g. 2) │
- ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
- │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │ 4 -> 5 │
- │ │ │ │ │ │ │ │ │ │ (1x, e.g. │
- │ │ │ │ │ │ │ │ │ │ 4) │
- ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
- │ status │ 80.00% │ │ │ │ │ │ │ │ "c" -> │
- │ │ │ │ │ │ │ │ │ │ "x" (1x, │
- │ │ │ │ │ │ │ │ │ │ e.g. 3) │
- └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┴───────────┘
+ ┏━━━━━━━━┳━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Mean ┃ Mean ┃ ┃
+ ┃ ┃ Match ┃ ┃ ┃ ┃ ┃ ┃ absolu… ┃ relat… ┃ Top ┃
+ ┃ Column ┃ Rate ┃ Mean ┃ Median ┃ Min ┃ Max ┃ Std ┃ deviat… ┃ devia… ┃ Changes ┃
+ ┡━━━━━━━━╇━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │ 40.0 -> │
+ │ │ │ │ │ │ │ │ │ │ 42.0 │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. 4) │
+ │ │ │ │ │ │ │ │ │ │ 20.0 -> │
+ │ │ │ │ │ │ │ │ │ │ 21.0 │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. 2) │
+ ├────────┼────────┼──────┼────────┼─────┼─────┼────────┼─────────┼────────┼─────────┤
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │ 4 -> 5 │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. 4) │
+ ├────────┼────────┼──────┼────────┼─────┼─────┼────────┼─────────┼────────┼─────────┤
+ │ status │ 80.00% │ │ │ │ │ │ │ │ "c" -> │
+ │ │ │ │ │ │ │ │ │ │ "x" │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. 3) │
+ └────────┴────────┴──────┴────────┴─────┴─────┴────────┴─────────┴────────┴─────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
index 418475e..5893689 100644
--- a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
@@ -20,10 +20,12 @@
Columns
▔▔▔▔▔▔▔
- ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┓
- ┃ Column ┃ Match Rate ┃ mean ┃ median ┃ min ┃ max ┃ std ┃ mad ┃ mrd ┃
- ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━┩
- │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │
- │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │
- │ status │ 80.00% │ │ │ │ │ │ │ │
- └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┘
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Mean ┃ Mean ┃
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ absolute ┃ relative ┃
+ ┃ Column ┃ Match Rate ┃ Mean ┃ Median ┃ Min ┃ Max ┃ Std ┃ deviation ┃ deviation ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │
+ │ status │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴────────────┴───────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
index 418475e..5893689 100644
--- a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
@@ -20,10 +20,12 @@
Columns
▔▔▔▔▔▔▔
- ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┓
- ┃ Column ┃ Match Rate ┃ mean ┃ median ┃ min ┃ max ┃ std ┃ mad ┃ mrd ┃
- ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━┩
- │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │
- │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │
- │ status │ 80.00% │ │ │ │ │ │ │ │
- └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┘
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Mean ┃ Mean ┃
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ absolute ┃ relative ┃
+ ┃ Column ┃ Match Rate ┃ Mean ┃ Median ┃ Min ┃ Max ┃ Std ┃ deviation ┃ deviation ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │
+ │ status │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴────────────┴───────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
index 84cbcd7..dc9a50a 100644
--- a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
@@ -8,10 +8,12 @@
Columns
▔▔▔▔▔▔▔
- ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┓
- ┃ Column ┃ Match Rate ┃ mean ┃ median ┃ min ┃ max ┃ std ┃ mad ┃ mrd ┃
- ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━┩
- │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │
- │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │
- │ status │ 80.00% │ │ │ │ │ │ │ │
- └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┘
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Mean ┃ Mean ┃
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ absolute ┃ relative ┃
+ ┃ Column ┃ Match Rate ┃ Mean ┃ Median ┃ Min ┃ Max ┃ Std ┃ deviation ┃ deviation ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │
+ │ status │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴────────────┴───────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
index 84cbcd7..dc9a50a 100644
--- a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
@@ -8,10 +8,12 @@
Columns
▔▔▔▔▔▔▔
- ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┓
- ┃ Column ┃ Match Rate ┃ mean ┃ median ┃ min ┃ max ┃ std ┃ mad ┃ mrd ┃
- ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━┩
- │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │
- │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │
- │ status │ 80.00% │ │ │ │ │ │ │ │
- └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┘
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Mean ┃ Mean ┃
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ absolute ┃ relative ┃
+ ┃ Column ┃ Match Rate ┃ Mean ┃ Median ┃ Min ┃ Max ┃ Std ┃ deviation ┃ deviation ┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │
+ │ status │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴────────────┴───────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
index 588bb44..0334464 100644
--- a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
@@ -20,18 +20,22 @@
Columns
▔▔▔▔▔▔▔
- ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┳━━━━━━━━━━━┓
- ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Top ┃
- ┃ Column ┃ Match Rate ┃ mean ┃ median ┃ min ┃ max ┃ std ┃ mad ┃ mrd ┃ Changes ┃
- ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━╇━━━━━━━━━━━┩
- │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │ 40.0 -> │
- │ │ │ │ │ │ │ │ │ │ 42.0 (1x) │
- │ │ │ │ │ │ │ │ │ │ 20.0 -> │
- │ │ │ │ │ │ │ │ │ │ 21.0 (1x) │
- ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
- │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │ 4 -> 5 │
- │ │ │ │ │ │ │ │ │ │ (1x) │
- ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
- │ status │ 80.00% │ │ │ │ │ │ │ │ "c" -> │
- │ │ │ │ │ │ │ │ │ │ "x" (1x) │
- └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┴───────────┘
+ ┏━━━━━━━━┳━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Mean ┃ Mean ┃ ┃
+ ┃ ┃ Match ┃ ┃ ┃ ┃ ┃ ┃ absolu… ┃ relat… ┃ Top ┃
+ ┃ Column ┃ Rate ┃ Mean ┃ Median ┃ Min ┃ Max ┃ Std ┃ deviat… ┃ devia… ┃ Changes ┃
+ ┡━━━━━━━━╇━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │ 40.0 -> │
+ │ │ │ │ │ │ │ │ │ │ 42.0 │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ │ │ │ │ │ │ │ │ │ │ 20.0 -> │
+ │ │ │ │ │ │ │ │ │ │ 21.0 │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├────────┼────────┼──────┼────────┼─────┼─────┼────────┼─────────┼────────┼─────────┤
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │ 4 -> 5 │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├────────┼────────┼──────┼────────┼─────┼─────┼────────┼─────────┼────────┼─────────┤
+ │ status │ 80.00% │ │ │ │ │ │ │ │ "c" -> │
+ │ │ │ │ │ │ │ │ │ │ "x" │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ └────────┴────────┴──────┴────────┴─────┴─────┴────────┴─────────┴────────┴─────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
index a91325f..2fd9096 100644
--- a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
@@ -20,22 +20,26 @@
Columns
▔▔▔▔▔▔▔
- ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┳━━━━━━━━━━━┓
- ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Top ┃
- ┃ Column ┃ Match Rate ┃ mean ┃ median ┃ min ┃ max ┃ std ┃ mad ┃ mrd ┃ Changes ┃
- ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━╇━━━━━━━━━━━┩
- │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │ 40.0 -> │
- │ │ │ │ │ │ │ │ │ │ 42.0 (1x, │
- │ │ │ │ │ │ │ │ │ │ e.g. 4) │
- │ │ │ │ │ │ │ │ │ │ 20.0 -> │
- │ │ │ │ │ │ │ │ │ │ 21.0 (1x, │
- │ │ │ │ │ │ │ │ │ │ e.g. 2) │
- ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
- │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │ 4 -> 5 │
- │ │ │ │ │ │ │ │ │ │ (1x, e.g. │
- │ │ │ │ │ │ │ │ │ │ 4) │
- ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
- │ status │ 80.00% │ │ │ │ │ │ │ │ "c" -> │
- │ │ │ │ │ │ │ │ │ │ "x" (1x, │
- │ │ │ │ │ │ │ │ │ │ e.g. 3) │
- └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┴───────────┘
+ ┏━━━━━━━━┳━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Mean ┃ Mean ┃ ┃
+ ┃ ┃ Match ┃ ┃ ┃ ┃ ┃ ┃ absolu… ┃ relat… ┃ Top ┃
+ ┃ Column ┃ Rate ┃ Mean ┃ Median ┃ Min ┃ Max ┃ Std ┃ deviat… ┃ devia… ┃ Changes ┃
+ ┡━━━━━━━━╇━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │ 40.0 -> │
+ │ │ │ │ │ │ │ │ │ │ 42.0 │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. 4) │
+ │ │ │ │ │ │ │ │ │ │ 20.0 -> │
+ │ │ │ │ │ │ │ │ │ │ 21.0 │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. 2) │
+ ├────────┼────────┼──────┼────────┼─────┼─────┼────────┼─────────┼────────┼─────────┤
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │ 4 -> 5 │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. 4) │
+ ├────────┼────────┼──────┼────────┼─────┼─────┼────────┼─────────┼────────┼─────────┤
+ │ status │ 80.00% │ │ │ │ │ │ │ │ "c" -> │
+ │ │ │ │ │ │ │ │ │ │ "x" │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. 3) │
+ └────────┴────────┴──────┴────────┴─────┴─────┴────────┴─────────┴────────┴─────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
index 811a324..eb395dd 100644
--- a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
@@ -8,18 +8,22 @@
Columns
▔▔▔▔▔▔▔
- ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┳━━━━━━━━━━━┓
- ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Top ┃
- ┃ Column ┃ Match Rate ┃ mean ┃ median ┃ min ┃ max ┃ std ┃ mad ┃ mrd ┃ Changes ┃
- ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━╇━━━━━━━━━━━┩
- │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │ 40.0 -> │
- │ │ │ │ │ │ │ │ │ │ 42.0 (1x) │
- │ │ │ │ │ │ │ │ │ │ 20.0 -> │
- │ │ │ │ │ │ │ │ │ │ 21.0 (1x) │
- ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
- │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │ 4 -> 5 │
- │ │ │ │ │ │ │ │ │ │ (1x) │
- ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
- │ status │ 80.00% │ │ │ │ │ │ │ │ "c" -> │
- │ │ │ │ │ │ │ │ │ │ "x" (1x) │
- └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┴───────────┘
+ ┏━━━━━━━━┳━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Mean ┃ Mean ┃ ┃
+ ┃ ┃ Match ┃ ┃ ┃ ┃ ┃ ┃ absolu… ┃ relat… ┃ Top ┃
+ ┃ Column ┃ Rate ┃ Mean ┃ Median ┃ Min ┃ Max ┃ Std ┃ deviat… ┃ devia… ┃ Changes ┃
+ ┡━━━━━━━━╇━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │ 40.0 -> │
+ │ │ │ │ │ │ │ │ │ │ 42.0 │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ │ │ │ │ │ │ │ │ │ │ 20.0 -> │
+ │ │ │ │ │ │ │ │ │ │ 21.0 │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├────────┼────────┼──────┼────────┼─────┼─────┼────────┼─────────┼────────┼─────────┤
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │ 4 -> 5 │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├────────┼────────┼──────┼────────┼─────┼─────┼────────┼─────────┼────────┼─────────┤
+ │ status │ 80.00% │ │ │ │ │ │ │ │ "c" -> │
+ │ │ │ │ │ │ │ │ │ │ "x" │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ └────────┴────────┴──────┴────────┴─────┴─────┴────────┴─────────┴────────┴─────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
index 09353d4..31d7344 100644
--- a/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_False_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
@@ -8,22 +8,26 @@
Columns
▔▔▔▔▔▔▔
- ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┳━━━━━━━━━━━┓
- ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Top ┃
- ┃ Column ┃ Match Rate ┃ mean ┃ median ┃ min ┃ max ┃ std ┃ mad ┃ mrd ┃ Changes ┃
- ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━╇━━━━━━━━━━━┩
- │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │ 40.0 -> │
- │ │ │ │ │ │ │ │ │ │ 42.0 (1x, │
- │ │ │ │ │ │ │ │ │ │ e.g. 4) │
- │ │ │ │ │ │ │ │ │ │ 20.0 -> │
- │ │ │ │ │ │ │ │ │ │ 21.0 (1x, │
- │ │ │ │ │ │ │ │ │ │ e.g. 2) │
- ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
- │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │ 4 -> 5 │
- │ │ │ │ │ │ │ │ │ │ (1x, e.g. │
- │ │ │ │ │ │ │ │ │ │ 4) │
- ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
- │ status │ 80.00% │ │ │ │ │ │ │ │ "c" -> │
- │ │ │ │ │ │ │ │ │ │ "x" (1x, │
- │ │ │ │ │ │ │ │ │ │ e.g. 3) │
- └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┴───────────┘
+ ┏━━━━━━━━┳━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┓
+ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Mean ┃ Mean ┃ ┃
+ ┃ ┃ Match ┃ ┃ ┃ ┃ ┃ ┃ absolu… ┃ relat… ┃ Top ┃
+ ┃ Column ┃ Rate ┃ Mean ┃ Median ┃ Min ┃ Max ┃ Std ┃ deviat… ┃ devia… ┃ Changes ┃
+ ┡━━━━━━━━╇━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━┩
+ │ price │ 60.00% │ 0.6 │ 0 │ 0 │ 2 │ 0.8944 │ 0.6 │ 0.02 │ 40.0 -> │
+ │ │ │ │ │ │ │ │ │ │ 42.0 │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. 4) │
+ │ │ │ │ │ │ │ │ │ │ 20.0 -> │
+ │ │ │ │ │ │ │ │ │ │ 21.0 │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. 2) │
+ ├────────┼────────┼──────┼────────┼─────┼─────┼────────┼─────────┼────────┼─────────┤
+ │ qty │ 80.00% │ 0.2 │ 0 │ 0 │ 1 │ 0.4472 │ 0.2 │ 0.05 │ 4 -> 5 │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. 4) │
+ ├────────┼────────┼──────┼────────┼─────┼─────┼────────┼─────────┼────────┼─────────┤
+ │ status │ 80.00% │ │ │ │ │ │ │ │ "c" -> │
+ │ │ │ │ │ │ │ │ │ │ "x" │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. 3) │
+ └────────┴────────┴──────┴────────┴─────┴─────┴────────┴─────────┴────────┴─────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
index 3726ca2..0acf593 100644
--- a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_False_sample_pk_False.txt
@@ -20,10 +20,12 @@
[1mColumns[0m
▔▔▔▔▔▔▔
- ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┓
- ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmedian[0m[1m [0m┃[1m [0m[1mmin[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m std[0m[1m [0m┃[1m [0m[1mmad[0m[1m [0m┃[1m [0m[1m mrd[0m[1m [0m┃
- ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━┩
- │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │
- │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │
- │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │
- └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┘
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m absolute[0m[1m [0m┃[1m [0m[1m relative[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mMean[0m[1m [0m┃[1m [0m[1mMedian[0m[1m [0m┃[1m [0m[1mMin[0m[1m [0m┃[1m [0m[1mMax[0m[1m [0m┃[1m [0m[1m Std[0m[1m [0m┃[1m [0m[1m deviation[0m[1m [0m┃[1m [0m[1mdeviation[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴────────────┴───────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
index 3726ca2..0acf593 100644
--- a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_False_slim_False_sample_rows_True_sample_pk_False.txt
@@ -20,10 +20,12 @@
[1mColumns[0m
▔▔▔▔▔▔▔
- ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┓
- ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmedian[0m[1m [0m┃[1m [0m[1mmin[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m std[0m[1m [0m┃[1m [0m[1mmad[0m[1m [0m┃[1m [0m[1m mrd[0m[1m [0m┃
- ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━┩
- │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │
- │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │
- │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │
- └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┘
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m absolute[0m[1m [0m┃[1m [0m[1m relative[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mMean[0m[1m [0m┃[1m [0m[1mMedian[0m[1m [0m┃[1m [0m[1mMin[0m[1m [0m┃[1m [0m[1mMax[0m[1m [0m┃[1m [0m[1m Std[0m[1m [0m┃[1m [0m[1m deviation[0m[1m [0m┃[1m [0m[1mdeviation[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴────────────┴───────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
index 610dfc4..5510545 100644
--- a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_False_sample_pk_False.txt
@@ -8,10 +8,12 @@
[1mColumns[0m
▔▔▔▔▔▔▔
- ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┓
- ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmedian[0m[1m [0m┃[1m [0m[1mmin[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m std[0m[1m [0m┃[1m [0m[1mmad[0m[1m [0m┃[1m [0m[1m mrd[0m[1m [0m┃
- ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━┩
- │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │
- │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │
- │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │
- └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┘
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m absolute[0m[1m [0m┃[1m [0m[1m relative[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mMean[0m[1m [0m┃[1m [0m[1mMedian[0m[1m [0m┃[1m [0m[1mMin[0m[1m [0m┃[1m [0m[1mMax[0m[1m [0m┃[1m [0m[1m Std[0m[1m [0m┃[1m [0m[1m deviation[0m[1m [0m┃[1m [0m[1mdeviation[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴────────────┴───────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
index 610dfc4..5510545 100644
--- a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_False_slim_True_sample_rows_True_sample_pk_False.txt
@@ -8,10 +8,12 @@
[1mColumns[0m
▔▔▔▔▔▔▔
- ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┓
- ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmedian[0m[1m [0m┃[1m [0m[1mmin[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m std[0m[1m [0m┃[1m [0m[1mmad[0m[1m [0m┃[1m [0m[1m mrd[0m[1m [0m┃
- ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━┩
- │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │
- │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │
- │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │
- └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┘
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m absolute[0m[1m [0m┃[1m [0m[1m relative[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mMean[0m[1m [0m┃[1m [0m[1mMedian[0m[1m [0m┃[1m [0m[1mMin[0m[1m [0m┃[1m [0m[1mMax[0m[1m [0m┃[1m [0m[1m Std[0m[1m [0m┃[1m [0m[1m deviation[0m[1m [0m┃[1m [0m[1mdeviation[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴────────────┴───────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
index e6c2d3a..ca13fa5 100644
--- a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_False_sample_pk_False.txt
@@ -20,18 +20,22 @@
[1mColumns[0m
▔▔▔▔▔▔▔
- ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┳━━━━━━━━━━━┓
- ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Top[0m[1m [0m┃
- ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmedian[0m[1m [0m┃[1m [0m[1mmin[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m std[0m[1m [0m┃[1m [0m[1mmad[0m[1m [0m┃[1m [0m[1m mrd[0m[1m [0m┃[1m [0m[1m Changes[0m[1m [0m┃
- ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━╇━━━━━━━━━━━┩
- │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │ [33m40.0[0m -> │
- │ │ │ │ │ │ │ │ │ │ [33m42.0[0m (1x) │
- │ │ │ │ │ │ │ │ │ │ [33m20.0[0m -> │
- │ │ │ │ │ │ │ │ │ │ [33m21.0[0m (1x) │
- ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
- │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │ [33m4[0m -> [33m5[0m │
- │ │ │ │ │ │ │ │ │ │ (1x) │
- ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
- │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │ [33m"c"[0m -> │
- │ │ │ │ │ │ │ │ │ │ [33m"x"[0m (1x) │
- └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┴───────────┘
+ ┏━━━━━━━━┳━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m┃
+ ┃[1m [0m┃[1m [0m[1m Match[0m[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1mabsolu…[0m[1m [0m┃[1m [0m[1mrelat…[0m[1m [0m┃[1m [0m[1m Top[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1m Rate[0m[1m [0m┃[1m [0m[1mMean[0m[1m [0m┃[1m [0m[1mMedian[0m[1m [0m┃[1m [0m[1mMin[0m[1m [0m┃[1m [0m[1mMax[0m[1m [0m┃[1m [0m[1m Std[0m[1m [0m┃[1m [0m[1mdeviat…[0m[1m [0m┃[1m [0m[1mdevia…[0m[1m [0m┃[1m [0m[1mChanges[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │ [33m40.0[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m42.0[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ │ │ │ │ │ │ │ │ │ │ [33m20.0[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m21.0[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├────────┼────────┼──────┼────────┼─────┼─────┼────────┼─────────┼────────┼─────────┤
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │ [33m4[0m -> [33m5[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├────────┼────────┼──────┼────────┼─────┼─────┼────────┼─────────┼────────┼─────────┤
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │ [33m"c"[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m"x"[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ └────────┴────────┴──────┴────────┴─────┴─────┴────────┴─────────┴────────┴─────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
index d310b83..f07a09b 100644
--- a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_True_slim_False_sample_rows_True_sample_pk_True.txt
@@ -20,22 +20,26 @@
[1mColumns[0m
▔▔▔▔▔▔▔
- ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┳━━━━━━━━━━━┓
- ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Top[0m[1m [0m┃
- ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmedian[0m[1m [0m┃[1m [0m[1mmin[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m std[0m[1m [0m┃[1m [0m[1mmad[0m[1m [0m┃[1m [0m[1m mrd[0m[1m [0m┃[1m [0m[1m Changes[0m[1m [0m┃
- ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━╇━━━━━━━━━━━┩
- │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │ [33m40.0[0m -> │
- │ │ │ │ │ │ │ │ │ │ [33m42.0[0m (1x, │
- │ │ │ │ │ │ │ │ │ │ e.g. [33m4[0m) │
- │ │ │ │ │ │ │ │ │ │ [33m20.0[0m -> │
- │ │ │ │ │ │ │ │ │ │ [33m21.0[0m (1x, │
- │ │ │ │ │ │ │ │ │ │ e.g. [33m2[0m) │
- ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
- │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │ [33m4[0m -> [33m5[0m │
- │ │ │ │ │ │ │ │ │ │ (1x, e.g. │
- │ │ │ │ │ │ │ │ │ │ [33m4[0m) │
- ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
- │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │ [33m"c"[0m -> │
- │ │ │ │ │ │ │ │ │ │ [33m"x"[0m (1x, │
- │ │ │ │ │ │ │ │ │ │ e.g. [33m3[0m) │
- └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┴───────────┘
+ ┏━━━━━━━━┳━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m┃
+ ┃[1m [0m┃[1m [0m[1m Match[0m[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1mabsolu…[0m[1m [0m┃[1m [0m[1mrelat…[0m[1m [0m┃[1m [0m[1m Top[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1m Rate[0m[1m [0m┃[1m [0m[1mMean[0m[1m [0m┃[1m [0m[1mMedian[0m[1m [0m┃[1m [0m[1mMin[0m[1m [0m┃[1m [0m[1mMax[0m[1m [0m┃[1m [0m[1m Std[0m[1m [0m┃[1m [0m[1mdeviat…[0m[1m [0m┃[1m [0m[1mdevia…[0m[1m [0m┃[1m [0m[1mChanges[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │ [33m40.0[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m42.0[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. [33m4[0m) │
+ │ │ │ │ │ │ │ │ │ │ [33m20.0[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m21.0[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. [33m2[0m) │
+ ├────────┼────────┼──────┼────────┼─────┼─────┼────────┼─────────┼────────┼─────────┤
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │ [33m4[0m -> [33m5[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. [33m4[0m) │
+ ├────────┼────────┼──────┼────────┼─────┼─────┼────────┼─────────┼────────┼─────────┤
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │ [33m"c"[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m"x"[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. [33m3[0m) │
+ └────────┴────────┴──────┴────────┴─────┴─────┴────────┴─────────┴────────┴─────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
index d979e56..939f003 100644
--- a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_False_sample_pk_False.txt
@@ -8,18 +8,22 @@
[1mColumns[0m
▔▔▔▔▔▔▔
- ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┳━━━━━━━━━━━┓
- ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Top[0m[1m [0m┃
- ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmedian[0m[1m [0m┃[1m [0m[1mmin[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m std[0m[1m [0m┃[1m [0m[1mmad[0m[1m [0m┃[1m [0m[1m mrd[0m[1m [0m┃[1m [0m[1m Changes[0m[1m [0m┃
- ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━╇━━━━━━━━━━━┩
- │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │ [33m40.0[0m -> │
- │ │ │ │ │ │ │ │ │ │ [33m42.0[0m (1x) │
- │ │ │ │ │ │ │ │ │ │ [33m20.0[0m -> │
- │ │ │ │ │ │ │ │ │ │ [33m21.0[0m (1x) │
- ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
- │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │ [33m4[0m -> [33m5[0m │
- │ │ │ │ │ │ │ │ │ │ (1x) │
- ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
- │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │ [33m"c"[0m -> │
- │ │ │ │ │ │ │ │ │ │ [33m"x"[0m (1x) │
- └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┴───────────┘
+ ┏━━━━━━━━┳━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m┃
+ ┃[1m [0m┃[1m [0m[1m Match[0m[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1mabsolu…[0m[1m [0m┃[1m [0m[1mrelat…[0m[1m [0m┃[1m [0m[1m Top[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1m Rate[0m[1m [0m┃[1m [0m[1mMean[0m[1m [0m┃[1m [0m[1mMedian[0m[1m [0m┃[1m [0m[1mMin[0m[1m [0m┃[1m [0m[1mMax[0m[1m [0m┃[1m [0m[1m Std[0m[1m [0m┃[1m [0m[1mdeviat…[0m[1m [0m┃[1m [0m[1mdevia…[0m[1m [0m┃[1m [0m[1mChanges[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │ [33m40.0[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m42.0[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ │ │ │ │ │ │ │ │ │ │ [33m20.0[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m21.0[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├────────┼────────┼──────┼────────┼─────┼─────┼────────┼─────────┼────────┼─────────┤
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │ [33m4[0m -> [33m5[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├────────┼────────┼──────┼────────┼─────┼─────┼────────┼─────────┼────────┼─────────┤
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │ [33m"c"[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m"x"[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ └────────┴────────┴──────┴────────┴─────┴─────┴────────┴─────────┴────────┴─────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
index f0ab5a8..ae20da9 100644
--- a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_False_top_True_slim_True_sample_rows_True_sample_pk_True.txt
@@ -8,22 +8,26 @@
[1mColumns[0m
▔▔▔▔▔▔▔
- ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┳━━━━━━━━━━━┓
- ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Top[0m[1m [0m┃
- ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmedian[0m[1m [0m┃[1m [0m[1mmin[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m std[0m[1m [0m┃[1m [0m[1mmad[0m[1m [0m┃[1m [0m[1m mrd[0m[1m [0m┃[1m [0m[1m Changes[0m[1m [0m┃
- ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━╇━━━━━━━━━━━┩
- │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │ [33m40.0[0m -> │
- │ │ │ │ │ │ │ │ │ │ [33m42.0[0m (1x, │
- │ │ │ │ │ │ │ │ │ │ e.g. [33m4[0m) │
- │ │ │ │ │ │ │ │ │ │ [33m20.0[0m -> │
- │ │ │ │ │ │ │ │ │ │ [33m21.0[0m (1x, │
- │ │ │ │ │ │ │ │ │ │ e.g. [33m2[0m) │
- ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
- │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │ [33m4[0m -> [33m5[0m │
- │ │ │ │ │ │ │ │ │ │ (1x, e.g. │
- │ │ │ │ │ │ │ │ │ │ [33m4[0m) │
- ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
- │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │ [33m"c"[0m -> │
- │ │ │ │ │ │ │ │ │ │ [33m"x"[0m (1x, │
- │ │ │ │ │ │ │ │ │ │ e.g. [33m3[0m) │
- └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┴───────────┘
+ ┏━━━━━━━━┳━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m┃
+ ┃[1m [0m┃[1m [0m[1m Match[0m[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1mabsolu…[0m[1m [0m┃[1m [0m[1mrelat…[0m[1m [0m┃[1m [0m[1m Top[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1m Rate[0m[1m [0m┃[1m [0m[1mMean[0m[1m [0m┃[1m [0m[1mMedian[0m[1m [0m┃[1m [0m[1mMin[0m[1m [0m┃[1m [0m[1mMax[0m[1m [0m┃[1m [0m[1m Std[0m[1m [0m┃[1m [0m[1mdeviat…[0m[1m [0m┃[1m [0m[1mdevia…[0m[1m [0m┃[1m [0m[1mChanges[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │ [33m40.0[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m42.0[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. [33m4[0m) │
+ │ │ │ │ │ │ │ │ │ │ [33m20.0[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m21.0[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. [33m2[0m) │
+ ├────────┼────────┼──────┼────────┼─────┼─────┼────────┼─────────┼────────┼─────────┤
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │ [33m4[0m -> [33m5[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. [33m4[0m) │
+ ├────────┼────────┼──────┼────────┼─────┼─────┼────────┼─────────┼────────┼─────────┤
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │ [33m"c"[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m"x"[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. [33m3[0m) │
+ └────────┴────────┴──────┴────────┴─────┴─────┴────────┴─────────┴────────┴─────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
index 3726ca2..0acf593 100644
--- a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_False_sample_pk_False.txt
@@ -20,10 +20,12 @@
[1mColumns[0m
▔▔▔▔▔▔▔
- ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┓
- ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmedian[0m[1m [0m┃[1m [0m[1mmin[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m std[0m[1m [0m┃[1m [0m[1mmad[0m[1m [0m┃[1m [0m[1m mrd[0m[1m [0m┃
- ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━┩
- │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │
- │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │
- │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │
- └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┘
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m absolute[0m[1m [0m┃[1m [0m[1m relative[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mMean[0m[1m [0m┃[1m [0m[1mMedian[0m[1m [0m┃[1m [0m[1mMin[0m[1m [0m┃[1m [0m[1mMax[0m[1m [0m┃[1m [0m[1m Std[0m[1m [0m┃[1m [0m[1m deviation[0m[1m [0m┃[1m [0m[1mdeviation[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴────────────┴───────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
index 3726ca2..0acf593 100644
--- a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_False_slim_False_sample_rows_True_sample_pk_False.txt
@@ -20,10 +20,12 @@
[1mColumns[0m
▔▔▔▔▔▔▔
- ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┓
- ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmedian[0m[1m [0m┃[1m [0m[1mmin[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m std[0m[1m [0m┃[1m [0m[1mmad[0m[1m [0m┃[1m [0m[1m mrd[0m[1m [0m┃
- ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━┩
- │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │
- │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │
- │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │
- └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┘
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m absolute[0m[1m [0m┃[1m [0m[1m relative[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mMean[0m[1m [0m┃[1m [0m[1mMedian[0m[1m [0m┃[1m [0m[1mMin[0m[1m [0m┃[1m [0m[1mMax[0m[1m [0m┃[1m [0m[1m Std[0m[1m [0m┃[1m [0m[1m deviation[0m[1m [0m┃[1m [0m[1mdeviation[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴────────────┴───────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
index 610dfc4..5510545 100644
--- a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_False_sample_pk_False.txt
@@ -8,10 +8,12 @@
[1mColumns[0m
▔▔▔▔▔▔▔
- ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┓
- ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmedian[0m[1m [0m┃[1m [0m[1mmin[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m std[0m[1m [0m┃[1m [0m[1mmad[0m[1m [0m┃[1m [0m[1m mrd[0m[1m [0m┃
- ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━┩
- │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │
- │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │
- │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │
- └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┘
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m absolute[0m[1m [0m┃[1m [0m[1m relative[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mMean[0m[1m [0m┃[1m [0m[1mMedian[0m[1m [0m┃[1m [0m[1mMin[0m[1m [0m┃[1m [0m[1mMax[0m[1m [0m┃[1m [0m[1m Std[0m[1m [0m┃[1m [0m[1m deviation[0m[1m [0m┃[1m [0m[1mdeviation[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴────────────┴───────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
index 610dfc4..5510545 100644
--- a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_False_slim_True_sample_rows_True_sample_pk_False.txt
@@ -8,10 +8,12 @@
[1mColumns[0m
▔▔▔▔▔▔▔
- ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┓
- ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmedian[0m[1m [0m┃[1m [0m[1mmin[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m std[0m[1m [0m┃[1m [0m[1mmad[0m[1m [0m┃[1m [0m[1m mrd[0m[1m [0m┃
- ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━┩
- │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │
- │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │
- │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │
- └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┘
+ ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m absolute[0m[1m [0m┃[1m [0m[1m relative[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mMean[0m[1m [0m┃[1m [0m[1mMedian[0m[1m [0m┃[1m [0m[1mMin[0m[1m [0m┃[1m [0m[1mMax[0m[1m [0m┃[1m [0m[1m Std[0m[1m [0m┃[1m [0m[1m deviation[0m[1m [0m┃[1m [0m[1mdeviation[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │
+ └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴────────────┴───────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
index e6c2d3a..ca13fa5 100644
--- a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_False_sample_pk_False.txt
@@ -20,18 +20,22 @@
[1mColumns[0m
▔▔▔▔▔▔▔
- ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┳━━━━━━━━━━━┓
- ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Top[0m[1m [0m┃
- ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmedian[0m[1m [0m┃[1m [0m[1mmin[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m std[0m[1m [0m┃[1m [0m[1mmad[0m[1m [0m┃[1m [0m[1m mrd[0m[1m [0m┃[1m [0m[1m Changes[0m[1m [0m┃
- ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━╇━━━━━━━━━━━┩
- │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │ [33m40.0[0m -> │
- │ │ │ │ │ │ │ │ │ │ [33m42.0[0m (1x) │
- │ │ │ │ │ │ │ │ │ │ [33m20.0[0m -> │
- │ │ │ │ │ │ │ │ │ │ [33m21.0[0m (1x) │
- ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
- │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │ [33m4[0m -> [33m5[0m │
- │ │ │ │ │ │ │ │ │ │ (1x) │
- ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
- │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │ [33m"c"[0m -> │
- │ │ │ │ │ │ │ │ │ │ [33m"x"[0m (1x) │
- └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┴───────────┘
+ ┏━━━━━━━━┳━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m┃
+ ┃[1m [0m┃[1m [0m[1m Match[0m[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1mabsolu…[0m[1m [0m┃[1m [0m[1mrelat…[0m[1m [0m┃[1m [0m[1m Top[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1m Rate[0m[1m [0m┃[1m [0m[1mMean[0m[1m [0m┃[1m [0m[1mMedian[0m[1m [0m┃[1m [0m[1mMin[0m[1m [0m┃[1m [0m[1mMax[0m[1m [0m┃[1m [0m[1m Std[0m[1m [0m┃[1m [0m[1mdeviat…[0m[1m [0m┃[1m [0m[1mdevia…[0m[1m [0m┃[1m [0m[1mChanges[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │ [33m40.0[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m42.0[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ │ │ │ │ │ │ │ │ │ │ [33m20.0[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m21.0[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├────────┼────────┼──────┼────────┼─────┼─────┼────────┼─────────┼────────┼─────────┤
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │ [33m4[0m -> [33m5[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├────────┼────────┼──────┼────────┼─────┼─────┼────────┼─────────┼────────┼─────────┤
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │ [33m"c"[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m"x"[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ └────────┴────────┴──────┴────────┴─────┴─────┴────────┴─────────┴────────┴─────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
index d310b83..f07a09b 100644
--- a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_True_slim_False_sample_rows_True_sample_pk_True.txt
@@ -20,22 +20,26 @@
[1mColumns[0m
▔▔▔▔▔▔▔
- ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┳━━━━━━━━━━━┓
- ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Top[0m[1m [0m┃
- ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmedian[0m[1m [0m┃[1m [0m[1mmin[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m std[0m[1m [0m┃[1m [0m[1mmad[0m[1m [0m┃[1m [0m[1m mrd[0m[1m [0m┃[1m [0m[1m Changes[0m[1m [0m┃
- ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━╇━━━━━━━━━━━┩
- │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │ [33m40.0[0m -> │
- │ │ │ │ │ │ │ │ │ │ [33m42.0[0m (1x, │
- │ │ │ │ │ │ │ │ │ │ e.g. [33m4[0m) │
- │ │ │ │ │ │ │ │ │ │ [33m20.0[0m -> │
- │ │ │ │ │ │ │ │ │ │ [33m21.0[0m (1x, │
- │ │ │ │ │ │ │ │ │ │ e.g. [33m2[0m) │
- ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
- │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │ [33m4[0m -> [33m5[0m │
- │ │ │ │ │ │ │ │ │ │ (1x, e.g. │
- │ │ │ │ │ │ │ │ │ │ [33m4[0m) │
- ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
- │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │ [33m"c"[0m -> │
- │ │ │ │ │ │ │ │ │ │ [33m"x"[0m (1x, │
- │ │ │ │ │ │ │ │ │ │ e.g. [33m3[0m) │
- └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┴───────────┘
+ ┏━━━━━━━━┳━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m┃
+ ┃[1m [0m┃[1m [0m[1m Match[0m[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1mabsolu…[0m[1m [0m┃[1m [0m[1mrelat…[0m[1m [0m┃[1m [0m[1m Top[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1m Rate[0m[1m [0m┃[1m [0m[1mMean[0m[1m [0m┃[1m [0m[1mMedian[0m[1m [0m┃[1m [0m[1mMin[0m[1m [0m┃[1m [0m[1mMax[0m[1m [0m┃[1m [0m[1m Std[0m[1m [0m┃[1m [0m[1mdeviat…[0m[1m [0m┃[1m [0m[1mdevia…[0m[1m [0m┃[1m [0m[1mChanges[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │ [33m40.0[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m42.0[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. [33m4[0m) │
+ │ │ │ │ │ │ │ │ │ │ [33m20.0[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m21.0[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. [33m2[0m) │
+ ├────────┼────────┼──────┼────────┼─────┼─────┼────────┼─────────┼────────┼─────────┤
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │ [33m4[0m -> [33m5[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. [33m4[0m) │
+ ├────────┼────────┼──────┼────────┼─────┼─────┼────────┼─────────┼────────┼─────────┤
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │ [33m"c"[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m"x"[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. [33m3[0m) │
+ └────────┴────────┴──────┴────────┴─────┴─────┴────────┴─────────┴────────┴─────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
index d979e56..939f003 100644
--- a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_False_sample_pk_False.txt
@@ -8,18 +8,22 @@
[1mColumns[0m
▔▔▔▔▔▔▔
- ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┳━━━━━━━━━━━┓
- ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Top[0m[1m [0m┃
- ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmedian[0m[1m [0m┃[1m [0m[1mmin[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m std[0m[1m [0m┃[1m [0m[1mmad[0m[1m [0m┃[1m [0m[1m mrd[0m[1m [0m┃[1m [0m[1m Changes[0m[1m [0m┃
- ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━╇━━━━━━━━━━━┩
- │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │ [33m40.0[0m -> │
- │ │ │ │ │ │ │ │ │ │ [33m42.0[0m (1x) │
- │ │ │ │ │ │ │ │ │ │ [33m20.0[0m -> │
- │ │ │ │ │ │ │ │ │ │ [33m21.0[0m (1x) │
- ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
- │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │ [33m4[0m -> [33m5[0m │
- │ │ │ │ │ │ │ │ │ │ (1x) │
- ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
- │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │ [33m"c"[0m -> │
- │ │ │ │ │ │ │ │ │ │ [33m"x"[0m (1x) │
- └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┴───────────┘
+ ┏━━━━━━━━┳━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m┃
+ ┃[1m [0m┃[1m [0m[1m Match[0m[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1mabsolu…[0m[1m [0m┃[1m [0m[1mrelat…[0m[1m [0m┃[1m [0m[1m Top[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1m Rate[0m[1m [0m┃[1m [0m[1mMean[0m[1m [0m┃[1m [0m[1mMedian[0m[1m [0m┃[1m [0m[1mMin[0m[1m [0m┃[1m [0m[1mMax[0m[1m [0m┃[1m [0m[1m Std[0m[1m [0m┃[1m [0m[1mdeviat…[0m[1m [0m┃[1m [0m[1mdevia…[0m[1m [0m┃[1m [0m[1mChanges[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │ [33m40.0[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m42.0[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ │ │ │ │ │ │ │ │ │ │ [33m20.0[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m21.0[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├────────┼────────┼──────┼────────┼─────┼─────┼────────┼─────────┼────────┼─────────┤
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │ [33m4[0m -> [33m5[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ ├────────┼────────┼──────┼────────┼─────┼─────┼────────┼─────────┼────────┼─────────┤
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │ [33m"c"[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m"x"[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x) │
+ └────────┴────────┴──────┴────────┴─────┴─────┴────────┴─────────┴────────┴─────────┘
diff --git a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
index f0ab5a8..ae20da9 100644
--- a/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
+++ b/tests/summary/fixtures/metrics_presets_many/gen/pretty_True_perfect_True_top_True_slim_True_sample_rows_True_sample_pk_True.txt
@@ -8,22 +8,26 @@
[1mColumns[0m
▔▔▔▔▔▔▔
- ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━┳━━━━━━━━━━━┓
- ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Top[0m[1m [0m┃
- ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1mMatch Rate[0m[1m [0m┃[1m [0m[1mmean[0m[1m [0m┃[1m [0m[1mmedian[0m[1m [0m┃[1m [0m[1mmin[0m[1m [0m┃[1m [0m[1mmax[0m[1m [0m┃[1m [0m[1m std[0m[1m [0m┃[1m [0m[1mmad[0m[1m [0m┃[1m [0m[1m mrd[0m[1m [0m┃[1m [0m[1m Changes[0m[1m [0m┃
- ┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━╇━━━━━━━━━━━┩
- │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │ [33m40.0[0m -> │
- │ │ │ │ │ │ │ │ │ │ [33m42.0[0m (1x, │
- │ │ │ │ │ │ │ │ │ │ e.g. [33m4[0m) │
- │ │ │ │ │ │ │ │ │ │ [33m20.0[0m -> │
- │ │ │ │ │ │ │ │ │ │ [33m21.0[0m (1x, │
- │ │ │ │ │ │ │ │ │ │ e.g. [33m2[0m) │
- ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
- │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │ [33m4[0m -> [33m5[0m │
- │ │ │ │ │ │ │ │ │ │ (1x, e.g. │
- │ │ │ │ │ │ │ │ │ │ [33m4[0m) │
- ├────────┼────────────┼──────┼────────┼─────┼─────┼────────┼─────┼──────┼───────────┤
- │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │ [33m"c"[0m -> │
- │ │ │ │ │ │ │ │ │ │ [33m"x"[0m (1x, │
- │ │ │ │ │ │ │ │ │ │ e.g. [33m3[0m) │
- └────────┴────────────┴──────┴────────┴─────┴─────┴────────┴─────┴──────┴───────────┘
+ ┏━━━━━━━━┳━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┓
+ ┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m[1m Mean[0m[1m [0m┃[1m [0m┃
+ ┃[1m [0m┃[1m [0m[1m Match[0m[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m┃[1m [0m[1mabsolu…[0m[1m [0m┃[1m [0m[1mrelat…[0m[1m [0m┃[1m [0m[1m Top[0m[1m [0m┃
+ ┃[1m [0m[1mColumn[0m[1m [0m┃[1m [0m[1m Rate[0m[1m [0m┃[1m [0m[1mMean[0m[1m [0m┃[1m [0m[1mMedian[0m[1m [0m┃[1m [0m[1mMin[0m[1m [0m┃[1m [0m[1mMax[0m[1m [0m┃[1m [0m[1m Std[0m[1m [0m┃[1m [0m[1mdeviat…[0m[1m [0m┃[1m [0m[1mdevia…[0m[1m [0m┃[1m [0m[1mChanges[0m[1m [0m┃
+ ┡━━━━━━━━╇━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━┩
+ │ [36mprice [0m │ 60.00% │ [33m0.6[0m │ [33m0[0m │ [33m0[0m │ [33m2[0m │ [33m0.8944[0m │ [33m0.6[0m │ [33m0.02[0m │ [33m40.0[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m42.0[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. [33m4[0m) │
+ │ │ │ │ │ │ │ │ │ │ [33m20.0[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m21.0[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. [33m2[0m) │
+ ├────────┼────────┼──────┼────────┼─────┼─────┼────────┼─────────┼────────┼─────────┤
+ │ [36mqty [0m │ 80.00% │ [33m0.2[0m │ [33m0[0m │ [33m0[0m │ [33m1[0m │ [33m0.4472[0m │ [33m0.2[0m │ [33m0.05[0m │ [33m4[0m -> [33m5[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. [33m4[0m) │
+ ├────────┼────────┼──────┼────────┼─────┼─────┼────────┼─────────┼────────┼─────────┤
+ │ [36mstatus[0m │ 80.00% │ │ │ │ │ │ │ │ [33m"c"[0m -> │
+ │ │ │ │ │ │ │ │ │ │ [33m"x"[0m │
+ │ │ │ │ │ │ │ │ │ │ (1x, │
+ │ │ │ │ │ │ │ │ │ │ e.g. [33m3[0m) │
+ └────────┴────────┴──────┴────────┴─────┴─────┴────────┴─────────┴────────┴─────────┘
From 019d91417d52a230b0424c4e0f5f32172d049026 Mon Sep 17 00:00:00 2001
From: EgeKaraismailogluQC
<128645043+EgeKaraismailogluQC@users.noreply.github.com>
Date: Wed, 6 May 2026 09:28:03 +0200
Subject: [PATCH 15/19] review
---
docs/guides/features/summary.ipynb | 6 +++---
tests/summary/test_summary.py | 14 +++++++-------
2 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/docs/guides/features/summary.ipynb b/docs/guides/features/summary.ipynb
index 524e7b2..bec4877 100644
--- a/docs/guides/features/summary.ipynb
+++ b/docs/guides/features/summary.ipynb
@@ -551,9 +551,9 @@
" comparison.summary(\n",
" top_k_column_changes=3,\n",
" metrics={\n",
- " \"mean\": metrics.mean,\n",
- " \"mrd\": metrics.mean_relative_deviation,\n",
- " \"max_abs\": lambda l, r: (r - l).abs().max(),\n",
+ " \"Mean\": metrics.mean,\n",
+ " \"Mean relative deviation\": metrics.mean_relative_deviation,\n",
+ " \"Max absolute deviation\": lambda l, r: (r - l).abs().max(),\n",
" },\n",
" )\n",
")"
diff --git a/tests/summary/test_summary.py b/tests/summary/test_summary.py
index 4faebc9..ad75558 100644
--- a/tests/summary/test_summary.py
+++ b/tests/summary/test_summary.py
@@ -258,12 +258,12 @@ def test_summary_data_metrics(show_perfect_column_matches: bool) -> None:
comp = _make_comparison()
summary = comp.summary(
show_perfect_column_matches=show_perfect_column_matches,
- metrics={"mean": metrics.mean, "max": metrics.max},
+ metrics={"Mean": metrics.mean, "Max": metrics.max},
)
result = json.loads(summary.to_json())
# Joined rows (id=1,2,3): value deltas = [0, 5, 0].
- value_metrics = {"mean": pytest.approx(5 / 3), "max": 5.0}
+ value_metrics = {"Mean": pytest.approx(5 / 3), "Max": 5.0}
expected_columns = []
if show_perfect_column_matches:
@@ -293,10 +293,10 @@ def test_summary_data_metrics(show_perfect_column_matches: bool) -> None:
def test_summary_format_with_metrics() -> None:
comp = _make_comparison()
rendered = comp.summary(
- metrics={"mean": metrics.mean},
+ metrics={"Mean": metrics.mean},
).format(pretty=False)
- # Header is shown when metrics are present; "mean" appears as a column header.
- assert "mean" in rendered
+ # Header is shown when metrics are present; "Mean" appears as a column header.
+ assert "Mean" in rendered
# Numeric column gets a rendered metric value; expected mean delta is 5/3.
assert "1.667" in rendered
@@ -306,7 +306,7 @@ def test_summary_metrics_no_joined_rows() -> None:
left = pl.DataFrame({"id": [1, 2], "value": [1.0, 2.0]})
right = pl.DataFrame({"id": [10, 20], "value": [5.0, 6.0]})
comp = compare_frames(left, right, primary_key="id")
- summary = comp.summary(metrics={"mean": metrics.mean})
+ summary = comp.summary(metrics={"Mean": metrics.mean})
result = json.loads(summary.to_json())
# With no joined rows, the columns section is skipped entirely.
assert result["columns"] is None
@@ -319,7 +319,7 @@ def test_summary_metrics_no_numeric_columns() -> None:
comp = compare_frames(left, right, primary_key="id")
summary = comp.summary(
show_perfect_column_matches=True,
- metrics={"mean": metrics.mean},
+ metrics={"Mean": metrics.mean},
)
result = json.loads(summary.to_json())
# status appears but has no metric values (non-numeric column).
From 73bb89fb07f9a7224694e5cd5079b02387093d3b Mon Sep 17 00:00:00 2001
From: EgeKaraismailogluQC
<128645043+EgeKaraismailogluQC@users.noreply.github.com>
Date: Wed, 6 May 2026 09:52:08 +0200
Subject: [PATCH 16/19] review
---
tests/summary/test_summary.py | 57 ++++++++++-------------------------
1 file changed, 16 insertions(+), 41 deletions(-)
diff --git a/tests/summary/test_summary.py b/tests/summary/test_summary.py
index ad75558..a79214e 100644
--- a/tests/summary/test_summary.py
+++ b/tests/summary/test_summary.py
@@ -155,10 +155,18 @@ def _make_comparison() -> DataFrameComparison:
@pytest.mark.parametrize(
- "show_perfect_column_matches, show_top_column_changes, slim, sample_rows, sample_pk, hide_value",
+ "show_perfect_column_matches, show_top_column_changes, slim, sample_rows, sample_pk, hide_value, with_metrics",
[
- (combo[0], combo[1], combo[2], combo[3], combo[3] and combo[1], combo[4])
- for combo in itertools.product([True, False], repeat=5)
+ (
+ combo[0],
+ combo[1],
+ combo[2],
+ combo[3],
+ combo[3] and combo[1],
+ combo[4],
+ combo[5],
+ )
+ for combo in itertools.product([True, False], repeat=6)
],
)
def test_summary_data_parametrized(
@@ -168,10 +176,12 @@ def test_summary_data_parametrized(
sample_rows: bool,
sample_pk: bool,
hide_value: bool,
+ with_metrics: bool,
) -> None:
comp = _make_comparison()
top_k = 3 if show_top_column_changes else 0
hidden_columns = ["value"] if hide_value else None
+ metrics_arg = {"Mean": metrics.mean, "Max": metrics.max} if with_metrics else None
summary = comp.summary(
show_perfect_column_matches=show_perfect_column_matches,
top_k_column_changes=top_k,
@@ -179,6 +189,7 @@ def test_summary_data_parametrized(
show_sample_primary_key_per_change=sample_pk,
slim=slim,
hidden_columns=hidden_columns,
+ metrics=metrics_arg,
)
result = json.loads(summary.to_json())
@@ -216,7 +227,8 @@ def test_summary_data_parametrized(
if show_value_changes
else None
),
- "metrics": None,
+ # Joined rows (id=1,2,3): value deltas = [0, 5, 0].
+ "metrics": {"Mean": pytest.approx(5 / 3), "Max": 5.0} if with_metrics else None,
}
expected_columns = []
if show_perfect_column_matches:
@@ -253,43 +265,6 @@ def test_summary_data_parametrized(
assert result == expected
-@pytest.mark.parametrize("show_perfect_column_matches", [True, False])
-def test_summary_data_metrics(show_perfect_column_matches: bool) -> None:
- comp = _make_comparison()
- summary = comp.summary(
- show_perfect_column_matches=show_perfect_column_matches,
- metrics={"Mean": metrics.mean, "Max": metrics.max},
- )
- result = json.loads(summary.to_json())
-
- # Joined rows (id=1,2,3): value deltas = [0, 5, 0].
- value_metrics = {"Mean": pytest.approx(5 / 3), "Max": 5.0}
-
- expected_columns = []
- if show_perfect_column_matches:
- # status is non-numeric → metrics stays None.
- expected_columns.append(
- {
- "name": "status",
- "match_rate": 1.0,
- "n_total_changes": 0,
- "changes": None,
- "metrics": None,
- }
- )
- expected_columns.append(
- {
- "name": "value",
- "match_rate": pytest.approx(2 / 3),
- "n_total_changes": 0,
- "changes": None,
- "metrics": value_metrics,
- }
- )
-
- assert result["columns"] == expected_columns
-
-
def test_summary_format_with_metrics() -> None:
comp = _make_comparison()
rendered = comp.summary(
From 58645d09c025fa91c2aafc569eb2953b070770c2 Mon Sep 17 00:00:00 2001
From: EgeKaraismailogluQC
<128645043+EgeKaraismailogluQC@users.noreply.github.com>
Date: Wed, 6 May 2026 10:01:08 +0200
Subject: [PATCH 17/19] review
---
tests/summary/test_summary.py | 36 -----------------------------------
tests/test_metrics.py | 5 +++++
2 files changed, 5 insertions(+), 36 deletions(-)
diff --git a/tests/summary/test_summary.py b/tests/summary/test_summary.py
index a79214e..6e43832 100644
--- a/tests/summary/test_summary.py
+++ b/tests/summary/test_summary.py
@@ -265,42 +265,6 @@ def test_summary_data_parametrized(
assert result == expected
-def test_summary_format_with_metrics() -> None:
- comp = _make_comparison()
- rendered = comp.summary(
- metrics={"Mean": metrics.mean},
- ).format(pretty=False)
- # Header is shown when metrics are present; "Mean" appears as a column header.
- assert "Mean" in rendered
- # Numeric column gets a rendered metric value; expected mean delta is 5/3.
- assert "1.667" in rendered
-
-
-def test_summary_metrics_no_joined_rows() -> None:
- # Disjoint primary keys -> num_rows_joined == 0.
- left = pl.DataFrame({"id": [1, 2], "value": [1.0, 2.0]})
- right = pl.DataFrame({"id": [10, 20], "value": [5.0, 6.0]})
- comp = compare_frames(left, right, primary_key="id")
- summary = comp.summary(metrics={"Mean": metrics.mean})
- result = json.loads(summary.to_json())
- # With no joined rows, the columns section is skipped entirely.
- assert result["columns"] is None
-
-
-def test_summary_metrics_no_numeric_columns() -> None:
- # Non-PK common columns are all non-numeric.
- left = pl.DataFrame({"id": [1, 2, 3], "status": ["a", "b", "c"]})
- right = pl.DataFrame({"id": [1, 2, 3], "status": ["a", "b", "x"]})
- comp = compare_frames(left, right, primary_key="id")
- summary = comp.summary(
- show_perfect_column_matches=True,
- metrics={"Mean": metrics.mean},
- )
- result = json.loads(summary.to_json())
- # status appears but has no metric values (non-numeric column).
- assert all(col["metrics"] is None for col in result["columns"])
-
-
@pytest.mark.parametrize(
"input, expected",
[
diff --git a/tests/test_metrics.py b/tests/test_metrics.py
index 4c5e7cc..f5e710b 100644
--- a/tests/test_metrics.py
+++ b/tests/test_metrics.py
@@ -68,3 +68,8 @@ def test_quantile(frame: pl.DataFrame) -> None:
# deltas [0, 0, 2]: p50 = 0, p100 = 2
assert _apply(metrics.quantile(0.5), frame) == 0
assert _apply(metrics.quantile(1.0), frame) == 2
+
+
+def test_quantile_out_of_range() -> None:
+ with pytest.raises(ValueError, match="q must be in"):
+ metrics.quantile(1.5)
From 5eb965cfd21368d3ddf2472f37d66d1735a695e4 Mon Sep 17 00:00:00 2001
From: EgeKaraismailogluQC
<128645043+EgeKaraismailogluQC@users.noreply.github.com>
Date: Wed, 6 May 2026 10:04:57 +0200
Subject: [PATCH 18/19] pre-commit
---
diffly/cli.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/diffly/cli.py b/diffly/cli.py
index e86b264..1e9f153 100644
--- a/diffly/cli.py
+++ b/diffly/cli.py
@@ -141,7 +141,6 @@ def main(
] = [],
) -> None:
"""Compare two `parquet` files and print the comparison result."""
-
for name in metric:
if name not in _PRESETS:
raise typer.BadParameter(
From e8aa08ac6717261245a113bddfb65f7758bdb215 Mon Sep 17 00:00:00 2001
From: EgeKaraismailogluQC
<128645043+EgeKaraismailogluQC@users.noreply.github.com>
Date: Wed, 6 May 2026 10:18:44 +0200
Subject: [PATCH 19/19] pre-commit
---
diffly/metrics.py | 17 +++++++++--------
1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/diffly/metrics.py b/diffly/metrics.py
index cc53a41..ae81dd4 100644
--- a/diffly/metrics.py
+++ b/diffly/metrics.py
@@ -9,14 +9,6 @@
import polars as pl
import polars.selectors as cs
-Metric = Callable[[pl.Expr, pl.Expr], pl.Expr]
-"""A metric is a callable mapping ``(left_expr, right_expr)`` to a scalar aggregation
-expression.
-
-The expressions refer to the left-side and right-side values of a single column across
-all joined rows.
-"""
-
@dataclass(frozen=True)
class _Metric:
@@ -29,6 +21,15 @@ class _Metric:
selector: pl.Expr
+Metric = Callable[[pl.Expr, pl.Expr], pl.Expr]
+"""A metric is a callable mapping ``(left_expr, right_expr)`` to a scalar aggregation
+expression.
+
+The expressions refer to the left-side and right-side values of a single column across
+all joined rows.
+"""
+
+
def _make_numeric_metric(metric: Metric) -> _Metric:
return _Metric(fn=metric, selector=cs.numeric())