geoprior.utils.calibrate#
Forecast utilities.
Functions
|
Apply interval-width calibration factors to quantile forecasts stored in a DataFrame. |
|
Calibrate continuous quantile forecasts by fitting a calibrated CDF surrogate and inverting it at the nominal quantile levels. |
|
Train and apply isotonic calibration models on forecast data. |
|
Calibrate binary probability forecasts with isotonic regression or logistic scaling. |
|
Fit and apply post-hoc interval calibration for quantile forecasts. |
|
Fit empirical interval-width correction factors from an evaluation DataFrame. |
- geoprior.utils.calibrate.fit_interval_factors_df(df_eval, *, target_name='subsidence', column_map=None, step_col='forecast_step', interval=(0.1, 0.9), target_coverage=0.8, median_q=0.5, tol=0.001, f_max=5.0, max_iter=32, verbose=1, logger=None)[source]#
Fit empirical interval-width correction factors from an evaluation DataFrame.
This function learns one multiplicative factor per forecast horizon so that an existing predictive interval reaches a desired empirical coverage on evaluation data. It is designed for the common case where a model already produces quantile forecasts, but the resulting intervals are systematically too narrow or too wide.
For each group of rows, the function identifies
one lower quantile,
one median-like quantile, and
one upper quantile,
then searches for the smallest factor \(f \ge 1\) such that the rescaled interval reaches the requested target coverage.
If \(q_{lo}\), \(q_{md}\), and \(q_{hi}\) denote the selected lower, median, and upper forecasts, the calibrated interval used during fitting is
(1)#\[\tilde q_{lo} = q_{md} - f \, (q_{md} - q_{lo})\](2)#\[\tilde q_{hi} = q_{md} + f \, (q_{hi} - q_{md})\]and the fitted factor is the smallest value whose empirical coverage is at least the target, up to the requested tolerance.
This is a lightweight post-hoc calibration strategy that is especially useful for multi-horizon quantile forecasts, where sharpness and coverage must be balanced carefully in downstream risk analysis [1, 2].
- Parameters:
df_eval (
pandas.DataFrame) – Evaluation table containing the observed target and the forecast quantile columns used for calibration.target_name (
str, default"subsidence") – Base name of the forecasted variable. Whencolumn_mapis not provided, quantile columns are auto-detected using names such as"{target_name}_q10","{target_name}_q50", and"{target_name}_q90".column_map (
mappingorNone, defaultNone) –Optional explicit column mapping. This may contain at least
"quantiles": a mapping from quantile level to column name, or a list of quantile column names,"actual": the observed target column.
This is useful when the DataFrame does not follow the default naming convention.
step_col (
str, default"forecast_step") – Column used to fit separate factors per horizon or lead time. If this column is missing, the full DataFrame is treated as a single group and a single factor is returned with key1.interval (
tupleoffloat, default(0.1,0.9)) – Nominal lower and upper quantiles defining the interval to calibrate. The function uses the closest available quantiles in the DataFrame.target_coverage (
float, default0.8) – Desired empirical coverage of the calibrated interval.median_q (
float, default0.5) – Target quantile used as the center of the interval expansion. The nearest available quantile in the DataFrame is used.tol (
float, default1e-3) – Numerical tolerance used during the bisection search.f_max (
float, default5.0) – Upper bound for the searched interval factor. If the target coverage cannot be reached before this bound,f_maxis returned for that group.max_iter (
int, default32) – Maximum number of bisection iterations used to fit each factor.verbose (
int, default1) – Verbosity level forwarded to the internal logging helper.logger (
logging.LoggerorNone, defaultNone) – Optional logger used for progress messages.
- Returns:
Dictionary mapping each forecast horizon to its fitted interval factor.
- Return type:
dict[int,float]- Raises:
ValueError – If no actual column can be resolved, or if no valid quantile interval can be formed from the available columns.
KeyError – If a user-specified column in
column_mapis missing.
Notes
This function does not change the forecasts directly. It only learns the correction factors. To apply the fitted factors to evaluation or future forecasts, use
apply_interval_factors_df().Because the method calibrates interval width around a median-like center, it is most appropriate when miscalibration is primarily a sharpness problem rather than a severe bias in the central forecast.
Examples
>>> import pandas as pd >>> from geoprior.utils.calibrate import fit_interval_factors_df >>> df = pd.DataFrame( ... { ... "forecast_step": [1, 1, 1, 2, 2, 2], ... "subsidence_actual": [0.4, 0.7, 1.0, 0.5, 0.8, 1.2], ... "subsidence_q10": [0.3, 0.5, 0.8, 0.4, 0.6, 0.9], ... "subsidence_q50": [0.4, 0.7, 1.0, 0.5, 0.8, 1.1], ... "subsidence_q90": [0.5, 0.9, 1.1, 0.6, 1.0, 1.3], ... } ... ) >>> factors = fit_interval_factors_df( ... df, ... target_name="subsidence", ... interval=(0.1, 0.9), ... target_coverage=0.8, ... ) >>> isinstance(factors, dict) True
See also
apply_interval_factors_dfApply learned interval factors to forecast quantiles.
calibrate_quantile_forecastsHigh-level wrapper that can fit and apply factors in one call.
- geoprior.utils.calibrate.apply_interval_factors_df(df, factors, *, target_name='subsidence', column_map=None, step_col='forecast_step', median_q=0.5, keep_original=False, factor_col='calibration_factor', calibrated_col='is_calibrated', enforce_monotonic='cummax', verbose=1, logger=None)[source]#
Apply interval-width calibration factors to quantile forecasts stored in a DataFrame.
This function rescales forecast quantiles around a median-like central forecast using either
a single scalar factor applied to every row, or
a mapping of
forecast_step -> factor.
The main goal is to widen or shrink predictive intervals after the factors have been estimated on evaluation data with
fit_interval_factors_df().If \(q_{md}\) is the selected median-like forecast and \(f_h\) is the factor for horizon \(h\), then for a lower quantile \(q < q_{md}\) the calibrated value is
(3)#\[\tilde q = q_{md} - f_h \, (q_{md} - q)\]and for an upper quantile \(q > q_{md}\) it is
(4)#\[\tilde q = q_{md} + f_h \, (q - q_{md})\]while the median itself is left unchanged.
- Parameters:
df (
pandas.DataFrame) – DataFrame containing forecast quantile columns.factors (
mappingorfloat) –Calibration factor specification. This may be
a scalar applied to every row, or
a mapping from forecast horizon to factor.
target_name (
str, default"subsidence") – Base variable name used to auto-detect quantile columns whencolumn_mapis not supplied.column_map (
mappingorNone, defaultNone) – Optional explicit mapping describing the quantile columns.step_col (
str, default"forecast_step") – Horizon column used whenfactorsis a mapping. If this column is absent, it is created and filled with1.median_q (
float, default0.5) – Quantile used as the center of the recalibration. The closest available forecast quantile is used.keep_original (
bool, defaultFalse) – If True, each raw quantile column is preserved in an additional"{col}_raw"column before calibration is applied.factor_col (
str, default"calibration_factor") – Name of the column storing the factor applied to each row.calibrated_col (
str, default"is_calibrated") – Name of the Boolean marker column added to the returned DataFrame.enforce_monotonic (
{"cummax", "sort", "none"}, default"cummax") –Strategy used after recalibration to keep quantiles ordered.
"cummax"applies a cumulative maximum across quantiles,"sort"sorts the calibrated quantiles row-wise,"none"leaves the result unchanged.
verbose (
int, default1) – Verbosity level forwarded to the internal logger helper.logger (
logging.LoggerorNone, defaultNone) – Optional logger used for progress messages.
- Returns:
A calibrated copy of
dfcontaining updated quantiles and the metadata columns specified byfactor_colandcalibrated_col.- Return type:
- Raises:
ValueError – If no quantile columns can be resolved from the DataFrame or if an invalid
enforce_monotonicmode is requested.
Notes
Monotonicity enforcement is important because quantile-wise post-processing can otherwise produce crossing quantiles. Keeping quantiles ordered is especially important when the calibrated outputs are used to derive intervals, exceedance probabilities, or downstream risk metrics [1, 2].
This function only modifies the quantile forecasts. It does not recompute summary coverage statistics. For fit-and-apply workflows with optional evaluation summaries, use
calibrate_quantile_forecasts().Examples
>>> import pandas as pd >>> from geoprior.utils.calibrate import apply_interval_factors_df >>> df = pd.DataFrame( ... { ... "forecast_step": [1, 1, 2, 2], ... "subsidence_q10": [0.3, 0.5, 0.4, 0.6], ... "subsidence_q50": [0.4, 0.7, 0.5, 0.8], ... "subsidence_q90": [0.5, 0.9, 0.6, 1.0], ... } ... ) >>> out = apply_interval_factors_df( ... df, ... factors={1: 1.2, 2: 1.1}, ... target_name="subsidence", ... ) >>> "calibration_factor" in out.columns True
See also
fit_interval_factors_dfFit per-horizon interval scaling factors.
calibrate_quantile_forecastsHigh-level wrapper for fitting, applying, and summarizing interval calibration.
- geoprior.utils.calibrate.calibrate_quantile_forecasts(*, df_eval=None, df_future=None, target_name='subsidence', column_map=None, step_col='forecast_step', interval=(0.1, 0.9), target_coverage=0.8, median_q=0.5, use='auto', tol=0.02, f_max=5.0, max_iter=32, keep_original=False, enforce_monotonic='cummax', overall_key='__overall__', calibrated_col='is_calibrated', factor_col='calibration_factor', factors=None, save_eval=None, save_future=None, save_stats=None, verbose=1, logger=None)[source]#
Fit and apply post-hoc interval calibration for quantile forecasts.
This is the high-level DataFrame-oriented entry point for interval recalibration in
geoprior.utils.calibrate. It candetect whether evaluation forecasts already appear calibrated,
fit interval-width correction factors from evaluation data,
apply those factors to evaluation and/or future forecasts,
compute before/after summary diagnostics on the evaluation set,
optionally save the outputs to disk.
The function is designed for workflows where quantile forecasts are already available in tabular form and calibration should be handled without retraining the forecasting model.
Conceptually, the function widens or narrows a predictive interval around a median-like forecast so that the empirical interval coverage better matches the requested target. This is a practical post-hoc strategy for uncertainty refinement in multi-horizon forecasting pipelines [1, 2].
- Parameters:
df_eval (
pandas.DataFrameorNone, defaultNone) – Evaluation forecasts used to fit calibration factors and to compute before/after diagnostics. This table should contain the observed target column in addition to the quantile forecasts.df_future (
pandas.DataFrameorNone, defaultNone) – Future or inference forecasts to which the fitted factors should be applied. This table does not need observed targets.target_name (
str, default"subsidence") – Base name used to infer forecast and observation columns whencolumn_mapis not explicitly supplied.column_map (
mappingorNone, defaultNone) – Optional mapping describing the observed column and the quantile columns. This is helpful when the table does not follow the default naming conventions.step_col (
str, default"forecast_step") – Column used to fit and apply separate factors per forecast horizon.interval (
tupleoffloat, default(0.1,0.9)) – Lower and upper quantiles defining the interval to calibrate. The nearest available quantiles are used.target_coverage (
float, default0.8) – Desired empirical coverage after calibration.median_q (
float, default0.5) – Central quantile used as the expansion anchor.use (
{"auto", True, False}, default"auto") –Control flag for whether calibration is performed.
Falsedisables calibration and returns inputs unchanged."auto"skips calibration when evaluation forecasts already look calibrated.Trueforces calibration even if the automatic check would skip it.
tol (
float, default0.02) – Tolerance used by the automatic already-calibrated check.f_max (
float, default5.0) – Maximum factor allowed during fitting.max_iter (
int, default32) – Maximum number of bisection iterations used when fitting factors.keep_original (
bool, defaultFalse) – If True, raw quantiles are copied into*_rawcolumns before calibration is applied.enforce_monotonic (
{"cummax", "sort", "none"}, default"cummax") – Strategy used to prevent quantile crossing after recalibration.overall_key (
strorNone, default"__overall__") – Reserved label stored in the returned statistics dictionary for overall summary reporting.calibrated_col (
str, default"is_calibrated") – Column name added to calibrated outputs as a Boolean marker.factor_col (
str, default"calibration_factor") – Column name used to store the factor applied to each row.factors (
floatormappingorNone, defaultNone) – Optional user-specified calibration factors. If provided, these take precedence over factors fitted fromdf_eval.save_eval (
strorpath-likeorNone, defaultNone) – Optional CSV path for saving the calibrated evaluation table.save_future (
strorpath-likeorNone, defaultNone) – Optional CSV path for saving the calibrated future table.save_stats (
strorpath-likeorNone, defaultNone) – Optional JSON path for saving the calibration summary.verbose (
int, default1) – Verbosity level forwarded to logging helpers.logger (
logging.LoggerorNone, defaultNone) – Optional logger used for progress messages.
- Returns:
df_eval_cal (
pandas.DataFrameorNone) – Calibrated evaluation DataFrame, or None when no evaluation table was provided.df_future_cal (
pandas.DataFrameorNone) – Calibrated future DataFrame, or None when no future table was provided.stats (
dict[str,Any]) – Dictionary describing the calibration workflow. Depending on the path taken, it may containthe target interval and target coverage,
the fitted or user-specified factors,
skip reasons,
evaluation summaries before and after calibration.
- Return type:
Notes
In
use="auto"mode, the function first checks for an explicitcalibrated_coland then falls back to a simple empirical coverage-based decision. This makes the wrapper conservative in repeated workflows, where the same tables may pass through the calibration stage more than once.The returned
statsdictionary is designed to be JSON-friendly and therefore suitable for audit trails, experiment manifests, or gallery artifacts.Examples
>>> import pandas as pd >>> from geoprior.utils.calibrate import ( ... calibrate_quantile_forecasts, ... ) >>> df_eval = pd.DataFrame( ... { ... "forecast_step": [1, 1, 2, 2], ... "subsidence_actual": [0.4, 0.7, 0.5, 0.9], ... "subsidence_q10": [0.3, 0.5, 0.4, 0.6], ... "subsidence_q50": [0.4, 0.7, 0.5, 0.8], ... "subsidence_q90": [0.5, 0.9, 0.6, 1.0], ... } ... ) >>> df_eval_cal, df_future_cal, stats = ( ... calibrate_quantile_forecasts( ... df_eval=df_eval, ... target_name="subsidence", ... target_coverage=0.8, ... ) ... ) >>> isinstance(stats, dict) True
See also
fit_interval_factors_dfFit per-horizon interval-width correction factors.
apply_interval_factors_dfApply a scalar or per-horizon factor map to quantile forecasts.
- geoprior.utils.calibrate.calibrate_forecasts_in(y_val, forecasts_val, *forecasts_to_calibrate, quantiles=(10, 20, 30, 40, 50, 60, 70, 80, 90), prefix='subsidence', verbose=None, _logger=None)[source]#
Train and apply isotonic calibration models on forecast data.
- Parameters:
y_val (
pandas.Series) – Observed target values for calibration.forecasts_val (
pandas.DataFrame) – Validation forecasts with columns named <prefix>_q{quantile} for each quantile.*forecasts_to_calibrate (
DataFrameordict) – One or more DataFrames (or a mapping) of new forecasts to calibrate using trained models.quantiles (
tupleofint, optional) – List of quantile levels (e.g., 10, 50, 90) to calibrate.prefix (
str, default'subsidence') – Column name prefix for quantile forecasts.verbose (
int, optional) – Verbosity level passed to vlog for logging._logger (
Loggerorcallable, optional) – Sink for log messages (print or logging.Logger).
- Returns:
Notes
This function fits an isotonic regression for each quantile on validation data and then predicts calibrated probabilities for new forecasts. Use normalize_model_inputs to handle varied input formats.
Examples
>>> import pandas as pd >>> import numpy as np >>> from geoprior.utils.calibrate import calibrate_forecasts >>> # Create dummy validation series >>> idx = pd.date_range('2021-01-01', periods=50) >>> y_val = pd.Series( ... np.random.randn(50), index=idx ... ) >>> # Simulate validation forecasts >>> val_df = pd.DataFrame({ ... 'subsidence_q10': y_val - 0.2, ... 'subsidence_q90': y_val + 0.2 ... }, index=idx) >>> # Simulate two new forecast sets >>> newA = val_df + 0.1 >>> newB = val_df - 0.1 >>> # Calibrate forecasts >>> results, models = calibrate_forecasts( ... y_val, ... val_df, ... newA, ... newB, ... quantiles=(10, 90), ... verbose=2 ... ) >>> # Access calibrated probabilities >>> results['ModelA']['subsidence_q10_cal_prob'] >>> # Inspect fitted calibrator for q90 >>> models['subsidence_q90'].threshold_
- geoprior.utils.calibrate.calibrate_probability_forecast(df, prob_col, actual_col, method='isotonic', out_col=None, clip=True, savefile=None)[source]#
Calibrate binary probability forecasts with isotonic regression or logistic scaling.
This function post-processes a column of raw forecast probabilities so that they better agree with observed binary outcomes. It returns a copy of the input DataFrame with one additional calibrated probability column.
Two calibration modes are supported:
"isotonic"Non-parametric monotone calibration using isotonic regression.
"logistic"Parametric calibration using logistic regression on the raw probabilities.
- Parameters:
df (
pandas.DataFrame) – DataFrame containing a column of raw forecast probabilities and a column of observed binary outcomes.prob_col (
str) – Name of the column containing the raw forecast probabilities. Values are usually expected in the interval[0, 1].actual_col (
str) – Name of the column containing the realized binary outcomes, encoded as0and1.method (
{"isotonic", "logistic"}, default"isotonic") – Calibration method to apply.out_col (
strorNone, defaultNone) – Name of the calibrated probability column. If None,f"{prob_col}_calib"is used.clip (
bool, defaultTrue) – If True, calibrated outputs are clipped to[0, 1]before being written to the result.savefile (
strorNone, defaultNone) – Optional output path handled by theSaveFile()decorator.
- Returns:
Copy of
dfwith an additional calibrated probability column.- Return type:
- Raises:
ValueError – If
methodis not one of"isotonic"or"logistic".
Notes
This function is appropriate for event-probability forecasts, not for continuous quantile forecasts. For quantile-table recalibration, use
calibrate_forecasts()orcalibrate_quantile_forecasts()depending on the workflow.The isotonic option is often preferred when a flexible monotone mapping is desired, whereas the logistic option is useful when a simple parametric correction is sufficient.
Examples
>>> import pandas as pd >>> from geoprior.utils.calibrate import ( ... calibrate_probability_forecast, ... ) >>> df = pd.DataFrame( ... { ... "p_raw": [0.10, 0.30, 0.60, 0.85], ... "y": [0, 0, 1, 1], ... } ... ) >>> out = calibrate_probability_forecast( ... df, ... prob_col="p_raw", ... actual_col="y", ... method="isotonic", ... ) >>> "p_raw_calib" in out.columns True
See also
calibrate_forecastsCalibrate continuous quantile forecasts by inverting calibrated CDF estimates.
calibrate_quantile_forecastsFit-and-apply interval calibration for tabular quantile forecasts.
- geoprior.utils.calibrate.calibrate_forecasts(df, quantiles, q_prefix, actual_col, method='isotonic', out_prefix='calib', grid_mode='unit', grid_size=1001, group_by=None, savefile=None)[source]#
Calibrate continuous quantile forecasts by fitting a calibrated CDF surrogate and inverting it at the nominal quantile levels.
This function works on a DataFrame containing continuous quantile forecasts and the corresponding observed target. For each requested quantile level \(q\), it builds a binary supervision signal of the form
(5)#\[y_q = \mathbb{1}\{y \le \hat q\}\]where \(y\) is the observed outcome and \(\hat q\) is the raw forecast quantile at level \(q\).
A monotone classifier is then fitted to approximate the conditional CDF at that threshold. Finally, the calibrated forecast is obtained by inverting the fitted CDF on a grid and taking the first threshold whose calibrated CDF reaches the nominal quantile level.
This provides a practical post-hoc recalibration mechanism for continuous quantile forecasts in multi-horizon settings [1, 2].
- Parameters:
df (
pandas.DataFrame) – DataFrame containing the raw forecast quantile columns and the observed target column.quantiles (
sequenceoffloat) – Nominal quantile levels to recalibrate, expressed on the unit interval, for example[0.1, 0.5, 0.9].q_prefix (
str) – Base prefix for the raw quantile columns. For example, ifq_prefix="subsidence", the function expects columns such as"subsidence_q10","subsidence_q50", and"subsidence_q90".actual_col (
str) – Name of the observed continuous target column.method (
{"isotonic", "logistic"}, default"isotonic") – Calibration method used to estimate the monotone CDF surrogate at each quantile level.out_prefix (
str, default"calib") – Prefix used to build the new calibrated quantile column names. The output columns follow the pattern"{out_prefix}_{q_prefix}_qXX".grid_mode (
{"unit", "range"}, default"unit") –Domain used to construct the inversion grid.
"unit"buildsnp.linspace(0, 1, grid_size). This is mainly appropriate when the forecast support is already normalized to the unit interval."range"builds a grid from the observed range of each raw quantile column and is typically the safer choice for forecasts on the original target scale.
grid_size (
int, default1001) – Number of points used in the inversion grid.group_by (
strorNone, defaultNone) – Optional grouping column. When provided, calibration is performed separately within each group, for example by forecast horizon.savefile (
strorNone, defaultNone) – Optional output path handled by theSaveFile()decorator.
- Returns:
Copy of
dfwith calibrated quantile columns appended.- Return type:
Notes
This function differs from
calibrate_quantile_forecasts(). Here, each quantile is recalibrated through a classifier-plus- inversion approach. By contrast,calibrate_quantile_forecasts()applies multiplicative interval width factors around a median-like forecast.The two approaches answer different needs:
use
calibrate_forecasts()when you want to recalibrate the quantile levels themselves through a threshold-CDF view;use
calibrate_quantile_forecasts()when your main issue is interval under- or over-dispersion and you want a simpler, auditable width correction.
When
group_byis used, the function preserves the original row index order after concatenating the calibrated groups.Examples
>>> import pandas as pd >>> from geoprior.utils.calibrate import calibrate_forecasts >>> df = pd.DataFrame( ... { ... "forecast_step": [1, 1, 2, 2], ... "subsidence_actual": [0.4, 0.8, 0.5, 1.0], ... "subsidence_q10": [0.3, 0.6, 0.4, 0.8], ... "subsidence_q50": [0.4, 0.8, 0.5, 1.0], ... "subsidence_q90": [0.5, 1.0, 0.6, 1.2], ... } ... ) >>> out = calibrate_forecasts( ... df, ... quantiles=[0.1, 0.5, 0.9], ... q_prefix="subsidence", ... actual_col="subsidence_actual", ... method="isotonic", ... grid_mode="range", ... group_by="forecast_step", ... ) >>> "calib_subsidence_q50" in out.columns True
See also
calibrate_probability_forecastCalibrate event probabilities rather than continuous quantiles.
calibrate_quantile_forecastsWrapper for interval-width calibration on tabular forecasts.
fit_interval_factors_dfLearn empirical per-horizon interval scaling factors.