Report API

DataComPy’s datacompy.report module provides a typed data model where every backend produces a ReportData instance via compare.build_report_data(). Rendering methods (render(), to_html(), save(), to_dict()) live directly on ReportData, making it easy to build custom dashboards, export to JSON, or plug in alternative templates.

Programmatic Access

Call compare.build_report_data() on any compare object to get a ReportData instance without triggering any rendering:

import pandas as pd
from datacompy import PandasCompare

df1 = pd.DataFrame({"id": [1, 2, 3], "val": [10, 20, 30]})
df2 = pd.DataFrame({"id": [1, 2, 3], "val": [10, 99, 30]})

compare = PandasCompare(df1, df2, join_columns="id")
data = compare.build_report_data()

# Inspect structured fields directly
print(data.row_summary.unequal_rows)          # 1
print(data.mismatch_stats.stats[0].column)   # 'val'
print(data.column_summary.common_columns)     # 2

The same method is available on all backends (PolarsCompare, SparkSQLCompare, SnowflakeCompare).

Rendering and Export

Rendering methods live on ReportData directly:

data = compare.build_report_data()

# Plain-text report (same output as compare.report())
print(data.render())

# Save as HTML file
data.save("comparison.html")

# JSON-serializable dict (for dashboards / APIs)
import json
payload = json.dumps(data.to_dict())

Custom Templates

Pass a template_path to render() or save() to use your own Jinja2 template:

print(data.render(template_path="my_report.j2"))
data.save("report.html", template_path="my_report.j2")

The template receives the same context as the default report_template.j2; see Template Customization Guide for the full variable reference.

Data Classes

Typed data model for DataComPy report generation.

All backends produce a ReportData instance via compare.build_report_data(). ReportData owns rendering to text and HTML, and exposes to_dict() for programmatic consumers.

Examples

Programmatic access without rendering:

>>> data = compare.build_report_data()
>>> print(data.row_summary.unequal_rows)
>>> print(data.mismatch_stats.stats[0].column)

Render to text and save HTML:

>>> data = compare.build_report_data()
>>> print(data.render())
>>> data.save("comparison.html")

Export as JSON-serializable dict:

>>> import json
>>> json.dumps(data.to_dict())
class datacompy.report.ColumnComparison(unequal_columns: int, equal_columns: int, unequal_values: int)

Bases: object

Aggregate column comparison counts.

Variables:
  • unequal_columns (int) – Number of compared columns with at least one unequal value.

  • equal_columns (int) – Number of compared columns where all values matched.

  • unequal_values (int) – Total number of individual cell values that did not match.

equal_columns: int
unequal_columns: int
unequal_values: int
class datacompy.report.ColumnSummary(common_columns: int, df1_unique: int, df1_unique_columns: Tuple[str, ...], df2_unique: int, df2_unique_columns: Tuple[str, ...], df1_name: str, df2_name: str)

Bases: object

Summary of column overlap between the two DataFrames.

Variables:
  • common_columns (int) – Number of columns present in both DataFrames.

  • df1_unique (int) – Number of columns only in df1.

  • df1_unique_columns (tuple of str) – Names of columns only in df1.

  • df2_unique (int) – Number of columns only in df2.

  • df2_unique_columns (tuple of str) – Names of columns only in df2.

  • df1_name (str) – Label for the first DataFrame.

  • df2_name (str) – Label for the second DataFrame.

common_columns: int
df1_name: str
df1_unique: int
df1_unique_columns: Tuple[str, ...]
df2_name: str
df2_unique: int
df2_unique_columns: Tuple[str, ...]
class datacompy.report.MismatchStat(column: str, dtype1: str, dtype2: str, unequal_cnt: int, max_diff: float, null_diff: int, rel_tol: float, abs_tol: float)

Bases: object

Per-column mismatch statistics.

Variables:
  • column (str) – Column name.

  • dtype1 (str) – Data type in df1.

  • dtype2 (str) – Data type in df2.

  • unequal_cnt (int) – Number of rows where the values differ.

  • max_diff (float) – Maximum absolute numeric difference (0.0 for non-numeric columns).

  • null_diff (int) – Number of rows where one value is null and the other is not.

  • rel_tol (float) – Relative tolerance applied to this column.

  • abs_tol (float) – Absolute tolerance applied to this column.

abs_tol: float
column: str
dtype1: str
dtype2: str
max_diff: float
null_diff: int
rel_tol: float
unequal_cnt: int
class datacompy.report.MismatchStats(has_mismatches: bool, has_samples: bool, stats: Tuple[MismatchStat, ...] = (), samples: Tuple[str, ...] = (), df1_name: str = '', df2_name: str = '')

Bases: object

Mismatch statistics across all compared columns.

Variables:
  • has_mismatches (bool) – True when at least one column has unequal values or types.

  • has_samples (bool) – True when sample rows are available for display.

  • stats (tuple of MismatchStat) – Per-column statistics, sorted by column name.

  • samples (tuple of str) – Pre-rendered string tables of sample mismatched rows (one per column).

  • df1_name (str) – Label for the first DataFrame.

  • df2_name (str) – Label for the second DataFrame.

df1_name: str = ''
df2_name: str = ''
has_mismatches: bool
has_samples: bool
samples: Tuple[str, ...] = ()
stats: Tuple[MismatchStat, ...] = ()
class datacompy.report.ReportData(df1_name: str, df2_name: str, df1_shape: Tuple[int, int], df2_shape: Tuple[int, int], column_count: int, column_summary: ColumnSummary, row_summary: RowSummary, column_comparison: ColumnComparison, mismatch_stats: MismatchStats, df1_unique_rows: UniqueRowsData, df2_unique_rows: UniqueRowsData)

Bases: object

Complete data model for a DataComPy comparison report.

Produced by compare.build_report_data(). All fields are immutable so the object can be safely cached or passed across threads.

Variables:
  • df1_name (str) – Label for the first DataFrame.

  • df2_name (str) – Label for the second DataFrame.

  • df1_shape (tuple of (int, int)) – (rows, columns) of df1.

  • df2_shape (tuple of (int, int)) – (rows, columns) of df2.

  • column_count (int) – Maximum number of columns displayed in unique-row samples.

  • column_summary (ColumnSummary)

  • row_summary (RowSummary)

  • column_comparison (ColumnComparison)

  • mismatch_stats (MismatchStats)

  • df1_unique_rows (UniqueRowsData)

  • df2_unique_rows (UniqueRowsData)

column_comparison: ColumnComparison
column_count: int
column_summary: ColumnSummary
df1_name: str
df1_shape: Tuple[int, int]
df1_unique_rows: UniqueRowsData
df2_name: str
df2_shape: Tuple[int, int]
df2_unique_rows: UniqueRowsData
mismatch_stats: MismatchStats
render(template_path: str | None = None) str

Render the report to a text string.

Parameters:

template_path (str, optional) – Path to a custom Jinja2 template. When None the default report_template.j2 is used.

Returns:

Formatted report text.

Return type:

str

row_summary: RowSummary
save(path: str | Path, template_path: str | None = None) None

Save the report as an HTML file.

Parameters:
  • path (str or Path) – Destination file path. Parent directories are created if needed.

  • template_path (str, optional) – Path to a custom Jinja2 template. When None the default report_template.j2 is used.

to_dict() Dict[str, Any]

Return the report data as a JSON-serializable dict.

Returns:

dataclasses.asdict(self) — safe for json.dumps().

Return type:

dict

to_html(template_path: str | None = None) str

Return the report wrapped in a minimal HTML page.

Parameters:

template_path (str, optional) – Path to a custom Jinja2 template. When None the default report_template.j2 is used.

Returns:

HTML string with the text report inside a <pre> block.

Return type:

str

class datacompy.report.RowSummary(match_columns: Tuple[str, ...], on_index: bool, has_duplicates: bool, abs_tol: Any, rel_tol: Any, common_rows: int, df1_unique: int, df2_unique: int, unequal_rows: int, equal_rows: int, df1_name: str, df2_name: str)

Bases: object

Summary of row overlap and match statistics.

Variables:
  • match_columns (tuple of str) – Column(s) used to join/match rows. Empty tuple when matching on index.

  • on_index (bool) – True when the comparison was joined on DataFrame index rather than columns.

  • has_duplicates (bool) – Whether duplicate join-key values were found.

  • abs_tol (float or dict) – Absolute tolerance used for numeric comparisons.

  • rel_tol (float or dict) – Relative tolerance used for numeric comparisons.

  • common_rows (int) – Number of rows present in both DataFrames after joining.

  • df1_unique (int) – Number of rows only in df1.

  • df2_unique (int) – Number of rows only in df2.

  • unequal_rows (int) – Number of common rows where at least one compared column differs.

  • equal_rows (int) – Number of common rows where all compared columns match.

  • df1_name (str) – Label for the first DataFrame.

  • df2_name (str) – Label for the second DataFrame.

abs_tol: Any
common_rows: int
df1_name: str
df1_unique: int
df2_name: str
df2_unique: int
equal_rows: int
has_duplicates: bool
match_columns: Tuple[str, ...]
on_index: bool
rel_tol: Any
unequal_rows: int
class datacompy.report.UniqueRowsData(has_rows: bool, rows: str = '')

Bases: object

Sample rows that exist in only one of the two DataFrames.

Variables:
  • has_rows (bool) – True when there is at least one unique row to display.

  • rows (str) – Pre-rendered string table of sample rows.

has_rows: bool
rows: str = ''