compara
Extremely fast table comparison: two tables on a composite key, with counts, per-column statistics and a self-contained HTML report. Key columns, compared columns and normalisation rules are parameters, so one tool serves every recurring comparison: a nightly export against yesterday's, a CSV against the Parquet a warehouse emits, a migration's source against its target.
Faster software for a better planet — less energy, less compute, more time for life.
- Reads CSV, newline-delimited JSON and Parquet, and the two sides need not be in the same format.
- Ten million rows × 20 columns in about a second on four cores (numbers below).
- One static binary; the report is a single HTML file with nothing to host.
- Exit codes made for pipelines:
0identical,1differences,2error,3duplicate keys (with--fail-on-dups).
Install
Prebuilt binaries for Linux (x86_64, aarch64), macOS (arm64, x86_64) and Windows (x64) are on the releases page, and the same binaries through package managers:
| Cargo | cargo install --locked compara |
| npm | npm install -g compara (or npx compara …) |
| pip / uv | pip install compara · uv tool install compara |
| Homebrew | brew install andrey-usa/tap/compara |
| Scoop | scoop bucket add andrey-usa https://github.com/andrey-usa/scoop-bucket then scoop install compara |
Use
compara compare a.csv b.csv -k account_id,txn_id -i updated_at
compara compare a.csv b.csv -k account_id,txn_id -i updated_at -o report.html
compara compare a.csv b.csv -k account_id,txn_id --trim --tolerance 0.005
compara compare export.csv warehouse.parquet -k id
compara compare a.csv b.csv --profile orders --json summary.json --export-dir out/
compara columns data.parquet # column names (reads only the footer)
compara head data.ndjson -n 5 # first rows, aligned
compare prints the counts and writes nothing else unless asked. -o PATH
writes the HTML report: counts, per-column statistics, and the changed,
added, removed and duplicate rows, each section capped at --max-rows
(counts are always exact). examples/report.html is
one, made from the two files next to it:
compara compare examples/orders_2026-08.csv examples/orders_2026-09.csv -k order_id,line_no -i updated_at -o examples/report.html
| Option | Effect |
|---|---|
-k, --key |
composite key, required (or from a profile) |
-c, --compare |
columns to diff; default every column present in both files except the key |
-i, --ignore |
columns to skip (timestamps, run ids) |
--trim, --ignore-case, --empty-is-null |
normalisation before comparing (applies to key and values) |
--tolerance |
absolute numeric tolerance where both sides parse as numbers |
--delimiter, --encoding |
override auto-detection (CSV only) |
--threads |
how wide the engines run; the default is every core |
--memory |
memory to plan for (e.g. 8G); decides when large text files are joined in one pass instead of two, and how many columns of two Parquet files are read at once (a Parquet pair that needs more than a stated budget is refused) |
--max-rows |
rows embedded per report section (default 50 000) |
-o, --out |
write the HTML report here; there is no report without it |
--export-dir |
full, uncapped changed/added/removed CSVs |
--json |
write a JSON summary (counts and column stats; schema in docs/contract.schema.json) |
--summary |
print the counts and refuse any flag that writes a file (the default without -o, --json or --export-dir) |
--engine |
auto (default), turbo, sortmerge or native; sortmerge and native read CSV only |
--no-compress |
plain JSON payload in the report, for pre-2023 browsers (with -o) |
--fail-on-dups |
exit 3 when either file has duplicate keys |
Duplicate keys are counted and listed per file; the first occurrence of each key takes part in the join.
Profiles. A compara.toml in the working directory (or
~/.config/compara/compara.toml, or --config PATH) names recurring
comparisons: key, compared and ignored columns, normalisation, engine. See
compara.example.toml. The command line overrides the
profile.
Speed
Ten million rows × 20 columns, keyed on (account_id, txn_id), --ignore updated_at, one GitHub Actions runner (AMD EPYC 7763, 4 vCPU / 16 GB), best of
five. Each cell is wall time · CPU time · memory above the mapped input.
| Format | Input a side | compara |
|---|---|---|
| CSV | 3,509 MB | 1.20 s · 4.0 s · 591 MB |
| ndjson | 8,487 MB | 2.79 s · 10.4 s · 592 MB |
| Parquet | 2,074 MB | 1.41 s · 4.7 s · 1,342 MB |
Contender benchmarks rerun weekly
(bench-contenders.yml) against the
latest DuckDB and pandas.
Engines
Three backends, one result contract: every engine returns identical counts and
column statistics for the same input, and the test suite asserts it cell for
cell. --engine auto takes the first one that can load the input, in this
order.
| Engine | Implementation | Memory model | Use it for |
|---|---|---|---|
turbo |
mapped file, byte-level index; CSV, JSON and Parquet | off-heap bytes, index in memory | the default, and the fastest |
sortmerge |
external sort, merge join | bounded memory, spills to disk | files past what memory can index |
native |
over the csv crate |
in-memory, row-oriented | the dependency-light baseline |
All three read values as text, with no type inference, so 1.0 and 1 stay
different unless a tolerance is set, and all three treat an empty field as
absent whether or not it is quoted. A Parquet column is the exception, on
either side: it stores typed values, so a column it holds as numbers
compares by value with the other file's text (31.40 against the DOUBLE
31.4 is no change), and a date, time or timestamp column by instant (T or
a space, a zero fraction or none, Z or nothing).
Input formats
turbo decides what a file is by its content rather than its name: a Parquet
magic number, a leading {, or a CSV header.
| Format | How it is read |
|---|---|
| CSV | mapped, scanned eight bytes at a time; a field is an offset and a length; a UTF-8 byte order mark is skipped |
| newline-delimited JSON | the same, addressed by key rather than by column number; \uXXXX is decoded, so an escaped and a literal character compare equal |
| Parquet | pages decoded into an arena; dictionary-encoded columns point at the dictionary entry, so a repeated value costs eight bytes a row |
The Parquet reader is written here rather than taken from a crate, to keep that
representation instead of converting to Arrow arrays. It reads PLAIN,
dictionary (PLAIN_DICTIONARY, RLE_DICTIONARY), RLE booleans and the
version-2 delta encodings; uncompressed, snappy, gzip, zstd and LZ4-raw pages;
data pages v1 and v2; definition levels for optional columns. It refuses, by
name, nested or repeated schemas, BYTE_STREAM_SPLIT, LZO and Brotli.
A typed Parquet value is rendered to text by a fixed rule: byte arrays as their
bytes, booleans true/false, integers and decimals in full, floats in the
shortest round-trip form, dates YYYY-MM-DD, timestamps ISO 8601.
tests/fixtures/formats/typed.csv is that rule written down.
Two Parquet files take a columnar path instead, which never builds a row: it joins on the key columns, then compares one column at a time, string columns by dictionary id or bytes and booleans, integers, floats, dates, times, timestamps and decimals of up to eight bytes as one word per value. A cell is rendered by the rule above only when the report shows it. A column the two files store as different types, or with a codec or encoding outside PLAIN, dictionary, snappy and v1 pages, goes the row-by-row way instead, with the same answer.
Contributing
See CONTRIBUTING.md.
License
Metadata
Release files for compara 0.5.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| compara-0.5.1-py3-none-win_amd64.whl | Python 3 | none | Windows x86-64 | Details |
| compara-0.5.1-py3-none-manylinux_2_28_x86_64.whl | Python 3 | none | Linux glibc 2.28+ x86-64 | Details |
| compara-0.5.1-py3-none-manylinux_2_28_aarch64.whl | Python 3 | none | Linux glibc 2.28+ ARM64 | Details |
| compara-0.5.1-py3-none-macosx_11_0_arm64.whl | Python 3 | none | macOS 11.0+ ARM64 | Details |
| compara-0.5.1-py3-none-macosx_10_12_x86_64.whl | Python 3 | none | macOS 10.12+ x86-64 | Details |
Total release size: 4.8 MB
Release files / compara-0.5.1-py3-none-win_amd64.whl
| Download URL | compara-0.5.1-py3-none-win_amd64.whl |
|---|---|
| Size | 901.2 kB |
| Tags | Python 3 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
b885ab3fb6b56167d4df4541bb090dbd1177fb8f28a3c57a087b16580d28d1c7
|
|
BLAKE2b-256 checksum How to use checksums |
76170327a2762dfeebd2f09c67ccac735568a7b6d5b502632395a86e8e4fcb8c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.
Transparency logRelease files / compara-0.5.1-py3-none-manylinux_2_28_x86_64.whl
| Download URL | compara-0.5.1-py3-none-manylinux_2_28_x86_64.whl |
|---|---|
| Size | 1.0 MB |
| Tags | Linux glibc 2.28+ x86-64 Python 3 |
|
SHA-256 checksum How to use checksums |
1057543b0eb39dd0c61bc516d9b5bdca7ac8e1549fdd4fd73bec77931bbdf83f
|
|
BLAKE2b-256 checksum How to use checksums |
86269b280ac30ce43adb7cf0338db5c784e32b25cab30d4732bddff1191cbd09
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.
Transparency logRelease files / compara-0.5.1-py3-none-manylinux_2_28_aarch64.whl
| Download URL | compara-0.5.1-py3-none-manylinux_2_28_aarch64.whl |
|---|---|
| Size | 919.0 kB |
| Tags | Linux glibc 2.28+ ARM64 Python 3 |
|
SHA-256 checksum How to use checksums |
1361685fe9318c2bfe62ea3f5dd916965728158a675bf5f110169dd567f378c0
|
|
BLAKE2b-256 checksum How to use checksums |
3524e14eee503b3fdcf5f23d5be1d56f69f1ac7a28201f9f60b068b42ed88c49
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.
Transparency logRelease files / compara-0.5.1-py3-none-macosx_11_0_arm64.whl
| Download URL | compara-0.5.1-py3-none-macosx_11_0_arm64.whl |
|---|---|
| Size | 950.7 kB |
| Tags | Python 3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
2f834e8a719a8f704a2f04583f41648668ccb48ca6e887b1329e1a1d3cc6850d
|
|
BLAKE2b-256 checksum How to use checksums |
ec1e19af3603e7a7aac80489c83dcd0d1dd8718deb269fc149e08337e5010f79
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.
Transparency logRelease files / compara-0.5.1-py3-none-macosx_10_12_x86_64.whl
| Download URL | compara-0.5.1-py3-none-macosx_10_12_x86_64.whl |
|---|---|
| Size | 1.0 MB |
| Tags | Python 3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
9177b7535842ab5b709df426f11a6bf81d9e2a463e64bf60942c48737b173296
|
|
BLAKE2b-256 checksum How to use checksums |
de15b0b9349c87ae5adf55725a6b68f89363b2a3086f6a2c5557fc2d83de8e40
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.
Transparency logSecurity for compara 0.5.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 5 files in this release have been uploaded with provenance metadata.
Attestation for compara-0.5.1-py3-none-win_amd64.whl
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.