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.10-cp312-cp312-win_amd64.whl (793.6 kB view details)

Uploaded CPython 3.12Windows x86-64

rdatacompy-0.1.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (920.4 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64

rdatacompy-0.1.10-cp311-cp311-win_amd64.whl (792.3 kB view details)

Uploaded CPython 3.11Windows x86-64

rdatacompy-0.1.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (919.6 kB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ x86-64

rdatacompy-0.1.10-cp310-cp310-win_amd64.whl (792.4 kB view details)

Uploaded CPython 3.10Windows x86-64

rdatacompy-0.1.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (919.8 kB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ x86-64

rdatacompy-0.1.10-cp39-cp39-win_amd64.whl (792.8 kB view details)

Uploaded CPython 3.9Windows x86-64

rdatacompy-0.1.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (920.2 kB view details)

Uploaded CPython 3.9manylinux: glibc 2.17+ x86-64

rdatacompy-0.1.10-cp38-cp38-win_amd64.whl (792.9 kB view details)

Uploaded CPython 3.8Windows x86-64

rdatacompy-0.1.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (920.3 kB view details)

Uploaded CPython 3.8manylinux: glibc 2.17+ x86-64

File details

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

File metadata

File hashes

Hashes for rdatacompy-0.1.10-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 aceb09d460f72c4436b1762c5dec7476f21165556f5b24ad595c178531bab5d4
MD5 c31cda43e48f525550257de008eb56d1
BLAKE2b-256 ba227b7d6947c4fecf8259d0749cf9ad578fd145159a531bde04ef0dadcfa6d3

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for rdatacompy-0.1.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 9358970fbe07bab43b60265c5bfae6742dc81b284953b598f6d0a5385aa6655a
MD5 5a16d735fced7b7a3611800bb8eeb110
BLAKE2b-256 3792aefbe811de739953a3dab6019085397574619243a5fd24eac32815cbca95

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for rdatacompy-0.1.10-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 d29963f5d052435d70cad87b9944677f315752b2b77fa9f48f62d92bd060a0d7
MD5 5fc57a2fab361c531540351d4a09b45a
BLAKE2b-256 68da90978dc520ed5f06cb242438f9f9d5130ca78ffbd60a806adc5b95102c46

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for rdatacompy-0.1.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 ea1ecd12c462c341ecf85da5ec0188c5daba62604144ff452c0f8338121ca908
MD5 bd135e2fc00a18da6aa722e8c9a6974b
BLAKE2b-256 6f933bdc6a616715c06747a527257407ee9e1a979ed8c11bb21fc8bd1fb6ceb0

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for rdatacompy-0.1.10-cp310-cp310-win_amd64.whl
Algorithm Hash digest
SHA256 3df04e5c99ad3cd8a481ab0e2f03424e8a4e589510c0d935bf301d72a4a8633c
MD5 770447e0cf877b04ec10f5f07e186ffe
BLAKE2b-256 04763edba980535fc84cdd43a4bb4c0242414a5b7b6b83eca6c0d29043ee1556

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for rdatacompy-0.1.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 63982c2906e9d33974e700f2448de99593e13e1228ac726828c03cb7c098f56c
MD5 14333ad2b14e44e7dc4d3c06acc812ec
BLAKE2b-256 f24ea131c27923059a7bcee454e89f79d1a6642e315567d4e92a0e127b98b254

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for rdatacompy-0.1.10-cp39-cp39-win_amd64.whl
Algorithm Hash digest
SHA256 fe73768f0445f180463d915c8fd00ed0d2593181d9055503090a21de053a6f2a
MD5 d74b6126627a38b120302b38170a4f95
BLAKE2b-256 a978eb659f59a984c6f54c6122ef93ebe046439cb4e7c982401ff4827b9259a1

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for rdatacompy-0.1.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 e548a75db02416cb984a1e5a664c091ccc2357c3566a8c5d15aac0ed2d17ca27
MD5 ed4bc4ccd0f486c598f24c8f0c602ff5
BLAKE2b-256 989fb740ef585c05152997d47bb67afd974d26db4460d73c8d3c4c0b7d7a3972

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for rdatacompy-0.1.10-cp38-cp38-win_amd64.whl
Algorithm Hash digest
SHA256 b62a572c79c7453ba4e2c7c5df17afde6d2805fcc332057cf1a1a244b08c4203
MD5 19bc1cde8ae9def55677ae4fc1d09c5a
BLAKE2b-256 757e93a6d47437d8cbf6214a1fd0b660a74a7a52c3bb5f1b80999ef4db5d31fa

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for rdatacompy-0.1.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 be0a4ed7247f5fb5e9febd9d9d41c115cc1c861ff81f4487a09cebc3eeb52c37
MD5 32992d418fb5217f9878a544536f1a18
BLAKE2b-256 f0531f05d759e96ca128bb77e51e76c71e3d57a252e2ca37eaf618331f6e70fc

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