Skip to content

Commit 2a5ea3d

Browse files
committed
new cell measures implementation
1 parent d2dedfa commit 2a5ea3d

File tree

3 files changed

+46
-24
lines changed

3 files changed

+46
-24
lines changed

cf_xarray/accessor.py

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -338,27 +338,21 @@ def _get_measure_variable(
338338

339339
def _get_measure(obj: Union[DataArray, Dataset], key: str) -> List[str]:
340340
"""
341-
Translate from cell measures ("area" or "volume") to appropriate variable name.
341+
Translate from cell measures to appropriate variable name.
342342
This function interprets the ``cell_measures`` attribute on DataArrays.
343343
344344
Parameters
345345
----------
346346
obj: DataArray, Dataset
347347
DataArray belonging to the coordinate to be checked
348-
key: str, ["area", "volume"]
348+
key: str
349349
key to check for.
350350
351351
Returns
352352
-------
353353
List[str], Variable name(s) in parent xarray object that matches axis or coordinate `key`
354354
"""
355355

356-
valid_keys = _CELL_MEASURES
357-
if key not in valid_keys:
358-
raise KeyError(
359-
f"cf_xarray did not understand key {key!r}. Expected one of {valid_keys!r}"
360-
)
361-
362356
if isinstance(obj, DataArray):
363357
obj = obj._to_temp_dataset()
364358

@@ -438,7 +432,7 @@ def _build_docstring(func):
438432
mapper_docstrings = {
439433
_get_axis_coord: f"One or more of {(_AXIS_NAMES + _COORD_NAMES)!r}",
440434
_get_axis_coord_single: f"One of {(_AXIS_NAMES + _COORD_NAMES)!r}",
441-
_get_measure_variable: f"One of {_CELL_MEASURES!r}",
435+
# _get_measure_variable: f"One of {_CELL_MEASURES!r}",
442436
}
443437

444438
sig = inspect.signature(func)
@@ -653,6 +647,9 @@ class CFAccessor:
653647

654648
def __init__(self, da):
655649
self._obj = da
650+
self._defined_cell_measures = tuple(
651+
set(_CELL_MEASURES + tuple(self.cell_measures))
652+
)
656653

657654
def _process_signature(
658655
self,
@@ -833,7 +830,7 @@ def describe(self):
833830

834831
text += "\nCell Measures:\n"
835832
measures = self.cell_measures
836-
for key in _CELL_MEASURES:
833+
for key in sorted(self._defined_cell_measures):
837834
text += f"\t{key}: {measures[key] if key in measures else []}\n"
838835

839836
text += "\nStandard Names:\n"
@@ -930,15 +927,33 @@ def cell_measures(self) -> Dict[str, List[str]]:
930927
Returns
931928
-------
932929
Dictionary of valid cell measure names that can be used with __getitem__ or .cf[key].
933-
Will be ("area", "volume") or a subset thereof.
934930
"""
935931

936-
measures = {
937-
key: apply_mapper(_get_measure, self._obj, key, error=False)
938-
for key in _CELL_MEASURES
939-
}
932+
obj = self._obj
933+
all_attrs = [
934+
da.attrs.get("cell_measures")
935+
for da in obj.coords.values()
936+
if da.attrs.get("cell_measures")
937+
]
938+
if isinstance(obj, DataArray):
939+
all_attrs += (
940+
[obj.attrs.get("cell_measures")]
941+
if obj.attrs.get("cell_measures")
942+
else []
943+
)
944+
elif isinstance(obj, Dataset):
945+
all_attrs += [
946+
da.attrs.get("cell_measures")
947+
for da in obj.data_vars.values()
948+
if da.attrs.get("cell_measures")
949+
]
950+
951+
measures: Dict[str, List[str]] = dict()
952+
for attr in all_attrs:
953+
for key, value in parse_cell_methods_attr(attr).items():
954+
measures[key] = measures.setdefault(key, []) + [value]
940955

941-
return {k: sorted(v) for k, v in measures.items() if v}
956+
return {k: sorted(set(v)) for k, v in measures.items() if v}
942957

943958
def get_standard_names(self) -> List[str]:
944959

@@ -1069,7 +1084,7 @@ def check_results(names, k):
10691084
check_results(names, k)
10701085
successful[k] = bool(names)
10711086
coords.extend(names)
1072-
elif k in _CELL_MEASURES:
1087+
elif k in self._defined_cell_measures:
10731088
measure = _get_measure(self._obj, k)
10741089
check_results(measure, k)
10751090
successful[k] = bool(measure)

cf_xarray/tests/test_accessor.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,20 +52,28 @@ def test_coordinates():
5252
assert actual == expected
5353

5454

55-
def test_cell_measures():
55+
def test_cell_measures(capsys):
5656
ds = airds.copy(deep=True)
5757
ds["foo"] = xr.DataArray(ds["cell_area"], attrs=dict(standard_name="foo_std_name"))
5858
ds["air"].attrs["cell_measures"] += " foo_measure: foo"
5959
assert "foo_std_name" in ds.cf["air_temperature"].cf
6060

6161
ds["air"].attrs["cell_measures"] += " volume: foo"
62-
expected = dict(area=["cell_area"], volume=["foo"])
62+
expected = dict(area=["cell_area"], foo_measure=["foo"], volume=["foo"])
6363
actual = ds["air"].cf.cell_measures
6464
assert actual == expected
6565

6666
actual = ds.cf.cell_measures
6767
assert actual == expected
6868

69+
ds.cf.describe()
70+
actual = capsys.readouterr().out
71+
expected = (
72+
"\nCell Measures:\n\tarea: ['cell_area']\n\tfoo_measure: ['foo']\n\tvolume: ['foo']\n"
73+
"\nStandard Names:\n\tair_temperature: ['air']\n\tfoo_std_name: ['foo']\n\n"
74+
)
75+
assert actual.endswith(expected)
76+
6977

7078
def test_standard_names():
7179
expected = dict(

doc/examples/introduction.ipynb

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -559,10 +559,9 @@
559559
"cell_type": "markdown",
560560
"metadata": {},
561561
"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"
562+
"**Note:** Although it is possible to assign additional coordinates,\n",
563+
"`.cf.coordinates` and only returns a subset of \n",
564+
"`(\"longitude\", \"latitude\", \"vertical\", \"time\")`.\n"
566565
]
567566
},
568567
{
@@ -927,7 +926,7 @@
927926
"source": [
928927
"## Feature: Weight by Cell Measures\n",
929928
"\n",
930-
"`cf_xarray` can weight by cell measure variables `\"area\"` and `\"volume\"` if the\n",
929+
"`cf_xarray` can weight by cell measure variables if the\n",
931930
"appropriate attribute is set\n"
932931
]
933932
},

0 commit comments

Comments
 (0)