Install & basics#
Installation#
Install the base package:
pip install pyversus
Install PySpark input support when needed:
pip install "pyversus[spark]"
The base install keeps DuckDB as the only required runtime dependency.
If pandas or polars are already available in your environment,
compare() accepts those inputs too. Spark-backed comparisons also need
PySpark plus a Java runtime.
Inputs#
compare() accepts DuckDB relations (tables or views), pandas
DataFrames, polars DataFrames, and PySpark DataFrames. If you provide
relations created on a non-default DuckDB connection, pass that
connection into compare() via con= so helper queries run in the same
session.
If either input is a PySpark DataFrame, or if you pass spark=, the
comparison runs on Spark and returns Spark-backed outputs while keeping
the same public versus.compare() entry point.
>>> import duckdb
>>> from versus import compare
>>> rel_a = duckdb.sql("SELECT 1 AS id, 10 AS value")
>>> rel_b = duckdb.sql("SELECT 1 AS id, 12 AS value")
>>> comparison = compare(rel_a, rel_b, by=["id"])
>>> comparison.summary()
┌────────────────┬─────────┐
│ difference │ found │
│ varchar │ boolean │
├────────────────┼─────────┤
│ value_diffs │ true │
│ unmatched_cols │ false │
│ unmatched_rows │ false │
│ type_diffs │ false │
└────────────────┴─────────┘
Spark inputs#
When Spark is selected, the same comparison helpers return Spark DataFrames.
>>> from versus import examples
>>> spark = examples.resolve_spark()
>>> left = spark.createDataFrame([(1, 10), (2, 20)], ["id", "value"])
>>> right = spark.createDataFrame([(1, 10), (2, 22), (3, 30)], ["id", "value"])
>>> comparison = compare(left, right, by=["id"])
>>> comparison.summary().show()
+--------------+-----+
| difference|found|
+--------------+-----+
| value_diffs| true|
|unmatched_cols|false|
|unmatched_rows| true|
| type_diffs|false|
+--------------+-----+
>>> comparison.value_diffs("value").show()
+-------+-------+---+
|value_a|value_b| id|
+-------+-------+---+
| 20| 22| 2|
+-------+-------+---+
Materialization modes#
When you call compare(), pyversus defines summary tables for the
printed output (tables, by, intersection, unmatched_cols,
unmatched_rows).
For DuckDB-backed comparisons, these are relation-like wrappers over DuckDB relations. For Spark-backed comparisons, they are Spark DataFrame wrappers that cache themselves on first materialization. The input tables are never materialized by pyversus in Python in any mode.
In full materialization, pyversus also builds a diff table: a single
backend-native relation/DataFrame with the by keys plus one boolean
flag per value column indicating a difference. The table only includes
rows with at least one difference. Those precomputed flags let row-level
helpers fetch the differing rows quickly. Other modes skip the diff
table and detect differences inline.
materialize="all": store the summary tables and the diff table up front. This is fastest if you will call row-level helpers multiple times.materialize="summary": store only the summary tables. Row-level helpers run inline predicates and return lazy backend-native objects.materialize="none": do not store anything up front. Printing the comparison materializes the summary tables on demand.
Row-level helper outputs are always returned as backend-native objects: DuckDB relations for DuckDB runs and Spark DataFrames for Spark runs.