Skip to content

Commit

Permalink
reorder with window (#1566)
Browse files Browse the repository at this point in the history
<!--Please ensure the PR fulfills the following requirements! -->
<!-- If this is your first PR, make sure to add your details to the
AUTHORS.rst! -->
### Pull Request Checklist:
- [ ] This PR addresses an already opened issue (for bug fixes /
features)
    - This PR fixes #xyz
- [x] Tests for the changes have been added (for bug fixes / features)
- [ ] (If applicable) Documentation has been added / updated (for bug
fixes / features)
- [x] CHANGES.rst has been updated (with summary of main changes)
- [ ] Link to issue (:issue:`number`) and pull request (:pull:`number`)
has been added

### What kind of change does this PR introduce?

* Allow reorder on group including a window

### Does this PR introduce a breaking change?
Previous behavior was to ignored the window (silently) and only do the
reorder on the group (eg. dayofyear), now the window is considered.


### Other information:
This is for MBCn. In an email, Cannon said: "All parts of the process,
including the reordering, are done on your subset, including the buffer
around the day of interest. If you’re using 31 days x 30 years then you
have 930 data points. Bias adjustment would be performed on these 930
cases. At the end, you’d reorder according to the QDM ranks and select
the middle day. Repeat for the remaining days of the year."
  • Loading branch information
aulemahal authored Feb 19, 2024
2 parents fb9b82f + e750705 commit 1508be0
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 15 deletions.
3 changes: 2 additions & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ New features and enhancements
* Added option ``never_reached`` to ``degree_days_exceedance_date`` to assign a custom value when the sum threshold is never reached. (:issue:`1459`, :pull:`1647`).
* Added option ``min_members`` to ensemble statistics to mask elements when the number of valid members is under a threshold. (:issue:`1459`, :pull:`1647`).
* Distribution instances can now be passed to the ``dist`` argument of most statistical indices. (:pull:`1644`).
* Added a new `xclim.indices.generic.select_rolling_resample_op` function to allow for computing rolling statistics. (:issue:`1480`, :pull:`1643`).
* Added a new ``xclim.indices.generic.select_rolling_resample_op`` function to allow for computing rolling statistics. (:issue:`1480`, :pull:`1643`).
* Add the possibility to use a group with a window in ``xc.sdba.processing.reordering``. (:pull:`1566`).

Breaking changes
^^^^^^^^^^^^^^^^
Expand Down
26 changes: 26 additions & 0 deletions tests/test_sdba/test_processing.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import pytest
import xarray as xr

from xclim.core.calendar import date_range
from xclim.core.units import units
from xclim.sdba.adjustment import EmpiricalQuantileMapping
from xclim.sdba.base import Grouper
Expand Down Expand Up @@ -185,6 +186,31 @@ def test_reordering():
assert out.attrs == y.attrs


def test_reordering_with_window():
time = list(
date_range("2000-01-01", "2000-01-04", freq="D", calendar="noleap")
) + list(date_range("2001-01-01", "2001-01-04", freq="D", calendar="noleap"))

x = xr.DataArray(
np.arange(1, 9, 1),
dims=("time"),
coords={"time": time},
)

y = xr.DataArray(
np.arange(8, 0, -1),
dims=("time"),
coords={"time": time},
)

group = Grouper(group="time.dayofyear", window=3)
out = reordering(x, y, group=group)

np.testing.assert_array_equal(out, [3.0, 3.0, 2.0, 2.0, 7.0, 7.0, 6.0, 6.0])
out.attrs.pop("history")
assert out.attrs == y.attrs


def test_to_additive(pr_series, hurs_series):
# log
pr = pr_series(np.array([0, 1e-5, 1, np.e**10]))
Expand Down
58 changes: 44 additions & 14 deletions xclim/sdba/_processing.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ def _normalize(
)


@map_groups(reordered=[Grouper.DIM], main_only=True)
@map_groups(reordered=[Grouper.DIM], main_only=False)
def _reordering(ds, *, dim):
"""Group-wise reordering.
Expand All @@ -159,17 +159,47 @@ def _reordering(ds, *, dim):
def _reordering_1d(data, ordr):
return np.sort(data)[np.argsort(np.argsort(ordr))]

return (
xr.apply_ufunc(
_reordering_1d,
ds.sim,
ds.ref,
input_core_dims=[[dim], [dim]],
output_core_dims=[[dim]],
vectorize=True,
dask="parallelized",
output_dtypes=[ds.sim.dtype],
def _reordering_2d(data, ordr):
data_r = data.ravel()
ordr_r = ordr.ravel()
reorder = np.sort(data_r)[np.argsort(np.argsort(ordr_r))]
return reorder.reshape(data.shape)[
:, int(data.shape[1] / 2)
] # pick the middle of the window

if {"window", "time"} == set(dim):
return (
xr.apply_ufunc(
_reordering_2d,
ds.sim,
ds.ref,
input_core_dims=[["time", "window"], ["time", "window"]],
output_core_dims=[["time"]],
vectorize=True,
dask="parallelized",
output_dtypes=[ds.sim.dtype],
)
.rename("reordered")
.to_dataset()
)
elif len(dim) == 1:
return (
xr.apply_ufunc(
_reordering_1d,
ds.sim,
ds.ref,
input_core_dims=[dim, dim],
output_core_dims=[dim],
vectorize=True,
dask="parallelized",
output_dtypes=[ds.sim.dtype],
)
.rename("reordered")
.to_dataset()
)
else:
raise ValueError(
f"Reordering can only be done along one dimension."
f" If there is more than one, they should be `window` and `time`."
f" The dimensions are {dim}."
)
.rename("reordered")
.to_dataset()
)

0 comments on commit 1508be0

Please sign in to comment.