Skip to content

Commit ae8d4ca

Browse files
authored
Allow any cell measure (#115)
* allow extra cell measures * update docs * no fancy note * manually lint notebook * break tests * comment valid keys check * do not pass through _get_measure * restore original _get_measure docstring
1 parent eae5c5c commit ae8d4ca

File tree

4 files changed

+43
-17
lines changed

4 files changed

+43
-17
lines changed

cf_xarray/accessor.py

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from xarray import DataArray, Dataset
2222

2323
from .helpers import bounds_to_vertices
24+
from .utils import parse_cell_methods_attr
2425

2526
#: Classes wrapped by cf_xarray.
2627
_WRAPPED_CLASSES = (
@@ -369,11 +370,8 @@ def _get_measure(da: Union[DataArray, Dataset], key: str) -> List[str]:
369370
)
370371

371372
attr = da.attrs["cell_measures"]
372-
strings = [s for scolons in attr.split(":") for s in scolons.split()]
373-
if len(strings) % 2 != 0:
374-
raise ValueError(f"attrs['cell_measures'] = {attr!r} is malformed.")
375-
measures = dict(zip(strings[slice(0, None, 2)], strings[slice(1, None, 2)]))
376-
results = measures.get(key, [])
373+
measures = parse_cell_methods_attr(attr)
374+
results: Union[str, List] = measures.get(key, [])
377375
if isinstance(results, str):
378376
return [results]
379377
return results
@@ -937,6 +935,7 @@ def cell_measures(self) -> Dict[str, List[str]]:
937935
Returns
938936
-------
939937
Dictionary of valid cell measure names that can be used with __getitem__ or .cf[key].
938+
Will be ("area", "volume") or a subset thereof.
940939
"""
941940
assert isinstance(self._obj, DataArray), "this only works with DataArrays"
942941

@@ -1012,13 +1011,7 @@ def get_associated_variable_names(self, name: Hashable) -> Dict[str, List[str]]:
10121011

10131012
if "cell_measures" in attrs_or_encoding:
10141013
coords["cell_measures"] = list(
1015-
itertools.chain(
1016-
*[
1017-
_get_measure(self._obj[name], measure)
1018-
for measure in _CELL_MEASURES
1019-
if measure in attrs_or_encoding["cell_measures"]
1020-
]
1021-
)
1014+
parse_cell_methods_attr(attrs_or_encoding["cell_measures"]).values()
10221015
)
10231016

10241017
if (

cf_xarray/tests/test_accessor.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,13 @@ def test_coordinates():
5353

5454

5555
def test_cell_measures():
56-
da = airds["air"].copy()
57-
da.attrs["cell_measures"] += " dummy: dummy"
56+
ds = airds.copy(deep=True)
57+
ds["foo"] = xr.DataArray(ds["cell_area"], attrs=dict(standard_name="foo_std_name"))
58+
ds["air"].attrs["cell_measures"] += " foo_measure: foo"
59+
assert "foo_std_name" in ds.cf["air_temperature"].cf
60+
5861
expected = dict(area=["cell_area"])
59-
actual = da.cf.cell_measures
62+
actual = ds["air"].cf.cell_measures
6063
assert actual == expected
6164

6265
with pytest.raises(AssertionError, match=r"this only works with DataArrays"):

cf_xarray/utils.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Any, Hashable, Mapping, Optional, TypeVar, cast
1+
from typing import Any, Dict, Hashable, Mapping, Optional, TypeVar, cast
22

33
K = TypeVar("K")
44
V = TypeVar("V")
@@ -47,3 +47,23 @@ def __get__(self, obj, cls):
4747
return self._accessor
4848

4949
return self._accessor(obj)
50+
51+
52+
def parse_cell_methods_attr(attr: str) -> Dict[str, str]:
53+
"""
54+
Parse cell_methods attributes (format is 'measure: name').
55+
56+
Parameters
57+
----------
58+
attr: str
59+
String to parse
60+
61+
Returns
62+
-------
63+
Dictionary mapping measure to name
64+
"""
65+
strings = [s for scolons in attr.split(":") for s in scolons.split()]
66+
if len(strings) % 2 != 0:
67+
raise ValueError(f"attrs['cell_measures'] = {attr!r} is malformed.")
68+
69+
return dict(zip(strings[slice(0, None, 2)], strings[slice(1, None, 2)]))

doc/examples/introduction.ipynb

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,16 @@
555555
"pop.cf.coordinates"
556556
]
557557
},
558+
{
559+
"cell_type": "markdown",
560+
"metadata": {},
561+
"source": [
562+
"**Note:** Although it is possible to assign additional coordinates and cell\n",
563+
"measures, `.cf.coordinates` and `.cf.cell_measures` only return a subset of\n",
564+
"`(\"longitude\", \"latitude\", \"vertical\", \"time\")` and `(\"area\", \"volume\")`,\n",
565+
"respectively.\n"
566+
]
567+
},
558568
{
559569
"cell_type": "markdown",
560570
"metadata": {},
@@ -1021,7 +1031,7 @@
10211031
"name": "python",
10221032
"nbconvert_exporter": "python",
10231033
"pygments_lexer": "ipython3",
1024-
"version": "3.8.6"
1034+
"version": "3.7.3"
10251035
},
10261036
"toc": {
10271037
"base_numbering": 1,

0 commit comments

Comments
 (0)