Skip to content

Commit

Permalink
Fix all-nan slices in ExtremeValues (#1983)
Browse files Browse the repository at this point in the history
### What kind of change does this PR introduce?

* All-nan slices were previously broken in `_extremes_train_1d`, now
there is a fast-track that treats all-nan separately.

### Does this PR introduce a breaking change?
No
  • Loading branch information
Zeitsperre authored Oct 31, 2024
2 parents 3d2be15 + 928eae7 commit f9c2d3e
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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)
--------------------
Expand Down
15 changes: 15 additions & 0 deletions tests/test_sdba/test_adjustment.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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):
Expand Down
8 changes: 6 additions & 2 deletions xclim/sdba/_adjustment.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down

0 comments on commit f9c2d3e

Please sign in to comment.