Utilities to generate synthetic tabular samples and compute model alignment (Rashomon alignment).
Project description
ralign — Rashomon Alignment
ralign is a Python package implementing Rashomon Alignment (RA), a framework for measuring functional similarity between classification models by comparing their decision boundaries rather than their predictive accuracy.
Soares, C., van der Putten, P., Pfahringer, B., & Santos, M. (2026). Rashomon Alignment. Faculty of Engineering, University of Porto / LIACC / Fraunhofer AICOS / BrightFactory / Leiden University / University of Waikato.
Motivation
Accuracy alone does not reveal whether two models err in the same regions of the instance space or define structurally similar decision boundaries. Rashomon Alignment addresses this gap by comparing model outputs directly. Two variants are defined:
| Variant | Symbol | Reference distribution | Description |
|---|---|---|---|
| Distributional RA | dRA | Data-generating distribution P(X) | Agreement on a held-out test set; ecologically valid but sensitive to distribution shift |
| Geometric RA | gRA | Uniform distribution U(F) over the instance space | Agreement on uniformly sampled synthetic data; invariant to distribution shift, captures the full instance space |
Formally, for any finite evaluation set X = {x₁, …, xₙ}, the empirical agreement is:
θ_X(Mₐ, M_b) = (1/n) Σᵢ 1[Mₐ(xᵢ) = M_b(xᵢ)] ∈ [0, 1]
- dRA estimates this expectation with xᵢ drawn from the test partition.
- gRA estimates it with xᵢ drawn uniformly from the bounding box of the training features.
A high dRA with low gRA indicates models that agree where data lies but disagree elsewhere — a form of apparent similarity that is fragile under distribution shift.
Installation
pip install ralign
For development (includes test and build tools):
git clone https://github.com/mmrsantos/ralign.git
cd ralign
pip install -e ".[dev]"
Requirements: Python ≥ 3.8, numpy, pandas, scikit-learn, matplotlib, umap-learn.
Quick Start
import numpy as np
from ralign import RashomonAlignment
# Predictions from two models on a held-out test set
pred_unpruned = np.array([0, 1, 1, 0, 1, 1, 0])
pred_pruned = np.array([0, 1, 0, 0, 1, 1, 1])
# Distributional RA (agreement on test data)
dra = RashomonAlignment.distributional(pred_unpruned, pred_pruned)
print(f"dRA: {dra:.3f}")
# Geometric RA (agreement over the full instance space)
import pandas as pd
from sklearn.tree import DecisionTreeClassifier
X = pd.DataFrame({"f1": [1.2, 3.4, 2.1, 0.5, 4.0],
"f2": [0.3, 1.1, 2.2, 3.3, 0.8]})
y = [0, 1, 1, 0, 1]
unpruned = DecisionTreeClassifier(min_samples_split=2, random_state=42).fit(X, y)
pruned = DecisionTreeClassifier(min_samples_split=5, random_state=42).fit(X, y)
result = RashomonAlignment.geometric(
models=[unpruned, pruned],
df=X,
feature_cols=list(X.columns),
n_samples=1000,
random_state=42,
)
print(f"gRA: {result['alignment']:.3f}")
Paper Experiment: Pruned vs. Unpruned Decision Trees
The paper benchmarks RA on 92 UCI datasets, comparing pruned and unpruned DecisionTreeClassifier models under 5-fold cross-validation. The experiment in examples/decision_tree_pruning_rashomon.py reproduces this setup exactly.
Running the experiment
python examples/decision_tree_pruning_rashomon.py
This writes examples/dt_pruning_rashomon_results.csv with columns dataset, accdiff (pruned − unpruned accuracy), dRA, and gRA.
Generating the figures
python examples/visualize_rashomon_results.py
Produces the following plots in examples/img/:
| File | Description |
|---|---|
hist_gRA.png |
Distribution of gRA across datasets |
hist_dRA.png |
Distribution of dRA across datasets |
hist_accdiff.png |
Distribution of accuracy difference |
scatter_gRA_accdiff.png |
gRA vs. accuracy difference (r ≈ 0.514) |
scatter_gRA_dRA.png |
gRA vs. dRA (r ≈ 0.745) |
quadrants_gRA_accdiff.png |
Quadrant analysis by medians of gRA and |Δacc| |
Key findings from the paper
E1 — gRA vs. accuracy difference (r = 0.514): Higher geometric alignment between pruned and unpruned trees is associated with smaller accuracy penalties, but the wide scatter shows the two measures are not redundant. Four regimes emerge when partitioning by their medians:
| Quadrant | Interpretation |
|---|---|
| High |Δacc| + Low gRA (dominant) | Pruning changes the boundary globally and hurts accuracy — expected when boundaries are complex |
| Low |Δacc| + High gRA | Both trees agree globally and have equivalent accuracy — simple, well-captured boundaries |
| High |Δacc| + High gRA (critical) | Models agree over most of the space but differ exactly where the test set lies — accuracy hides the structural alignment |
| Low |Δacc| + Low gRA (rare) | Globally misaligned models unlikely to produce equivalent test accuracy by chance |
E2 — gRA vs. dRA (r = 0.745): dRA is concentrated near 1.0 (models agree on observed data), while gRA is spread across [0, 1]. Several datasets show high dRA (> 0.8) with low gRA (< 0.3), cases where distributional alignment overestimates global structural similarity. No dataset shows high gRA with low dRA — consistent with the change-of-measure relationship: if two models agree across the whole space, they must also agree on any subsample.
Experimental protocol (from the paper)
- Datasets: 92 UCI datasets covering healthcare, finance, biology, image and signal processing; 32–4 600 instances; 4–649 features; numerical, categorical, and mixed types.
- Preprocessing: numerical features imputed with column median and standardised; categorical features imputed with most-frequent value and one-hot encoded.
- Models:
DecisionTreeClassifier(random_state=42).- Unpruned:
min_samples_split=2. - Pruned:
min_samples_split=10+ cost-complexity pruning with α selected per fold via an inner 80/20 split; the largest α in the pruning path is used (most aggressive pruning the data supports).
- Unpruned:
- Evaluation: 5-fold cross-validation (shuffled,
random_state=42); dRA on the test partition; gRA on 1 000 synthetic instances drawn uniformly from the training bounding box.
API Reference
All functionality is accessed via RashomonAlignment.
RashomonAlignment.distributional(a, b, method="jaccard")
Compute agreement between two prediction arrays on the same set of instances.
| Parameter | Type | Description |
|---|---|---|
a, b |
array-like | Prediction vectors (same length) |
method |
"jaccard" | "cohen_kappa" |
Agreement metric |
Returns float in [0, 1] ("jaccard") or [−1, 1] ("cohen_kappa").
RashomonAlignment.generate_uniform_samples(df, n_samples, numeric_cols, binary_cols, random_state)
Generate a synthetic dataset by sampling each column uniformly within its observed range.
| Parameter | Type | Default | Description |
|---|---|---|---|
df |
pd.DataFrame |
— | Reference data defining feature ranges |
n_samples |
int |
10000 |
Number of synthetic instances |
numeric_cols |
list|None | auto-detected | Columns sampled from U(min, max) |
binary_cols |
list|None | auto-detected | Columns sampled from observed unique values |
random_state |
int|None | None |
RNG seed |
Returns pd.DataFrame with the same columns as df.
RashomonAlignment.geometric(models, df, feature_cols, n_samples, binary_cols, numeric_cols, random_state, alignment_method)
Full gRA pipeline: generate synthetic data → apply models → compute agreement.
| Parameter | Type | Default | Description |
|---|---|---|---|
models |
list | — | Exactly two fitted estimators |
df |
pd.DataFrame |
— | Training data (defines bounding box) |
feature_cols |
list | — | Feature columns to use |
n_samples |
int |
1000 |
Synthetic instances |
alignment_method |
str |
"jaccard" |
Passed to distributional |
Returns dict with keys generated, predictions, alignment, alignment_method.
RashomonAlignment.plot_models(compare_result, feature_cols, model_names, figsize, random_state)
Visualise model predictions and their agreement on the synthetic data using UMAP (falls back to t-SNE).
Returns a matplotlib.figure.Figure with three panels: model A predictions, model B predictions, and an agreement map (blue = agree, red = disagree).
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ralign-0.1.0.tar.gz.
File metadata
- Download URL: ralign-0.1.0.tar.gz
- Upload date:
- Size: 10.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5d6a4dc2859bcfc36f97ab41d74d0557570aefb67adb3f6f0f39c87998112386
|
|
| MD5 |
c4a1062587162d8dff2162b89476a1c4
|
|
| BLAKE2b-256 |
01b93871f3bd00787ffba8d40eafcb070be5c30cea479ccc511e708d0655e5f5
|
File details
Details for the file ralign-0.1.0-py3-none-any.whl.
File metadata
- Download URL: ralign-0.1.0-py3-none-any.whl
- Upload date:
- Size: 8.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
babee42461cd77b29798ba9872a225cd5e586766a9ef63cce8d9c03fb014238b
|
|
| MD5 |
9d7299bc92fcb83cda3b9921ef90f944
|
|
| BLAKE2b-256 |
b182d643901354c338830d0fd36e4ec3da0222f1904ad8b3fbf861b6e92ea7d4
|