Skip to main content

Non-crossing quantile regression toolkit with joint multi-quantile fitting, inference, conformal calibration, and evaluation. Scikit-learn compatible.

Project description

PyPI Python Versions CI Docs

quantile-guard

Non-crossing quantile models with built-in inference, calibration, and evaluation.

A quantile modeling toolkit — not just a quantile regressor. Fits multiple quantiles jointly with monotonicity constraints that guarantee predictions never cross. Wraps the result in inference, conformal calibration, evaluation metrics, and crossing diagnostics.

Scikit-learn compatible. Validated against sklearn, statsmodels, and R's quantreg.

Why Not Just Fit Quantiles Independently?

When you fit quantiles one at a time (as sklearn and statsmodels do), nothing prevents the 90th percentile prediction from falling below the 10th. On real-world data with heavy tails, noise, or many quantile levels, this happens frequently:

n features quantiles Crossing rate (independent) Crossing rate (this package)
500 10 13 30.0% 0%
1,000 10 13 16.5% 0%
2,000 20 13 11.0% 0%
2,000 20 7 4.5% 0%

This package eliminates crossings by construction. The joint formulation also acts as beneficial regularization — achieving equal or better pinball loss than independent fitting.

Full benchmark methodology and results: Benchmarks

What You Get

This is a toolkit, not a single estimator. It covers the workflow from raw quantile regression through calibrated prediction intervals:

Workflow What it does
Joint Quantile Regression Fit multiple quantiles in one call with non-crossing guarantees
Conformalized Quantile Regression Calibrate intervals for finite-sample coverage guarantees
Censored Quantile Regression Handle right- or left-censored (survival) data
Evaluation & Metrics Pinball loss, coverage, interval score, crossing diagnostics
Calibration Diagnostics Coverage by group/bin, nominal vs empirical, sharpness analysis
Crossing Detection & Repair Diagnose and fix crossings from any quantile model

Feature Comparison

Feature This package sklearn statsmodels
Multiple quantiles (joint fit) Yes No No
Non-crossing guarantee Yes No No
Multi-output regression Yes No No
Analytical / kernel / cluster / bootstrap SEs Yes No Partial
L1 / Elastic Net / SCAD / MCP Yes L1 only No
Conformal calibration (CQR) Yes No No
Calibration diagnostics Yes No No
Evaluation metrics suite Yes Partial No
Crossing detection + fix Yes No No
Censored QR Yes No No
Prediction intervals Yes No No
Pseudo R² Yes No Yes
Formula interface Yes No Yes
Sklearn pipeline compatible Yes Yes No

Installation

pip install quantile-guard

Optional extras:

pip install quantile-guard[all]   # formula interface + plots
pip install quantile-guard[plot]   # matplotlib only
pip install quantile-guard[formula] # patsy only

Quick Start

import numpy as np
from quantile_guard import QuantileRegression

X = np.random.default_rng(0).normal(size=(200, 3))
y = X @ [2.0, -1.5, 0.8] + np.random.default_rng(1).normal(scale=0.5, size=200)

# Fit 3 quantiles jointly — guaranteed non-crossing
model = QuantileRegression(tau=[0.1, 0.5, 0.9], se_method='analytical')
model.fit(X, y)

# Summaries with coefficients, SEs, p-values, and 95% CIs
print(model.summary()[0.5]['y'])

# Prediction intervals (guaranteed monotone: lower < median < upper)
interval = model.predict_interval(X[:5], coverage=0.80)
print(interval['y']['lower'], interval['y']['upper'])

Conformal Calibration

Turn raw quantile predictions into intervals with coverage guarantees:

from quantile_guard.conformal import ConformalQuantileRegression

base = QuantileRegression(tau=[0.05, 0.5, 0.95], se_method='analytical')
cqr = ConformalQuantileRegression(base_estimator=base, coverage=0.90)
cqr.fit(X_train, y_train)

intervals = cqr.predict_interval(X_test)
print(cqr.empirical_coverage(X_test, y_test))  # should be >= 0.90

Censored Quantile Regression

For survival data with right- or left-censoring:

from quantile_guard import CensoredQuantileRegression

model = CensoredQuantileRegression(tau=0.5, censoring='right', se_method='analytical')
model.fit(X, observed_time, event_indicator=delta)

Evaluate Any Quantile Model

The metrics and diagnostics modules work with predictions from any source — not just this package:

from quantile_guard.metrics import quantile_evaluation_report
from quantile_guard.postprocess import crossing_summary

# Evaluate predictions from XGBoost, LightGBM, or any other model
report = quantile_evaluation_report(y_true, predictions, taus)
crossings = crossing_summary(predictions, taus)

Regularization

QuantileRegression(tau=0.5, regularization='l1', alpha=0.1)       # Lasso
QuantileRegression(tau=0.5, regularization='elasticnet', alpha=0.1, l1_ratio=0.5)
QuantileRegression(tau=0.5, regularization='scad', alpha=0.3)     # Less bias on large coefficients
QuantileRegression(tau=0.5, regularization='mcp', alpha=0.3)

Inference Options

QuantileRegression(tau=0.5, se_method='analytical')   # Fast asymptotic SEs
QuantileRegression(tau=0.5, se_method='kernel')        # Heteroscedasticity-robust
QuantileRegression(tau=0.5, se_method='bootstrap', n_bootstrap=500)
# Cluster-robust SEs
model.fit(X, y, clusters=group_labels)

Benchmarks

Tested on heavy-tailed heteroscedastic data (Student-t noise, 10-20 features, up to 13 quantiles):

n features quantiles Crossing (this) Crossing (sklearn) Pinball (this) Pinball (sklearn)
500 10 7 0% 11.0% 0.5148 0.5166
500 10 13 0% 30.0% 0.5095 0.5240
1,000 10 13 0% 16.5% 0.5048 0.5071
2,000 20 13 0% 11.0% 0.5599 0.5611

The joint formulation also achieves slightly better pinball loss — the non-crossing constraints act as beneficial regularization.

Speed tradeoff: This package solves a single joint LP with non-crossing constraints, which is slower than fitting each quantile independently. The value is in the guarantee and the richer downstream workflows. For single-quantile fits where speed matters most, sklearn or statsmodels may be more appropriate.

Full results: Benchmarks | Reproduce locally

When to Use This Package

Use this when you need:

  • Multiple quantile predictions that must not cross (production pipelines, interval forecasts)
  • Statistical inference on quantile coefficients (SEs, p-values, confidence intervals)
  • Calibrated prediction intervals (conformal quantile regression)
  • Censored/survival quantile models
  • A complete evaluation workflow for any quantile model's predictions

Use sklearn or statsmodels when:

  • You only need a single quantile (e.g., median regression)
  • Raw speed matters more than crossing guarantees
  • You don't need inference, calibration, or evaluation tooling

Documentation

Full docs: joshvern.github.io/quantile_guard

Implementation

Quantile regression is naturally a linear program. This package solves joint multi-quantile LPs with non-crossing constraints using:

  • PDLP — first-order primal-dual solver (default, from Google OR-Tools)
  • GLOP — revised simplex (faster on small/medium problems)
  • HiGHS — via scipy's sparse LP interface (memory-efficient)
QuantileRegression(tau=0.5, solver_backend='GLOP')   # simplex
QuantileRegression(tau=0.5, use_sparse=True)          # scipy sparse

Dependencies

Required: numpy, pandas, scipy, scikit-learn, ortools, tqdm, joblib

Optional: matplotlib (plots), patsy (formulas), statsmodels (benchmarks)

Contributing

Contributions welcome! Open an issue or submit a pull request on GitHub.

License

MIT

Project details


Download files

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

Source Distribution

quantile_guard-0.5.0.tar.gz (40.4 kB view details)

Uploaded Source

Built Distribution

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

quantile_guard-0.5.0-py3-none-any.whl (27.0 kB view details)

Uploaded Python 3

File details

Details for the file quantile_guard-0.5.0.tar.gz.

File metadata

  • Download URL: quantile_guard-0.5.0.tar.gz
  • Upload date:
  • Size: 40.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.15

File hashes

Hashes for quantile_guard-0.5.0.tar.gz
Algorithm Hash digest
SHA256 7bcbb8a9c189d87eb24a274b60663f075b68158e1b8a56dd99ab2827571d1792
MD5 925ab82b548f823371cfb0fb04a063c3
BLAKE2b-256 ba9df49addc2caf04114367af4eecbb210ba3bc05dec8f9bfbd6907e89d8bced

See more details on using hashes here.

File details

Details for the file quantile_guard-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: quantile_guard-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 27.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.15

File hashes

Hashes for quantile_guard-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c685205a96ef228d19827f07704f5073e5ead25579f71eda026837e222a8a762
MD5 9b0f4660784b2f1dfd0707c6f3d088ce
BLAKE2b-256 056a85003ac948e789821236c9a05aa5800f6be94963f4d588a2b880427f193f

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