Skip to main content

IMVpy

InterModel Vigorish for probabilistic model comparison and attribution

PyPI Python CI Documentation License: GPL-3.0-only

IMVpy implements the InterModel Vigorish (IMV), a model-agnostic metric for comparing two systems that produce probabilities for a binary outcome. It maps each system's geometric mean Bernoulli likelihood to an equivalent weighted coin and reports the enhanced system's advantage relative to the baseline system.

The project, PyPI distribution, and Python import are all named IMVpy / imvpy:

python -m pip install imvpy
import imvpy

print(imvpy.__version__)

IMVpy deliberately installs no imv namespace alias, avoiding ambiguity with unrelated distributions.

Workflows

Workflow Entry point Purpose
Vanilla IMV vanilla_imv Compare binary probability predictions or aggregated likelihoods
Exact SHAP-IMV BinaryIMV Attribute global held-out IMV across the complete feature power set
Multiclass IMV MulticlassIMV Compute one-vs-rest and pairwise multiclass extensions
Model ablation AblationIMV Compare aligned predictions from model variants directionally

All scoring uses probabilities rather than hard labels. IMV is relative to a declared baseline, directional, and intended for held-out or out-of-sample predictions.

Installation

IMVpy supports Python 3.9 and newer.

python -m pip install imvpy

Optional features are installed explicitly:

python -m pip install "imvpy[progress]"       # joblib-aware progress bars
python -m pip install "imvpy[deep-learning]" # PyTorch training helpers

For development from a clone:

git clone https://github.com/intermodelvigorish/imvpy.git
cd imvpy
python -m pip install -e ".[dev]"

Quick Start

Vanilla IMV

Pass baseline probabilities, enhanced probabilities, and observed binary outcomes. Numeric scalars are broadcast, while NumPy arrays, pandas Series, lists, and tuples are accepted as observation-level inputs.

from imvpy import ll, vanilla_imv

outcomes = [1, 0, 1, 0]
enhanced = [0.85, 0.15, 0.75, 0.25]

score = vanilla_imv(0.5, enhanced, outcomes)
print(score)

# The equivalent calculation from already-aggregated likelihoods:
same_score = vanilla_imv(ll(outcomes, 0.5), ll(outcomes, enhanced))

The argument order is always baseline, enhanced, then outcomes. A two-argument call instead treats both inputs as scalar geometric mean likelihoods. Use imv_from_likelihoods when that mode should be explicit.

Exact SHAP-IMV

BinaryIMV fits every feature coalition and calculates exact global Shapley attributions of held-out IMV.

import pandas as pd
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression

from imvpy import BinaryIMV

features, outcome = make_classification(
    n_samples=300,
    n_features=3,
    n_informative=3,
    n_redundant=0,
    random_state=42,
)
columns = ["x1", "x2", "x3"]
data = pd.DataFrame(features, columns=columns).assign(outcome=outcome)

evaluator = BinaryIMV(
    data=data,
    outcome_variable="outcome",
    optional_explanatory_variables=columns,
    model_creator=lambda: LogisticRegression(max_iter=2000),
    split_method="stratified_kfold",
    n_splits=3,
    random_seed=42,
)
evaluator.run_evaluation()

values = {
    feature: evaluator.calculate_imvshapley_value(feature)
    for feature in columns
}
print(values)

Exact SHAP-IMV costs 2**n_features * n_splits * 2 model fits. Keep the feature universe small enough to evaluate the complete power set.

Multiclass IMV

from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

from imvpy import MulticlassIMV

data = load_iris(as_frame=True).frame.rename(columns={"target": "species"})
feature_columns = [column for column in data.columns if column != "species"]

evaluator = MulticlassIMV(
    data=data,
    outcome_variable="species",
    optional_explanatory_variables=feature_columns,
    model_creator=lambda: make_pipeline(
        StandardScaler(),
        LogisticRegression(max_iter=2000),
    ),
    n_splits=5,
    random_state=42,
    stratified=True,
)

fold_matrices, mean_matrix = evaluator.k_fold_imv_matrix()
print(mean_matrix)

The pairwise multiclass matrix is symmetric by construction. This differs from the directional model-ablation matrix.

Model Ablation

Matrix calculation is framework-independent and does not require PyTorch. Each variant must contain probabilities for the same held-out rows in the same order.

import pandas as pd

from imvpy import AblationIMV

predictions = {
    "baseline": pd.DataFrame(
        {
            "True Label": [1, 0, 1, 0],
            "Positive Probability": [0.70, 0.30, 0.65, 0.35],
        }
    ),
    "enhanced": pd.DataFrame(
        {
            "True Label": [1, 0, 1, 0],
            "Positive Probability": [0.90, 0.10, 0.80, 0.20],
        }
    ),
}

matrix = AblationIMV.calculate_imv_matrix(predictions)
print(matrix)

Install imvpy[deep-learning] to construct AblationIMV and use its PyTorch seeding and training helpers. Static matrix calculation and averaging remain available from the base installation.

Plotting and Export

Plotting functions use the package's publication visual system: the canonical navy-to-red IMV palette, Helvetica-compatible typography, black-edged marks, and restrained dashed grids. They accept an existing Matplotlib axis or create one. The shared export helper writes PNG at 800 DPI plus PDF and SVG:

import matplotlib.pyplot as plt

from imvpy.utils import plotting_context, save_figure

figure, axis = evaluator.multinomial_IMV_heatmap(mean_matrix)
paths = save_figure(figure, "artifacts/multiclass_imv")

with plotting_context():
    custom_figure, custom_axis = plt.subplots()
    custom_axis.plot([0, 1], [0.1, 0.4])

The scoped plotting_context applies the same visual choices to custom Matplotlib panels without changing process-wide defaults.

Interpretation

  • IMV is a bounded likelihood transformation, not mutual information, accuracy, entropy, or a calibrated probability.
  • IMV is directional. Reversing baseline and enhanced predictions does not generally negate the original value.
  • Values calculated against different baselines are not directly comparable.
  • A geometric mean likelihood sufficiently below 0.5 has no equivalent-coin root. IMVpy warns and returns NaN rather than silently substituting a value.
  • SHAP-IMV is a global attribution of held-out predictive information, not a local explanation of one prediction.
  • Fold or seed variation is not a confidence interval without a valid inferential procedure.

Report the package version, baseline, estimator, split design, seed, calibration procedure, and any below-chance warnings with each result.

Documentation

Citation

IMVpy implements the method introduced in:

Domingue BW, Rahal C, Faul J, Freese J, Kanopka K, Rigos A, et al. (2025). "The InterModel Vigorish (IMV) as a flexible and portable approach for quantifying predictive accuracy with binary outcomes." PLOS ONE, 20(3), e0316491. doi:10.1371/journal.pone.0316491

Use the repository's citation metadata and record the exact package version when reporting software-derived results.

License

IMVpy is distributed under the GNU General Public License v3.0 only.

Metadata

Release files for imvpy 1.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for imvpy 1.2.0
File Size Uploaded
imvpy-1.2.0.tar.gz 102.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for imvpy 1.2.0
File Interpreter ABI Platform
imvpy-1.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 160.6 kB

Release files / imvpy-1.2.0.tar.gz

Download URL imvpy-1.2.0.tar.gz
Size 102.7 kB
Tags Source
SHA-256 checksum
How to use checksums
d053b3fddd596324b5a725345d973b6e4dbfb5774e12f8c8c1baec8877492ddf
BLAKE2b-256 checksum
How to use checksums
16ea5b0bfbed475c18dbec88e59d1371513b6cec5c57f3d70f4a36ae31cd71b0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.

Transparency log

Release files / imvpy-1.2.0-py3-none-any.whl

Download URL imvpy-1.2.0-py3-none-any.whl
Size 57.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e5f0f30a5d5c0419209e9dc4afc6ff38ca4d520562d3ae5072b0c6be6e0e246f
BLAKE2b-256 checksum
How to use checksums
f717956885c0afdb31f3278a225255663877cd24995c812b24c04403415509d6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.2.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page