Skip to content

Commit d06919f

Browse files
authored
Allow expanding single keys to multiple keys. (#27)
* Allow expanding single keys to multiple keys. .isel(X=5, Y=5) → .isel(xi_rho=5, xi_u=5, xi_v=5, eta_rho=10, ...) Closes #13 * fix test
1 parent e4cce59 commit d06919f

File tree

2 files changed

+40
-5
lines changed

2 files changed

+40
-5
lines changed

cf_xarray/accessor.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import functools
22
import inspect
3+
from collections import ChainMap
34
from typing import Any, List, Optional, Set, Union
45

56
import xarray as xr
@@ -95,7 +96,7 @@ def _get_axis_coord_single(var, key, *args):
9596
results = _get_axis_coord(var, key, *args)
9697
if len(results) > 1:
9798
raise ValueError(
98-
"Multiple results for {key!r} found: {results!r}. Is this valid CF? Please open an issue."
99+
f"Multiple results for {key!r} found: {results!r}. Is this valid CF? Please open an issue."
99100
)
100101
else:
101102
return results[0]
@@ -335,20 +336,34 @@ def _process_signature(self, func, args, kwargs, key_mappers):
335336
def _rewrite_values(self, kwargs, key_mappers: dict, var_kws):
336337
""" rewrites 'dim' for example using 'mapper' """
337338
updates: dict = {}
338-
key_mappers.update(dict.fromkeys(var_kws, _get_axis_coord_single))
339+
340+
# allow multiple return values here.
341+
# these are valid for .sel, .isel, .coarsen
342+
key_mappers.update(dict.fromkeys(var_kws, _get_axis_coord))
343+
339344
for key, mapper in key_mappers.items():
340345
value = kwargs.get(key, None)
346+
341347
if value is not None:
342348
if isinstance(value, str):
343349
value = [value]
344350

345351
if isinstance(value, dict):
346352
# this for things like isel where **kwargs captures things like T=5
347-
updates[key] = {
348-
mapper(self._obj, k, False, k): v for k, v in value.items()
349-
}
353+
# .sel, .isel, .rolling
354+
# Account for multiple names matching the key.
355+
# e.g. .isel(X=5) → .isel(xi_rho=5, xi_u=5, xi_v=5, xi_psi=5)
356+
# where xi_* have attrs["axis"] = "X"
357+
updates[key] = ChainMap(
358+
*[
359+
dict.fromkeys(mapper(self._obj, k, False, k), v)
360+
for k, v in value.items()
361+
]
362+
)
363+
350364
elif value is Ellipsis:
351365
pass
366+
352367
else:
353368
# things like sum which have dim
354369
updates[key] = [mapper(self._obj, v, False, v) for v in value]

cf_xarray/tests/test_accessor.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,26 @@ def test_kwargs_methods(obj):
9090
assert_identical(expected, actual)
9191

9292

93+
def test_kwargs_expand_key_to_multiple_keys():
94+
95+
ds = xr.Dataset()
96+
ds.coords["x1"] = ("x1", range(30), {"axis": "X"})
97+
ds.coords["y1"] = ("y1", range(20), {"axis": "Y"})
98+
ds.coords["x2"] = ("x2", range(10), {"axis": "X"})
99+
ds.coords["y2"] = ("y2", range(5), {"axis": "Y"})
100+
101+
ds["v1"] = (("x1", "y1"), np.ones((30, 20)) * 15)
102+
ds["v2"] = (("x2", "y2"), np.ones((10, 5)) * 15)
103+
104+
actual = ds.cf.isel(X=5, Y=3)
105+
expected = ds.isel(x1=5, y1=3, x2=5, y2=3)
106+
assert_identical(actual, expected)
107+
108+
actual = ds.cf.coarsen(X=10, Y=5)
109+
expected = ds.coarsen(x1=10, y1=5, x2=10, y2=5)
110+
assert_identical(actual.mean(), expected.mean())
111+
112+
93113
@pytest.mark.parametrize("obj", objects)
94114
def test_args_methods(obj):
95115
with raise_if_dask_computes():

0 commit comments

Comments
 (0)