distribution_fitting

distribution_fitting

Module: distribution_fitting.py

This module contains functions for statistical distribution fitting and quantile mapping used in the bias correction workflow. It includes functions for: - Calculating L-moments and L-moment ratios (using unbiased PWM estimators from Hosking 1990). - Fitting a gamma distribution using MLE (scipy.stats.gamma.fit with floc=0). - Fitting a Generalized Pareto Distribution (GPD) to data above a threshold. - Performing K-Fold cross-validation to obtain stable GPD parameters. - Applying gamma distribution-based quantile mapping with GPD tail adjustment using proper conditional probability mapping for extreme values.

Dependencies: - Imports default parameters from the config module. - Uses scipy.stats and sklearn.model_selection for statistical calculations.

Author: Benny Istanto Applied Climatology Study Program, Department of Geophysics and Meteorology, Bogor Agricultural University, Indonesia Email: bennyistanto@apps.ipb.ac.id

with supervision from Prof. Rizaldi Boer and Dr. I Putu Santikayasa

Update: 2026.03

Functions

Name Description
calculate_l_moments Calculate L-moments and L-moment ratios for the given data.
cross_validate_gpd Cross-validate GPD fitting by splitting data into folds.
fit_cpc_parameters_on_native_grid Fit gamma and GPD distribution parameters at each CPC native-resolution cell.
fit_gamma_distribution Fit a gamma distribution to the data using Maximum Likelihood Estimation (MLE)
fit_generalized_pareto_distribution Fit a Generalized Pareto Distribution (GPD) to the excesses above the threshold.
gamma_quantile_mapping Apply gamma distribution-based quantile mapping with tail adjustment
gamma_quantile_mapping_precomputed Apply gamma distribution-based quantile mapping with GPD tail adjustment,
interpolate_cpc_params_to_imerg_grid Bilinearly interpolate CPC distribution parameters from native ~0.5° to

calculate_l_moments

distribution_fitting.calculate_l_moments(data)

Calculate L-moments and L-moment ratios for the given data.

L-moments are statistics used to describe the shape of a probability distribution. They are analogous to conventional moments but can be more robust to outliers.

Uses the unbiased PWM estimators from Hosking (1990): b_r = (1/n) * sum_{i=1}^{n} C(i-1,r)/C(n-1,r) * x_{i:n} where x_{i:n} are the order statistics.

Parameters: data (numpy.ndarray): The input data.

Returns: tuple: L-moments (l1, l2, l3, l4) and L-moment ratios (t2, t3, t4)

cross_validate_gpd

distribution_fitting.cross_validate_gpd(
    data,
    threshold,
    n_splits=N_SPLITS_GPD_CROSSVALIDATE,
)

Cross-validate GPD fitting by splitting data into folds.

This function uses K-Fold cross-validation to assess the stability and reliability of the GPD parameter estimates.

Parameters: data (numpy.ndarray): Array of data values. threshold (float): Threshold value for defining the excesses. n_splits (int, optional): Number of cross-validation splits. Default is 5.

Returns: tuple: Averaged parameters of the GPD from cross-validation (shape, location, scale).

fit_cpc_parameters_on_native_grid

distribution_fitting.fit_cpc_parameters_on_native_grid(cpc_native_dekad)

Fit gamma and GPD distribution parameters at each CPC native-resolution cell.

This avoids redundantly fitting the same CPC time series 25 times (as happens when CPC is nearest-neighbour regridded to 0.1°). Instead, parameters are fitted once per ~0.5° cell and later interpolated to the IMERG grid.

Based on the BCSD principle (Wood et al. 2004): correct at reference resolution, then disaggregate smoothly.

Parameters

Name Type Description Default
cpc_native_dekad xarray.DataArray CPC precipitation at native ~0.5° resolution for one dekad across all years. Shape (n_time, n_lat_cpc, n_lon_cpc). required

Returns

Name Type Description
dict of xarray.DataArray Dictionary with 9 parameter arrays at CPC native resolution: ‘gamma_shape’, ‘gamma_scale’, ‘gpd_threshold’, ‘gpd_shape’, ‘gpd_loc’, ‘gpd_scale’, ‘upper_cap’, ‘p_threshold’, ‘p_dry_cpc’. Ocean/invalid cells are NaN.

Notes

Fixes applied (2026.04): * Fix C (Cannon 2015 §3.2): gamma distribution fitted on WET-day values only (> WET_DAY_THRESHOLD), not on all values including zeros. The GPD threshold is also computed from wet values only. * Fix A (Coles 2001): GPD fitting inside cross_validate_gpd uses floc=0 (see fit_generalized_pareto_distribution). * New: p_dry_cpc is returned per cell so the downstream mapping step can apply Cannon dry-day handling using the interpolated dry-day frequency at each IMERG pixel.

fit_gamma_distribution

distribution_fitting.fit_gamma_distribution(data)

Fit a gamma distribution to the data using Maximum Likelihood Estimation (MLE) via scipy.stats.gamma.fit with location fixed at zero.

For precipitation data, the gamma distribution is a natural choice as it models positive-valued, right-skewed random variables. Fixing loc=0 ensures the distribution starts at zero, which is physically meaningful for precipitation.

Parameters: data (numpy.ndarray): Array of data values.

Returns: tuple: Fitted parameters (shape, loc, scale) of the gamma distribution.

fit_generalized_pareto_distribution

distribution_fitting.fit_generalized_pareto_distribution(data, threshold)

Fit a Generalized Pareto Distribution (GPD) to the excesses above the threshold.

The GPD is often used in extreme value theory to model the tail of a distribution. It’s particularly useful for modeling events that exceed a high threshold.

Parameters: data (numpy.ndarray): Array of data values. threshold (float): Threshold value for defining the excesses.

Returns: tuple: Fitted parameters of the GPD (shape, location, scale).

gamma_quantile_mapping

distribution_fitting.gamma_quantile_mapping(imerg_values, cpc_values)

Apply gamma distribution-based quantile mapping with tail adjustment to correct the distribution of precipitation data.

This function fits gamma distributions to the IMERG and CPC precipitation values using MLE (scipy.stats.gamma.fit). It then computes the cumulative distribution function (CDF) of the IMERG values and applies the inverse CDF of the CPC values to obtain the corrected precipitation values. Additionally, it adjusts the tails using the Generalized Pareto Distribution (GPD) to better capture extreme values.

The GPD tail adjustment uses proper conditional probability mapping: For values above the threshold, the unconditional CDF value is mapped to the conditional exceedance probability before applying the GPD inverse CDF.

Parameters: imerg_values (numpy.ndarray): Array of IMERG precipitation values. cpc_values (numpy.ndarray): Array of CPC precipitation values.

Returns: numpy.ndarray: Corrected precipitation values after gamma quantile mapping with tail adjustment.

Notes

Fixes applied (2026.04): * Fix C (Cannon 2015 §3.2): gamma distributions are fitted on wet-day values only (> WET_DAY_THRESHOLD). The dry-day fraction is handled explicitly via the unconditional CDF construction, and sub-threshold IMERG values are mapped either to zero (when the unconditional probability is below the CPC dry-day fraction) or through the conditional wet CDF of CPC. This prevents drizzle days from contaminating the gamma body and matches the CPC dry-day frequency. * Fix B: GPD substitution is triggered on IMERG’s own 80th percentile of wet values, not on the CPC threshold, and the conditional probability is computed consistently in unconditional CDF space. * Fix A (Coles 2001): GPD fitting uses floc=0 internally.

gamma_quantile_mapping_precomputed

distribution_fitting.gamma_quantile_mapping_precomputed(
    imerg_values,
    cpc_gamma_shape,
    cpc_gamma_scale,
    cpc_gpd_threshold,
    cpc_gpd_shape,
    cpc_gpd_loc,
    cpc_gpd_scale,
    cpc_upper_cap,
    cpc_p_threshold,
    p_dry_cpc,
)

Apply gamma distribution-based quantile mapping with GPD tail adjustment, using pre-computed CPC-side parameters instead of fitting CPC per pixel.

This is the BCSD-path counterpart to gamma_quantile_mapping(). Statistical logic is identical, but CPC parameters come from the native-resolution fit (fit_cpc_parameters_on_native_grid) and are bilinearly interpolated to the IMERG grid, eliminating 0.5° block artefacts.

Parameters

Name Type Description Default
imerg_values numpy.ndarray IMERG precipitation time series for one pixel. required
cpc_gamma_shape float Pre-computed CPC gamma shape (fitted on wet values only, Fix C). required
cpc_gamma_scale float Pre-computed CPC gamma scale (fitted on wet values only, Fix C). required
cpc_gpd_threshold float Pre-computed CPC GPD threshold (80th percentile of wet values). required
cpc_gpd_shape float Pre-computed CPC GPD shape (fitted with floc=0, Fix A). required
cpc_gpd_loc float Pre-computed CPC GPD location. required
cpc_gpd_scale float Pre-computed CPC GPD scale. required
cpc_upper_cap float Pre-computed CPC upper cap (99.9th percentile of wet values). required
cpc_p_threshold float Pre-computed CPC gamma CDF value at the GPD threshold. required
p_dry_cpc float Pre-computed CPC dry-day fraction (fraction of days <= WET_DAY_THRESHOLD). Required for Cannon 2015 §3.2 dry-day handling (Fix C). required

Returns

Name Type Description
numpy.ndarray Corrected precipitation values after quantile mapping.

Notes

Fixes applied (2026.04): * Fix C (Cannon 2015 §3.2): IMERG gamma fitted on wet-day values only; Cannon dry-day handling uses p_dry_imerg (per pixel) and p_dry_cpc (interpolated from native CPC fit) to match dry-day frequencies. * Fix B: GPD substitution triggered on IMERG-side 80th percentile of wet values, with conditional probability computed in the unconditional CDF space for consistency between gamma body and GPD tail. * Fix A (Coles 2001): inherited via cpc_gpd_* which were fitted with floc=0 upstream in cross_validate_gpd.

interpolate_cpc_params_to_imerg_grid

distribution_fitting.interpolate_cpc_params_to_imerg_grid(
    cpc_params,
    target_lat,
    target_lon,
)

Bilinearly interpolate CPC distribution parameters from native ~0.5° to the IMERG 0.1° grid. Uses a two-pass approach: bilinear first, then nearest-neighbour to fill boundary NaN values.

Parameters

Name Type Description Default
cpc_params dict of xarray.DataArray CPC distribution parameters at native resolution, as returned by fit_cpc_parameters_on_native_grid(). required
target_lat numpy.ndarray Target latitude values (IMERG grid). required
target_lon numpy.ndarray Target longitude values (IMERG grid). required

Returns

Name Type Description
dict of xarray.DataArray Interpolated parameters at IMERG resolution.
Back to top