Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

249 clarification of the symmetry argument in cqr and more general documentation about cqr #443

Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 26 additions & 18 deletions doc/theoretical_description_regression.rst
Original file line number Diff line number Diff line change
Expand Up @@ -245,30 +245,38 @@ uncertainty is higher than :math:`CV+`, because the models' prediction spread
is then higher.


9. The conformalized quantile regression (CQR) method
=====================================================
9. The Conformalized Quantile Regression (CQR) Method
==================================================

The conformalized quantile method allows for better interval widths with
heteroscedastic data. It uses quantile regressors with different quantile
values to estimate the prediction bounds and the residuals of these methods are
used to create the guaranteed coverage value.
The conformalized quantile regression (CQR) method allows for better interval widths with
heteroscedastic data. It uses quantile regressors with different quantile values to estimate
the prediction bounds. The residuals of these methods are used to create the guaranteed
coverage value.

.. math::
Notations and Definitions
-------------------------
- :math:`E_i`: Residuals for the i-th sample in the calibration set.
- :math:`E_{\text{low}}`: Residuals from the lower quantile model.
- :math:`E_{\text{high}}`: Residuals from the upper quantile model.
- :math:`Q_{1-\alpha}(E, \mathcal{I}_2)`: The :math:`(1-\alpha)(1+1/|\mathcal{I}_2|)`-th empirical quantile of the set :math:`{E_i : i \in \mathcal{I}_2}`, where :math:`\mathcal{I}_2` is the set of indices of the residuals in the calibration set.

Mathematical Formulation
------------------------
The prediction interval :math:`\hat{C}_{n, \alpha}^{\text{CQR}}(X_{n+1})` for a new sample :math:`X_{n+1}` is given by:

.. math::

\hat{C}_{n, \alpha}^{\rm CQR}(X_{n+1}) =
[\hat{q}_{\alpha_{lo}}(X_{n+1}) - Q_{1-\alpha}(E_{low}, \mathcal{I}_2),
\hat{q}_{\alpha_{hi}}(X_{n+1}) + Q_{1-\alpha}(E_{high}, \mathcal{I}_2)]
\hat{C}_{n, \alpha}^{\text{CQR}}(X_{n+1}) =
[\hat{q}_{\alpha_{\text{lo}}}(X_{n+1}) - Q_{1-\alpha}(E_{\text{low}}, \mathcal{I}_2),
\hat{q}_{\alpha_{\text{hi}}}(X_{n+1}) + Q_{1-\alpha}(E_{\text{high}}, \mathcal{I}_2)]

Where :math:`Q_{1-\alpha}(E, \mathcal{I}_2) := (1-\alpha)(1+1/ |\mathcal{I}_2|)`-th
empirical quantile of :math:`{E_i : i \in \mathcal{I}_2}` and :math:`\mathcal{I}_2` is the
residuals of the estimator fitted on the calibration set. Note that in the symmetric method,
:math:`E_{low}` and :math:`E_{high}` are equal.
Where:
- :math:`\hat{q}_{\alpha_{\text{lo}}}(X_{n+1})` is the predicted lower quantile for the new sample.
- :math:`\hat{q}_{\alpha_{\text{hi}}}(X_{n+1})` is the predicted upper quantile for the new sample.

As justified by [3], this method offers a theoretical guarantee of the target coverage
level :math:`1-\alpha`.
Note: In the symmetric method, :math:`E_{\text{low}}` and :math:`E_{\text{high}}` are considered equal.

Note that only the split method has been implemented and that it will run three separate
regressions when using :class:`mapie.quantile_regression.MapieQuantileRegressor`.
As justified by the literature, this method offers a theoretical guarantee of the target coverage level :math:`1-\alpha`.


10. The ensemble batch prediction intervals (EnbPI) method
Expand Down
104 changes: 104 additions & 0 deletions examples/regression/1-quickstart/plot_cqr_symmetry_difference.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
"""
======================================================
Plotting MAPIE Quantile Regressor prediction intervals
======================================================
An example plot of :class:`~mapie.quantile_regression.MapieQuantileRegressor`
illustrating the impact of the symmetry parameter.
"""
import numpy as np
from matplotlib import pyplot as plt
from sklearn.datasets import make_regression
from sklearn.ensemble import GradientBoostingRegressor

from mapie.metrics import regression_coverage_score
from mapie.quantile_regression import MapieQuantileRegressor

# Generate synthetic data
X, y = make_regression(n_samples=500, n_features=1, noise=20, random_state=59)

# Define alpha level
alpha = 0.2

# Fit a Gradient Boosting Regressor for quantile regression
quantiles = [0.1, 0.9]
gb_reg = GradientBoostingRegressor(loss="quantile", alpha=quantiles[1])
gb_reg.fit(X, y)

# MAPIE Quantile Regressor with symmetry=True
mapie_qr_sym = MapieQuantileRegressor(estimator=gb_reg, alpha=alpha)
mapie_qr_sym.fit(X, y)
y_pred_sym, y_pis_sym = mapie_qr_sym.predict(X, symmetry=True)

# MAPIE Quantile Regressor with symmetry=False
mapie_qr_asym = MapieQuantileRegressor(estimator=gb_reg, alpha=alpha)
mapie_qr_asym.fit(X, y)
y_pred_asym, y_pis_asym = mapie_qr_asym.predict(X, symmetry=False)

# Calculate coverage scores
coverage_score_sym = regression_coverage_score(
y, y_pis_sym[:, 0], y_pis_sym[:, 1]
)
coverage_score_asym = regression_coverage_score(
y, y_pis_asym[:, 0], y_pis_asym[:, 1]
)

# Sort the values for plotting
order = np.argsort(X[:, 0])
X_sorted = X[order]
y_pred_sym_sorted = y_pred_sym[order]
y_pis_sym_sorted = y_pis_sym[order]
y_pred_asym_sorted = y_pred_asym[order]
y_pis_asym_sorted = y_pis_asym[order]

# Plot symmetric prediction intervals
plt.figure(figsize=(14, 7))

plt.subplot(1, 2, 1)
plt.xlabel("x")
plt.ylabel("y")
plt.scatter(X, y, alpha=0.3)
plt.plot(X_sorted, y_pred_sym_sorted, color="C1")
plt.plot(X_sorted, y_pis_sym_sorted[:, 0], color="C1", ls="--")
plt.plot(X_sorted, y_pis_sym_sorted[:, 1], color="C1", ls="--")
plt.fill_between(
X_sorted.ravel(),
y_pis_sym_sorted[:, 0].ravel(),
y_pis_sym_sorted[:, 1].ravel(),
alpha=0.2,
)
plt.title(
f"Symmetric Intervals\n"
f"Target and effective coverages for "
f"alpha={alpha:.2f}: ({1-alpha:.3f}, {coverage_score_sym:.3f})"
)

# Plot asymmetric prediction intervals
plt.subplot(1, 2, 2)
plt.xlabel("x")
plt.ylabel("y")
plt.scatter(X, y, alpha=0.3)
plt.plot(X_sorted, y_pred_asym_sorted, color="C2")
plt.plot(X_sorted, y_pis_asym_sorted[:, 0], color="C2", ls="--")
plt.plot(X_sorted, y_pis_asym_sorted[:, 1], color="C2", ls="--")
plt.fill_between(
X_sorted.ravel(),
y_pis_asym_sorted[:, 0].ravel(),
y_pis_asym_sorted[:, 1].ravel(),
alpha=0.2,
)
plt.title(
f"Asymmetric Intervals\n"
f"Target and effective coverages for "
f"alpha={alpha:.2f}: ({1-alpha:.3f}, {coverage_score_asym:.3f})"
)

plt.tight_layout()
plt.show()

# Explanation of the results
"""
The symmetric intervals (`symmetry=True`) are easier to interpret and
tend to have higher coverage but might not adapt well to varying
noise levels. The asymmetric intervals (`symmetry=False`) are more
flexible and better capture heteroscedasticity but can appear more jagged.
"""
Loading