daff review
daff 1.4.2 compares related tables by rows and columns, then emits the differences in a tabular format that can also be applied as a patch. Its command line handles CSV, TSV, newline-delimited JSON, and SQLite, can render HTML, and can perform a three-way merge from a common parent. It works best on revisions of the same dataset, where keys or row order give the aligner something dependable. The code originates in Haxe and is translated into Python and several other languages. Version 1.4.2 only trims material from the npm package, so the published Python behavior did not gain a new feature in that release. Our Python 3.12 install added one pure-Python package with no declared dependencies.
daff 1.4.2 installed in 0.2 seconds as 1 package using 1 MB, imported in 0.30 seconds, and had 0 audit findings in our sandbox. Install it for keyed revisions of CSV-like tables that need a readable, applicable patch; choose a dataframe comparator when types and numeric tolerance carry the decision.
We installed it
| Install | ✓ · 0.2s | 1 package on disk · 1 MB |
| Import | ✓ | import daff in 0.30s · pure Python |
| Known vulns | 0 | (pip-audit) |
Answers from our run
Does daff install cleanly?
Yes. In a fresh container with an empty cache, pip install daff finished in 0.2s, leaving 1 package and 1 MB on disk. pip-audit reported no known vulnerabilities.
What does daff need to run?
Python 3.x, and nothing compiled: it is pure Python. In our run import daff succeeded in 0.30s.
daff or csv-diff: which should you use?
csv-diff: Choose it for a smaller key-based CSV or JSON report when patching and three-way merge are unnecessary. daff 1.4.2 installed in 0.2 seconds as 1 package using 1 MB, imported in 0.30 seconds, and had 0 audit findings in our sandbox.
When should you not use daff?
The datasets came from unrelated systems and need entity resolution. daff is optimized for versions of the same table, not probabilistic matching between independent exports.
Use it if
- CSV reviews need cell-level inserts, deletes, moves, and updates instead of a line diff that loses column alignment.
- A diff must double as an input that another job can apply to a copy of the original table.
- Two edited tables share a common ancestor and need a three-way merge rather than last-writer-wins replacement.
- The same tabular diff format must be rendered in HTML, used from a shell, or consumed by another supported language build.
- The datasets came from unrelated systems and need entity resolution. daff is optimized for versions of the same table, not probabilistic matching between independent exports.
- Parquet, Arrow, Excel formatting, dataframe dtypes, or warehouse tables must survive comparison. The CLI documents CSV, TSV, ndjson, and SQLite, so conversion discards some source metadata.
- Floating-point tolerance or statistical equivalence decides whether values match. daff has case and whitespace controls but documents no numeric tolerance model.
- You need a polished Python dataframe API with type information. The project is generated from Haxe, PyPI marks it Alpha, and the main library walkthrough uses JavaScript objects.
- Rows have neither a stable key nor meaningful order. Heuristic alignment can make a readable report, but it is a weak basis for an unattended patch.
Setup reality
We installed daff 1.4.2 in a fresh Python 3.12 Bookworm sandbox. Installation completed in 0.2 seconds, left 1 package, and used 1 MB on disk. The wheel is pure Python with 0 direct dependencies. import daff worked in 0.30 seconds, and pip-audit found 0 known vulnerabilities. The distribution does not declare a Python version floor and does not ship py.typed, so static checkers cannot assume the generated module is typed.
No account, daemon, or configuration file is required. The first real choice is row identity. CSV input is ordered by default, while JSON-style input is unordered. Add one or more --id columns when the table has a stable primary key, and use --unordered only when position carries no meaning. Without a key, daff aligns similar rows heuristically; inspect that alignment before treating its output as an automated data change.
The highlighter table produced by diff is also the patch format. Keep marker columns intact and send patch output to a new file first. patch --inplace and merge --inplace modify the target, which is a poor default for CI or one-off recovery work. --fail-if-diff returns 0 for equality, 1 for a detected difference, and 2 for a processing error. Disable terminal color when logs or another program will consume stdout.
SQLite comparison needs a table name when it cannot be inferred; renamed tables use the documented old:new form. The CLI does not read Parquet or preserve Excel formulas and formatting. Direct Python use exposes translated classes rather than a Python-first dataframe contract, and the package has no py.typed marker. Pin 1.4.2 and favor the command boundary unless you are prepared to inspect generated signatures during upgrades.
Patterns
Compare two ordered CSV revisions compare-csv
daff before.csv after.csvCSV rows are ordered by default, so a move can appear as a structural change even when values stay the same.
Align records by an ID column set-primary-key
daff diff --id customer_id before.csv after.csvRepeat `--id` for a composite key; stable keys make alignment safer than similarity matching.
Compare keyed rows without position ignore-row-order
daff diff --unordered --id id before.csv after.csvUse unordered mode only when row position has no meaning, and pair it with a key when duplicates are possible.
Write an applicable tabular diff save-patch
daff --output changes.csv before.csv after.csvThe output is both a human-readable highlighter table and patch input, so its marker columns must remain unchanged.
Apply changes into a separate file apply-patch
daff patch before.csv changes.csv > after.csvWriting a new file avoids the destructive behavior of `--inplace` and leaves the original available for review.
Merge two edits from one parent merge-tables
daff merge parent.csv ours.csv theirs.csv > merged.csvThe first input must be the common ancestor; unrelated tables do not provide the three-way comparison daff expects.
Distinguish changes from processing errors gate-ci
daff diff --fail-if-diff --no-color expected.csv actual.csvExit status 0 means equal, 1 means different, and 2 means daff could not process the comparison.
Exclude volatile columns ignore-columns
daff diff --ignore updated_at --ignore exported_at before.csv after.csvIgnored fields disappear from the decision, so reserve this for values that the review and patch truly do not need.
Report only cell updates filter-updates
daff diff --act update before.csv after.csvThe filtered report omits inserts and deletes and therefore should not be saved as a complete migration patch.
Turn a saved diff into HTML render-html
daff render --output changes.html changes.csvAdd `--fragment` for embedding in an existing page or `--css` to write the matching stylesheet.
Diff a named SQLite table compare-sqlite
daff diff --input-format sqlite --table customers old.db new.dbA renamed table uses `--table old_name:new_name`; daff compares table contents rather than the full database schema.
Register CSV diff and merge drivers configure-git
daff git csvThis writes Git configuration, so inspect `daff git` instructions first when repository-wide driver policy is shared.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| csv-diff | PyPI | Choose it for a smaller key-based CSV or JSON report when patching and three-way merge are unnecessary. |
| datacompy | PyPI | Choose it for pandas or Spark comparisons that need join columns, numeric tolerances, and dataframe-oriented reports. |
| deepdiff | PyPI | Choose it when the objects are nested Python or JSON structures rather than rectangular tables. |
More data guides
numpy · fsspec · pandas · sqlalchemy · pyarrow · lxml · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

