diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c80fba2a4..ecda8c215 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -16,6 +16,7 @@ Bug fixes * Fixed a units formatting bug with indicators that output "delta" Celsius degrees. (:pull:`1973`). * Corrected the ``"choices"`` of parameter ``op`` in the docstring of ``frost_free_spell_max_length``. (:pull:`1977`). * Reorganised how ``Indicator`` subclasses can added arguments to the call signature. Injecting such arguments now works. For xclim's subclasses, this bug only affected the ``indexer`` argument of indicators subclassing ``xc.core.indicator.IndexingIndicator``. (:pull:`1981`). +* All-nan slices are now treated correctly in method `ExtremeValues`. (:issue:`1982`, :pull:`1983`). v0.53.1 (2024-10-21) -------------------- diff --git a/tests/test_sdba/test_adjustment.py b/tests/test_sdba/test_adjustment.py index f7d1c47f8..706bc5a29 100644 --- a/tests/test_sdba/test_adjustment.py +++ b/tests/test_sdba/test_adjustment.py @@ -725,6 +725,8 @@ def dist(ref, sim): assert (ref - scen).mean().tasmin < 5e-3 +# TODO: below we use `.adjust(scen,sim)`, but in the function signature, `sim` comes before +# are we testing the right thing below? class TestExtremeValues: @pytest.mark.parametrize( "c_thresh,q_thresh,frac,power", @@ -805,6 +807,19 @@ def test_real_data(self, open_dataset): new_scen = EX.adjust(scen, hist, frac=0.000000001) new_scen.load() + def test_nan_values(self): + times = xr.cftime_range("1990-01-01", periods=365, calendar="noleap") + ref = xr.DataArray( + np.arange(365), + dims=("time"), + coords={"time": times}, + attrs={"units": "mm/day"}, + ) + hist = (ref.copy() * np.nan).assign_attrs(ref.attrs) + EX = ExtremeValues.train(ref, hist, cluster_thresh="10 mm/day", q_thresh=0.9) + new_scen = EX.adjust(sim=hist, scen=ref) + assert new_scen.isnull().all() + class TestOTC: def test_compare_sbck(self, random, series): diff --git a/xclim/sdba/_adjustment.py b/xclim/sdba/_adjustment.py index 431df0195..ed7b4e36c 100644 --- a/xclim/sdba/_adjustment.py +++ b/xclim/sdba/_adjustment.py @@ -778,10 +778,14 @@ def _fit_on_cluster(data, thresh, dist, cluster_thresh): def _extremes_train_1d(ref, hist, ref_params, *, q_thresh, cluster_thresh, dist, N): """Train for method ExtremeValues, only for 1D input along time.""" + # Fast-track, do nothing for all-nan slices + if all(np.isnan(ref)) or all(np.isnan(hist)): + return np.full(N, np.nan), np.full(N, np.nan), np.nan + # Find quantile q_thresh thresh = ( - np.quantile(ref[ref >= cluster_thresh], q_thresh) - + np.quantile(hist[hist >= cluster_thresh], q_thresh) + np.nanquantile(ref[ref >= cluster_thresh], q_thresh) + + np.nanquantile(hist[hist >= cluster_thresh], q_thresh) ) / 2 # Fit genpareto on cluster maximums on ref (if needed) and hist.