Skip to content

Commit e139bf5

Browse files
committed
Added a nonparametric mapping mode to QDM. This will be a default in future versions as especially for precipitation the parametric mode can be unstable in some cases
1 parent cbdc83b commit e139bf5

File tree

1 file changed

+22
-13
lines changed

1 file changed

+22
-13
lines changed

ibicus/debias/_quantile_delta_mapping.py

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
StatisticalModel,
1818
ecdf,
1919
gen_PrecipitationGammaLeftCensoredModel,
20+
iecdf,
2021
threshold_cdf_vals,
2122
)
2223
from ..variables import Variable, hurs, pr, psl, rlds, sfcwind, tas, tasmax, tasmin
@@ -156,6 +157,7 @@ class QuantileDeltaMapping(SeasonalAndFutureRunningWindowDebiaser):
156157
scipy.stats.rv_discrete,
157158
scipy.stats.rv_histogram,
158159
StatisticalModel,
160+
None,
159161
] = attrs.field(
160162
validator=attrs.validators.instance_of(
161163
(
@@ -169,6 +171,10 @@ class QuantileDeltaMapping(SeasonalAndFutureRunningWindowDebiaser):
169171
trend_preservation: str = attrs.field(
170172
validator=attrs.validators.in_(["absolute", "relative"])
171173
)
174+
mapping_type: str = attrs.field(
175+
default="parametric",
176+
validator=attrs.validators.in_(["parametric", "nonparametric"]),
177+
)
172178

173179
# Relevant for precipitation
174180
censor_values_to_zero: bool = attrs.field(
@@ -285,25 +291,28 @@ def apply_on_seasonal_and_future_window(
285291
Applies QuantileDeltaMapping at one location and returns the debiased timeseries.
286292
"""
287293

288-
fit_obs, fit_cm_hist = self._get_obs_and_cm_hist_fits(obs, cm_hist)
289-
290-
tau_t = threshold_cdf_vals(
294+
tau_m_p = threshold_cdf_vals(
291295
ecdf(cm_future, cm_future, method=self.ecdf_method),
292296
cdf_threshold=self.cdf_threshold,
293297
)
294298

295-
if self.trend_preservation == "absolute":
296-
bias_corrected_vals = (
297-
cm_future
298-
+ self.distribution.ppf(tau_t, *fit_obs)
299-
- self.distribution.ppf(tau_t, *fit_cm_hist)
299+
if self.mapping_type == "nonparametric":
300+
x_obs_to_cm_fut = iecdf(obs, tau_m_p)
301+
inv_F_cm_hist_tau = iecdf(cm_hist, tau_m_p)
302+
elif self.mapping_type == "parametric":
303+
fit_obs, fit_cm_hist = self._get_obs_and_cm_hist_fits(obs, cm_hist)
304+
305+
x_obs_to_cm_fut = self.distribution.ppf(tau_m_p, *fit_obs)
306+
inv_F_cm_hist_tau = self.distribution.ppf(tau_m_p, *fit_cm_hist)
307+
else:
308+
raise ValueError(
309+
'self.mapping_type needs to be one of ["parametric", "nonparametric"]'
300310
)
311+
312+
if self.trend_preservation == "absolute":
313+
bias_corrected_vals = x_obs_to_cm_fut + cm_future - inv_F_cm_hist_tau
301314
elif self.trend_preservation == "relative":
302-
bias_corrected_vals = (
303-
cm_future
304-
* self.distribution.ppf(tau_t, *fit_obs)
305-
/ self.distribution.ppf(tau_t, *fit_cm_hist)
306-
)
315+
bias_corrected_vals = x_obs_to_cm_fut * cm_future / inv_F_cm_hist_tau
307316
else:
308317
raise ValueError(
309318
'self.trend_preservation needs to be one of ["absolute", "relative"]'

0 commit comments

Comments
 (0)