Skip to main content

Lightning-fast dataframe comparison library built in Rust with Python bindings

Project description

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 3.5+ (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

Roadmap

See TODO.md for planned features and improvements.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

rdatacompy-0.1.11-cp312-cp312-win_amd64.whl (862.2 kB view details)

Uploaded CPython 3.12Windows x86-64

rdatacompy-0.1.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (971.1 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64

rdatacompy-0.1.11-cp311-cp311-win_amd64.whl (860.8 kB view details)

Uploaded CPython 3.11Windows x86-64

rdatacompy-0.1.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (969.2 kB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ x86-64

rdatacompy-0.1.11-cp310-cp310-win_amd64.whl (861.0 kB view details)

Uploaded CPython 3.10Windows x86-64

rdatacompy-0.1.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (969.4 kB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ x86-64

rdatacompy-0.1.11-cp39-cp39-win_amd64.whl (861.3 kB view details)

Uploaded CPython 3.9Windows x86-64

rdatacompy-0.1.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (969.9 kB view details)

Uploaded CPython 3.9manylinux: glibc 2.17+ x86-64

rdatacompy-0.1.11-cp38-cp38-win_amd64.whl (861.1 kB view details)

Uploaded CPython 3.8Windows x86-64

rdatacompy-0.1.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (969.9 kB view details)

Uploaded CPython 3.8manylinux: glibc 2.17+ x86-64

File details

Details for the file rdatacompy-0.1.11-cp312-cp312-win_amd64.whl.

File metadata

File hashes

Hashes for rdatacompy-0.1.11-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 1d2ca2b00af8a514a7f5d5150d0e9fb9929352bb60ee73ceba99524888ae7ff0
MD5 df758135cd83d8c2f20765f56242c242
BLAKE2b-256 ec2872e41f9cc47811522fd4d1b6a1897c873616ffb77001ba3453c672ee8696

See more details on using hashes here.

File details

Details for the file rdatacompy-0.1.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for rdatacompy-0.1.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 5d387566b8d2877b778e414eaf83c6c7d90e7cc52af33da5506cc27ff7df0f21
MD5 9f8f54d999a84528d99f83cad47b8866
BLAKE2b-256 289153a380b9a3b4b4b34083635a53b3f65f9a4040a11126c29088f71920c8a1

See more details on using hashes here.

File details

Details for the file rdatacompy-0.1.11-cp311-cp311-win_amd64.whl.

File metadata

File hashes

Hashes for rdatacompy-0.1.11-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 628f9b943388762a42c80dd8271dddb3f991ef2c21d2b642050fc1c2d709f272
MD5 6ce9f1ef61f46a61e3bebe8bc58f35b2
BLAKE2b-256 2df58b63e0c2aa7addadf51c81ebd0f7bfb1c198c153a13f12425ae59a2c1839

See more details on using hashes here.

File details

Details for the file rdatacompy-0.1.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for rdatacompy-0.1.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 fcae57508c8b21e0ab234cd3a5357a2a2ebb95712a44702899afafa90081e38f
MD5 42dbeeb8bfd476a40bdf6e7600c9fe48
BLAKE2b-256 2a1ff627f70fb7d5cc4af3784adfe2d89eef4ea14b175206d9a20864b0262ec9

See more details on using hashes here.

File details

Details for the file rdatacompy-0.1.11-cp310-cp310-win_amd64.whl.

File metadata

File hashes

Hashes for rdatacompy-0.1.11-cp310-cp310-win_amd64.whl
Algorithm Hash digest
SHA256 962e91d66de250f7addb971fd748a58ca52b190bc6cd60031e00db4f78c66548
MD5 887eec9234e5bbf12c42bb8405bd62dd
BLAKE2b-256 d9599eecf80a19606c2ef6373e7fd728a48b2eb838d37d1f73a2a428a131917e

See more details on using hashes here.

File details

Details for the file rdatacompy-0.1.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for rdatacompy-0.1.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 1d67373febe635a0cfb37bcab552ef3940bcd3a5c548873bd9ddb9965931c9a2
MD5 212711954ac1403f064469a7147a6111
BLAKE2b-256 4265c335ad9d8ff745076c3948c256ca545cdb258a72f3f413a624bb0e524ee3

See more details on using hashes here.

File details

Details for the file rdatacompy-0.1.11-cp39-cp39-win_amd64.whl.

File metadata

File hashes

Hashes for rdatacompy-0.1.11-cp39-cp39-win_amd64.whl
Algorithm Hash digest
SHA256 9fdc627e60597f93de6f9b2bac728fbe819111c1f71cb14230e0d2d9f113abec
MD5 a05adb8ab39bd525c6d51414438eb8fc
BLAKE2b-256 d9d433efa2d207fe4963b036cdf544abcc66872d550b669d98902e8fa947bc46

See more details on using hashes here.

File details

Details for the file rdatacompy-0.1.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for rdatacompy-0.1.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 97d549231a62b5229cc017d4db8c0b00310778ccc47606dbec398030d80d38b6
MD5 1edf60d67060831779b600890bcbd723
BLAKE2b-256 f7f49c5636894c121b5d58e67906c6cf2b688ca326bd87b9da245ed635e0a4e6

See more details on using hashes here.

File details

Details for the file rdatacompy-0.1.11-cp38-cp38-win_amd64.whl.

File metadata

File hashes

Hashes for rdatacompy-0.1.11-cp38-cp38-win_amd64.whl
Algorithm Hash digest
SHA256 382c75b9db524a995cb7f8c786e7c47564f603d4b926730e1cab78db6137a2d5
MD5 57ef7e1af4925a72cce360d13fbb6873
BLAKE2b-256 11d41e4f64bde739245049e27ff373a3f76fc3a56aa0f9f1ad088e22ec3c9c5a

See more details on using hashes here.

File details

Details for the file rdatacompy-0.1.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for rdatacompy-0.1.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 73f8d4b180d42d824c1a96c379368a8b40f39efb07d376d37398fb3bad9db13e
MD5 283ec869b55abf5d98cfbbb8ed7c66e0
BLAKE2b-256 1e50f25b5930405ab413437942ed6c99ad2b41f342b8cdad665c9570adf244c0

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page