Metadata-Version: 2.4
Name: isodistrreg
Version: 0.5.2
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Rust
Classifier: Development Status :: 4 - Beta
Classifier: Operating System :: OS Independent
Classifier: Topic :: Scientific/Engineering :: Mathematics
Classifier: Intended Audience :: Science/Research
Requires-Dist: numpy>=1.20
Requires-Dist: scikit-learn>=1.9.0 ; extra == 'scikit-learn'
Requires-Dist: numpy>=1 ; extra == 'scikit-learn'
Provides-Extra: scikit-learn
License-File: LICENSE
Summary: Isotonic Distributional Regression (IDR)
Keywords: statistics,regression,distributional,isotonic,survival
Author-email: Bram van den Heuvel <bram.vandenheuvel@stat.math.ethz.ch>, Alexander Henzi <henzi.alexander@gmail.com>, Martin Bladt <martinbladt@math.ku.dk>, Johanna Ziegel <johanna.ziegel@stat.math.ethz.ch>
License-Expression: GPL-2.0-or-later
Requires-Python: >=3.13
Description-Content-Type: text/markdown
Project-URL: Issues, https://github.com/AlexanderHenzi/isodistrreg/issues
Project-URL: Repository, https://github.com/AlexanderHenzi/isodistrreg

# isodistrreg: Python bindings

Python bindings for Isotonic Distributional Regression (IDR) and Survival-IDR
(S-IDR), built with [PyO3](https://pyo3.rs) and
[maturin](https://www.maturin.rs). See the [main
README](https://github.com/AlexanderHenzi/isodistrreg) for background and
references.

## Installation

```
pip install isodistrreg
```

Pre-built wheels are provided for Linux, macOS, and Windows on CPython 3.13+.
On other platforms pip builds from the source distribution, which requires a
[Rust toolchain](https://rustup.rs).

The scikit-learn–compatible estimator is available through an optional extra:

```
pip install "isodistrreg[scikit-learn]"
```

## Examples

We use numpy to set up a toy problem, but plain python lists can also be used.

### Precision

CDF outputs (`cdf`, `cdf_at`, `cdf_grid`) are always `np.float32`. Covariates and
thresholds are stored at the dtype of the input arrays passed to `IDR(...)`:
`float32` inputs stay `float32`, `float64` inputs stay `float64`, and `.X` /
`.thresholds` return zero-copy views at the storage dtype. See the docstring on
`IDR.cdf` and `IDR.from_cdfs` in `_core.pyi` for the full rules.

### Example 1: Covariate 1-dimensional, outcome censored

```python
import numpy as np
# we visualize with matplotlib
from matplotlib import pyplot as plt
from isodistrreg import IDR

# Generate an instance of increasing conditional CDFs with censoring, and a one-dimensional covariate
n = 500
rng = np.random.default_rng(seed=123)
x = rng.uniform(size=n)
y = x + rng.uniform(size=n)
c = x + rng.uniform(size=n)

t = np.minimum(y, c)
d = y <= c

# Fit the IDR / S-IDR model, censoring is indicated by "False"
fit = IDR(t, x, d)

# Sorted and deduplicated covariates and thresholds are available
sorted_x = fit.X
sorted_y = fit.thresholds

# Estimate and plot the complete distributional estimate
cdf_for_each_x = fit.cdf(sorted_x)
def plot_cdfs_nicely(cdfs, centers, times):
    """Plot a picture with the right scale; plt.imshow is simpler, but spacing along the axis is not to scale"""
    plt.pcolormesh(
        [centers[0] - (centers[1] - centers[0]) / 2]
            + list((centers[1:] + centers[:-1]) / 2)
            + [centers[-1] + (centers[-1] - centers[-2]) / 2],
        list(times) + [times[-1] + (times[-1] - times[0]) / len(times)],
        cdfs.T,
        vmin=0.0,
        vmax=1.0,
    )
plot_cdfs_nicely(cdf_for_each_x, sorted_x, sorted_y)
plt.colorbar()

# Estimate and plot the mean
mean_for_each_x = fit.predict(sorted_x)
# Due to censoring, the estimated sub-CDF may not always have a mean
plt.plot(sorted_x, sorted_x + 0.5, color="lightblue", label="true mean")
plt.plot(sorted_x, mean_for_each_x, color="red", label="mean")

# Estimate and plot quantiles
probabilities = np.array([0.2, 0.8])
quantiles = fit.quantile(sorted_x[:, np.newaxis], probabilities)
plt.plot(sorted_x, quantiles, label=[f"{p} quantile" for p in probabilities])

plt.legend(loc="lower right")
plt.show()
```

### Example 2: Covariate 3-dimensional
```python
import numpy as np
from isodistrreg import IDR

## toy data (3-dimensional covariate)
X = np.column_stack([np.arange(1, 5)] * 3)
y = np.array([1, 0, 2, 2])

## fit
idr_fit = IDR(X = X, y = y)

## get CDF for new x at all relevant thresholds
new_x = np.array([[1, 1, 1], [1.5, 1.5, 1.5]])
idr_fit.cdf(new_x) # (one CDF per row = per x)

## broadcasting
idr_fit.cdf_at(new_x, 0) # (evaluate CDF at 0 for all covariates)
idr_fit.cdf_at(new_x, [0,1]) # (evaluate CDF for x1 at 0, x2 at 1)
idr_fit.cdf_at(new_x, np.column_stack([[1, 2, 3], [0, 1, 2]])) # (CDF at 1,2,3 for x1, and 0,1,2 for x2)

## same for quantiles
idr_fit.quantile(new_x, 0.5)
idr_fit.quantile(new_x, [0.25, 0.5])
idr_fit.quantile(new_x, np.column_stack([[0.25, 0.5, 0.75], [0.1, 0.2, 0.3]]))

# Fast grid evaluation for 1-dimensional covariate
X = np.arange(5)
y = np.arange(5)
idr_fit = IDR(X = X, y = y)
idr_fit.cdf_grid(X, y) # (CDF at all covariate-threshold-combinations)
```

