RDataCompy
Lightning-fast dataframe comparison library, implemented in Rust with Python bindings.
Overview
RDataCompy is a high-performance library for comparing dataframes, inspired by Capital One's datacompy. Built in Rust and leveraging Apache Arrow, it provides 40+ million cells/second throughput with comprehensive comparison reports.
Why RDataCompy?
- 🚀 Blazing Fast: 40-46M cells/second (100-1000x faster than Python-based solutions)
- 💾 Memory Efficient: Zero-copy operations using Apache Arrow columnar format
- 🎯 Flexible: Configurable tolerance for numeric comparisons
- 📊 Comprehensive: Detailed reports showing exact differences
- 🔧 Multi-Format: Works with PyArrow, Pandas, PySpark, and Polars DataFrames
- 💰 Decimal Support: Full DECIMAL(p,s) support with cross-precision compatibility
Installation
pip install rdatacompy
Optional Dependencies
# For PySpark support
pip install rdatacompy[spark]
# For Pandas support
pip install rdatacompy[pandas]
# For Polars support
pip install rdatacompy[polars]
# Install everything
pip install rdatacompy[all]
Quick Start
Basic Usage (PyArrow)
import pyarrow as pa
from rdatacompy import Compare
# Create sample tables
df1 = pa.table({
'id': [1, 2, 3, 4],
'value': [10.0, 20.0, 30.0, 40.0],
'name': ['Alice', 'Bob', 'Charlie', 'David']
})
df2 = pa.table({
'id': [1, 2, 3, 5],
'value': [10.001, 20.0, 30.5, 50.0],
'name': ['Alice', 'Bob', 'Chuck', 'Eve']
})
# Compare dataframes
comp = Compare(
df1,
df2,
join_columns=['id'],
abs_tol=0.01, # Absolute tolerance for floats
df1_name='original',
df2_name='updated'
)
# Print comprehensive report
print(comp.report())
Example Output:
DataComPy Comparison
--------------------
DataFrame Summary
-----------------
DataFrame Columns Rows
original 3 4
updated 3 4
Column Summary
--------------
Number of columns in common: 3
Number of columns in original but not in updated: 0
Number of columns in updated but not in original: 0
Row Summary
-----------
Matched on: id
Any duplicates on match values: No
Absolute Tolerance: 0.01
Relative Tolerance: 0
Number of rows in common: 3
Number of rows in original but not in updated: 1
Number of rows in updated but not in original: 1
Number of rows with some compared columns unequal: 1
Number of rows with all compared columns equal: 2
Column Comparison
-----------------
Number of columns compared with some values unequal: 2
Number of columns compared with all values equal: 0
Total number of values which compare unequal: 2
Columns with Unequal Values or Types
------------------------------------
Column original dtype updated dtype # Unequal Max Diff # Null Diff
value float64 float64 1 0.5000 0
name string string 1 N/A 0
Sample Rows with Unequal Values for 'value'
--------------------------------------------------
id value (original) value (updated)
3 30.000000 30.500000
Sample Rows with Unequal Values for 'name'
--------------------------------------------------
id name (original) name (updated)
3 Charlie Chuck
Understanding the Report:
- DataFrame Summary: Shows the dimensions of both dataframes being compared
- Column Summary: Lists columns that exist in both, and columns unique to each dataframe
- Row Summary:
- Shows which columns were used for matching (join keys)
- Number of rows that exist in both dataframes (3 in this example)
- Rows unique to each dataframe (id=4 only in original, id=5 only in updated)
- How many matched rows have differences (1 row has differences, 2 are identical)
- Column Comparison: Summary of which columns have differences across all matched rows
- Columns with Unequal Values: Detailed breakdown per column showing:
- Data types in each dataframe
- Number of unequal values
- Max difference (for numeric columns)
- Number of null mismatches
- Sample Rows: Shows actual examples of differences with join key values displayed first (not row index), making it easy to identify exactly which records differ
Using with Pandas
import pandas as pd
from rdatacompy import Compare
df1 = pd.DataFrame({
'id': [1, 2, 3],
'amount': [100.50, 200.75, 300.25]
})
df2 = pd.DataFrame({
'id': [1, 2, 3],
'amount': [100.51, 200.75, 300.24]
})
# Directly compare Pandas DataFrames (auto-converted to Arrow)
comp = Compare(df1, df2, join_columns=['id'], abs_tol=0.01)
print(comp.report())
Using with PySpark
from pyspark.sql import SparkSession
from rdatacompy import Compare
# For Spark 3.5, enable Arrow for better performance
spark = SparkSession.builder \
.config("spark.sql.execution.arrow.pyspark.enabled", "true") \
.getOrCreate()
df1 = spark.createDataFrame([(1, 100), (2, 200)], ['id', 'value'])
df2 = spark.createDataFrame([(1, 100), (2, 201)], ['id', 'value'])
# Directly compare Spark DataFrames (auto-converted to Arrow)
# Works with Spark <4.0 (via toPandas) and 4.0+ (via toArrow)
comp = Compare(df1, df2, join_columns=['id'])
print(comp.report())
Decimal Support
from decimal import Decimal
import pyarrow as pa
from rdatacompy import Compare
# Compare DECIMAL columns with different precision/scale
df1 = pa.table({
'id': [1, 2, 3],
'price': pa.array([
Decimal('123.456789012345'),
Decimal('999.999999999999'),
Decimal('42.123456789012')
], type=pa.decimal128(28, 12)) # High precision
})
df2 = pa.table({
'id': [1, 2, 3],
'price': pa.array([
Decimal('123.456789'),
Decimal('999.999998'),
Decimal('42.123457')
], type=pa.decimal128(18, 6)) # Lower precision
})
# Compare with tolerance - handles different precision automatically
comp = Compare(df1, df2, join_columns=['id'], abs_tol=0.00001)
print(comp.report())
Features
Comparison Report
The report includes:
- DataFrame Summary: Row and column counts
- Column Summary: Common columns, unique to each dataframe
- Row Summary: Matched rows, unique rows, duplicates
- Column Comparison: Which columns have differences
- Sample Differences: Example rows with unequal values
- Statistics: Number of differences, max difference, null differences
API Methods
comp = Compare(df1, df2, join_columns=['id'])
# Get full comparison report
report = comp.report()
# Check if dataframes match
matches = comp.matches() # Returns bool
# Get common columns
common_cols = comp.intersect_columns()
# Get columns unique to each dataframe
df1_only = comp.df1_unq_columns()
df2_only = comp.df2_unq_columns()
Supported Data Types
- ✅ Integers: int8, int16, int32, int64, uint8, uint16, uint32, uint64
- ✅ Floats: float32, float64
- ✅ Decimals: decimal128, decimal256 (with cross-precision compatibility)
- ✅ Strings: utf8, large_utf8
- ✅ Booleans
- ✅ Dates: date32, date64
- ✅ Timestamps (with timezone support)
Cross-Type Compatibility
RDataCompy is designed for real-world data migration scenarios:
# Compare different numeric types (int vs float vs decimal)
df1 = pa.table({'id': [1], 'val': pa.array([100], type=pa.int64())})
df2 = pa.table({'id': [1], 'val': pa.array([100.0], type=pa.float64())})
comp = Compare(df1, df2, join_columns=['id'])
comp.matches() # True - types are compatible!
# Compare different decimal precisions
df1 = pa.table({'val': pa.array([Decimal('123.45')], type=pa.decimal128(28, 12))})
df2 = pa.table({'val': pa.array([Decimal('123.45')], type=pa.decimal128(18, 6))})
# Compares successfully - precision difference handled automatically
Performance
Benchmarked on a dataset with 150,000 rows × 200 columns (58.8M data points):
- Comparison time: 1.3 seconds
- Throughput: 46 million cells/second
- Memory overhead: 16 MB (only stores differences)
vs datacompy
RDataCompy is significantly faster than Python-based solutions:
- Columnar processing: Uses SIMD-optimized Arrow compute kernels
- Zero-copy: Works directly on Arrow arrays without data duplication
- Hash-based joins: O(n) row matching vs O(n²) pandas merge
- No type inference: Arrow types known upfront (no runtime checks per column)
Development
Prerequisites
- Rust 1.70+
- Python 3.8+
- maturin
Building from Source
# Clone repository
git clone https://github.com/yourusername/rdatacompy
cd rdatacompy
# Create virtual environment
python -m venv .venv
source .venv/bin/activate
# Install maturin
pip install maturin
# Build and install in development mode
maturin develop --release
# Run examples
python examples/basic_usage.py
Running Tests
# Rust tests
cargo test
# Python examples
python examples/test_multi_dataframe_types.py
python examples/test_decimal_types.py
python examples/benchmark_large.py
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
Apache-2.0
Acknowledgments
- Inspired by Capital One's datacompy
- Built with Apache Arrow and PyO3
Roadmap
See TODO.md for planned features and improvements.
Metadata
Release files for rdatacompy 1.1.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distributions (wheels)
Total release size: 13.5 MB
Release files / rdatacompy-1.1.3-cp314-cp314-win_amd64.whl
| Download URL | rdatacompy-1.1.3-cp314-cp314-win_amd64.whl |
|---|---|
| Size | 921.3 kB |
| Tags | CPython 3.14 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
8bbeeb15caed187b639b8103abd9e60958ce2db476de621c34193a3f1f59e793
|
|
BLAKE2b-256 checksum How to use checksums |
73144e0b95a561310396b7508b2267065400619451325781aa7e8457a866038f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|
Release files / rdatacompy-1.1.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | rdatacompy-1.1.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 1.0 MB |
| Tags | CPython 3.14 Linux glibc 2.17+ x86-64 |
|
SHA-256 checksum How to use checksums |
4febff7a5e615d18d4c608a3dd5d1dcceeb69907799fff47b51dcdf297d342b9
|
|
BLAKE2b-256 checksum How to use checksums |
9946f5961a909cc430d67eb616a4a03590ba571b7b2b76f71970be6d837da940
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|
Release files / rdatacompy-1.1.3-cp313-cp313-win_amd64.whl
| Download URL | rdatacompy-1.1.3-cp313-cp313-win_amd64.whl |
|---|---|
| Size | 921.3 kB |
| Tags | CPython 3.13 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
f33ecf75929febbfdfc80c592857a9490db908725e8b850aefce3d89a0c226ac
|
|
BLAKE2b-256 checksum How to use checksums |
f4fc94768eeb33217bfca456b490856675417fa05157e5e28b84d817c21a040a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|
Release files / rdatacompy-1.1.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | rdatacompy-1.1.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 1.0 MB |
| Tags | CPython 3.13 Linux glibc 2.17+ x86-64 |
|
SHA-256 checksum How to use checksums |
36a1b00d20dbbc3f2abbe279470d8c262e12b5ac2e2c4fafdc340cfb92af00f7
|
|
BLAKE2b-256 checksum How to use checksums |
37e03aa67a84dbd42877e0ce5a44ef2f03675979820295b20f23f9439082f626
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|
Release files / rdatacompy-1.1.3-cp312-cp312-win_amd64.whl
| Download URL | rdatacompy-1.1.3-cp312-cp312-win_amd64.whl |
|---|---|
| Size | 921.4 kB |
| Tags | CPython 3.12 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
cc0cf6b3cc0728dd9070a4693dccb73aea31e91782e3a83d3039c1e31a884287
|
|
BLAKE2b-256 checksum How to use checksums |
856aa5498dd5d2333b45150f7436ce90a7c3c1e40d1a41600fa5f94dcfa244be
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|
Release files / rdatacompy-1.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | rdatacompy-1.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 1.0 MB |
| Tags | CPython 3.12 Linux glibc 2.17+ x86-64 |
|
SHA-256 checksum How to use checksums |
4a1e86578392512ec758043ff7aa83aa62d64104ae3da7d13435c0dd2b6d95a4
|
|
BLAKE2b-256 checksum How to use checksums |
3366e6fbc0ec1354f21576197d57ecb31bf8f2f80e648de34eeae3405e1bcc25
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|
Release files / rdatacompy-1.1.3-cp311-cp311-win_amd64.whl
| Download URL | rdatacompy-1.1.3-cp311-cp311-win_amd64.whl |
|---|---|
| Size | 920.4 kB |
| Tags | CPython 3.11 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
1fb06ae55c1b2d2d11f4b01f8e134cb5391efcfa01bd9c2d271e3b3805561f9a
|
|
BLAKE2b-256 checksum How to use checksums |
306d04fe9efaa11a54604c7e27040063abbf65914742453e04378710e1784ab5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|
Release files / rdatacompy-1.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | rdatacompy-1.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 999.9 kB |
| Tags | CPython 3.11 Linux glibc 2.17+ x86-64 |
|
SHA-256 checksum How to use checksums |
a98f5d603b59e044ad851032ba7c2689aeb97889e7ab740f2d94bb39f9cd8994
|
|
BLAKE2b-256 checksum How to use checksums |
4c7ae34712c5a779e7ab697897308f761f9880760722a2e19803be92a118b39c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|
Release files / rdatacompy-1.1.3-cp310-cp310-win_amd64.whl
| Download URL | rdatacompy-1.1.3-cp310-cp310-win_amd64.whl |
|---|---|
| Size | 922.4 kB |
| Tags | CPython 3.10 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
cccc2fe75af47796ef64c550d6466173988ef67791b7cd98d1cbe0ea50a50b6c
|
|
BLAKE2b-256 checksum How to use checksums |
f7c9992681e7dc759966839743e2e3c1d31c917e0d1a02ee27468099b67f1ff9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|
Release files / rdatacompy-1.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | rdatacompy-1.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 1.0 MB |
| Tags | CPython 3.10 Linux glibc 2.17+ x86-64 |
|
SHA-256 checksum How to use checksums |
5ebaa0fddaa94433e0eee928df630e46111b7aa9cec009d11069ae50b2bab640
|
|
BLAKE2b-256 checksum How to use checksums |
815de4c2757cd40a7270922233982e32c03b79e92e79e0ade044b2d40cfcb46e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|
Release files / rdatacompy-1.1.3-cp39-cp39-win_amd64.whl
| Download URL | rdatacompy-1.1.3-cp39-cp39-win_amd64.whl |
|---|---|
| Size | 923.4 kB |
| Tags | CPython 3.9 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
44c6e3f99fb3256ce54361012d49710833aaf765c8b8fd0910b9e9704d5df944
|
|
BLAKE2b-256 checksum How to use checksums |
45f0aaa79cd363a9b1979d8bd9165c6ba52048483fbe9f31eaed5e64e2f1adab
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|
Release files / rdatacompy-1.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | rdatacompy-1.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 1.0 MB |
| Tags | CPython 3.9 Linux glibc 2.17+ x86-64 |
|
SHA-256 checksum How to use checksums |
235ee43fdaeb588a69e887236045b7999eeae34d4d512b57dc0960850bd211b3
|
|
BLAKE2b-256 checksum How to use checksums |
e6653adbe78da87ffb42426f4b0be9f3a31f60890640ea3477d3db4c03aca5c8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|
Release files / rdatacompy-1.1.3-cp38-cp38-win_amd64.whl
| Download URL | rdatacompy-1.1.3-cp38-cp38-win_amd64.whl |
|---|---|
| Size | 922.6 kB |
| Tags | CPython 3.8 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
4b87437ec64497e79f7f78873fb371426244eea3f39ec49adc8ac447d17e7230
|
|
BLAKE2b-256 checksum How to use checksums |
d6fe8b9466713be402313d5878d72348890221b13cb2710f1ebb7e81922d323f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|
Release files / rdatacompy-1.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | rdatacompy-1.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 1.0 MB |
| Tags | CPython 3.8 Linux glibc 2.17+ x86-64 |
|
SHA-256 checksum How to use checksums |
ecdf60c3cc1098d676805cbd7f22f91b2474139e4b11678b0bad6cea8d065a44
|
|
BLAKE2b-256 checksum How to use checksums |
13ee9ab294d0a9bc8e9cfef3ecb04a1b9717dc65e8330cb6090b5de9427162b6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|
Security for rdatacompy 1.1.3
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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A 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.
To learn more about protecting yourself from malicious actors, visit our package security documentation.