Skip to content

Commit 4249cf1

Browse files
authored
properties now return dictionaries rather than lists (#110)
* properties now return dicts rather than list * sort standard names * no need to call dict twice * describe uses properties * update docs * oops, restore sorting * sorting in describe and update .keys
1 parent 630d40d commit 4249cf1

File tree

3 files changed

+76
-75
lines changed

3 files changed

+76
-75
lines changed

cf_xarray/accessor.py

Lines changed: 55 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -823,31 +823,30 @@ def describe(self):
823823
Print a string repr to screen.
824824
"""
825825
text = "Axes:\n"
826+
axes = self.axes
826827
for key in _AXIS_NAMES:
827-
axes = apply_mapper(_get_axis_coord, self._obj, key, error=False)
828-
text += f"\t{key}: {axes}\n"
828+
text += f"\t{key}: {axes[key] if key in axes else []}\n"
829829

830830
text += "\nCoordinates:\n"
831+
coords = self.coordinates
831832
for key in _COORD_NAMES:
832-
coords = apply_mapper(_get_axis_coord, self._obj, key, error=False)
833-
text += f"\t{key}: {coords}\n"
833+
text += f"\t{key}: {coords[key] if key in coords else []}\n"
834834

835835
text += "\nCell Measures:\n"
836-
for measure in _CELL_MEASURES:
837-
if isinstance(self._obj, Dataset):
838-
text += f"\t{measure}: unsupported\n"
839-
else:
840-
measures = apply_mapper(_get_measure, self._obj, measure, error=False)
841-
text += f"\t{measure}: {measures}\n"
836+
if isinstance(self._obj, Dataset):
837+
measures = {key: "unsupported" for key in _CELL_MEASURES}
838+
else:
839+
measures = self.cell_measures
840+
for key in _CELL_MEASURES:
841+
text += f"\t{key}: {measures[key] if key in measures else []}\n"
842842

843843
text += "\nStandard Names:\n"
844844
if isinstance(self._obj, DataArray):
845845
text += "\tunsupported\n"
846846
else:
847-
stdnames = sorted(self.get_standard_names())
848-
for name in stdnames:
849-
if name not in _COORD_NAMES:
850-
text += f"\t{name}: {_get_with_standard_name(self._obj, name)}\n"
847+
for key, value in sorted(self.standard_names.items()):
848+
if key not in _COORD_NAMES:
849+
text += f"\t{key}: {value}\n"
851850

852851
print(text)
853852

@@ -871,101 +870,92 @@ def keys(self) -> Set[str]:
871870
-------
872871
Set of valid key names that can be used with __getitem__ or .cf[key].
873872
"""
874-
varnames = [
875-
key
876-
for key in _AXIS_NAMES + _COORD_NAMES
877-
if apply_mapper(_get_axis_coord, self._obj, key, error=False)
878-
]
873+
874+
varnames = list(self.axes) + list(self.coordinates)
879875
if not isinstance(self._obj, Dataset):
880-
measures = [
881-
key
882-
for key in _CELL_MEASURES
883-
if apply_mapper(_get_measure, self._obj, key, error=False)
884-
]
885-
if measures:
886-
varnames.extend(measures)
887-
888-
varnames.extend(self.get_standard_names())
876+
varnames.extend(list(self.cell_measures))
877+
varnames.extend(list(self.standard_names))
878+
889879
return set(varnames)
890880

891881
@property
892-
def axes(self) -> Set[str]:
882+
def axes(self) -> Dict[str, List[str]]:
893883
"""
894-
Property that returns valid Axis names for ``.cf[]``.
884+
Property that returns a dictionary mapping valid Axis standard names for ``.cf[]``
885+
to variable names.
895886
896887
This is useful for checking whether a key is valid for indexing, i.e.
897888
that the attributes necessary to allow indexing by that key exist.
898889
However, it will only return the Axis names, not Coordinate names.
899890
900891
Returns
901892
-------
902-
Set of valid Axis names that can be used with ``__getitem__`` or ``.cf[key]``.
893+
Dictionary of valid Axis names that can be used with ``__getitem__`` or ``.cf[key]``.
903894
Will be ("X", "Y", "Z", "T") or a subset thereof.
904895
"""
905-
varnames = [
906-
key
896+
vardict = {
897+
key: apply_mapper(_get_axis_coord, self._obj, key, error=False)
907898
for key in _AXIS_NAMES
908-
if apply_mapper(_get_axis_coord, self._obj, key, error=False)
909-
]
899+
}
910900

911-
return set(varnames)
901+
return {k: sorted(v) for k, v in vardict.items() if v}
912902

913903
@property
914-
def coordinates(self) -> Set[str]:
904+
def coordinates(self) -> Dict[str, List[str]]:
915905
"""
916-
Property that returns valid Coordinate names for .cf[].
906+
Property that returns a dictionary mapping valid Coordinate standard names for ``.cf[]``
907+
to variable names.
917908
918909
This is useful for checking whether a key is valid for indexing, i.e.
919910
that the attributes necessary to allow indexing by that key exist.
920911
However, it will only return the Coordinate names, not Axis names.
921912
922913
Returns
923914
-------
924-
Set of valid Coordinate names that can be used with ``__getitem__`` or ``.cf[key]``.
915+
Dictionary of valid Coordinate names that can be used with ``__getitem__`` or ``.cf[key]``.
925916
Will be ("longitude", "latitude", "vertical", "time") or a subset thereof.
926917
"""
927-
varnames = [
928-
key
918+
vardict = {
919+
key: apply_mapper(_get_axis_coord, self._obj, key, error=False)
929920
for key in _COORD_NAMES
930-
if apply_mapper(_get_axis_coord, self._obj, key, error=False)
931-
]
921+
}
932922

933-
return set(varnames)
923+
return {k: sorted(v) for k, v in vardict.items() if v}
934924

935925
@property
936-
def cell_measures(self) -> Set[str]:
926+
def cell_measures(self) -> Dict[str, List[str]]:
937927
"""
938-
Property that returns valid cell measure names for .cf[].
928+
Property that returns a dictionary mapping valid cell measure standard names for ``.cf[]``
929+
to variable names.
939930
940931
This is useful for checking whether a key is valid for indexing, i.e.
941932
that the attributes necessary to allow indexing by that key exist.
942933
However, it will only return the cell measure names.
943934
944935
Returns
945936
-------
946-
Set of valid cell measure names that can be used with __getitem__ or .cf[key].
937+
Dictionary of valid cell measure names that can be used with __getitem__ or .cf[key].
947938
"""
948-
assert isinstance(self._obj, DataArray), "this works with DataArrays"
939+
assert isinstance(self._obj, DataArray), "this only works with DataArrays"
949940

950-
measures = [
951-
key
941+
measures = {
942+
key: apply_mapper(_get_measure, self._obj, key, error=False)
952943
for key in _CELL_MEASURES
953-
if apply_mapper(_get_measure, self._obj, key, error=False)
954-
]
944+
}
955945

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

958948
def get_standard_names(self) -> List[str]:
959949

960950
warnings.warn(
961-
"Now called `standard_names` and `get_standard_names` will be removed in a future version.",
951+
"`get_standard_names` will be removed in a future version in favor of `standard_names`.",
962952
DeprecationWarning,
963953
)
964954

965-
return self.standard_names
955+
return list(self.standard_names.keys())
966956

967957
@property
968-
def standard_names(self) -> List[str]:
958+
def standard_names(self) -> Dict[str, List[str]]:
969959
"""
970960
Returns a sorted list of standard names in Dataset.
971961
@@ -977,21 +967,20 @@ def standard_names(self) -> List[str]:
977967
978968
Returns
979969
-------
980-
list of standard names in dataset
970+
Dictionary of standard names in dataset
981971
"""
982972
if isinstance(self._obj, Dataset):
983973
variables = self._obj.variables
984974
elif isinstance(self._obj, DataArray):
985975
variables = self._obj.coords
986-
return sorted(
987-
set(
988-
[
989-
v.attrs["standard_name"]
990-
for k, v in variables.items()
991-
if "standard_name" in v.attrs
992-
]
993-
)
994-
)
976+
977+
vardict: Dict[str, List[str]] = dict()
978+
for k, v in variables.items():
979+
if "standard_name" in v.attrs:
980+
std_name = v.attrs["standard_name"]
981+
vardict[std_name] = vardict.setdefault(std_name, []) + [k]
982+
983+
return {k: sorted(v) for k, v in vardict.items()}
995984

996985
def get_associated_variable_names(self, name: Hashable) -> Dict[str, List[str]]:
997986
"""

cf_xarray/tests/test_accessor.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,34 +33,45 @@ def test_describe(capsys):
3333

3434

3535
def test_axes():
36-
expected = {"T", "X", "Y"}
36+
expected = dict(T=["time"], X=["lon"], Y=["lat"])
3737
actual = airds.cf.axes
3838
assert actual == expected
3939

40-
expected = {"X", "Y"}
40+
expected = dict(X=["nlon"], Y=["nlat"])
4141
actual = popds.cf.axes
4242
assert actual == expected
4343

4444

4545
def test_coordinates():
46-
expected = {"latitude", "longitude", "time"}
46+
expected = dict(latitude=["lat"], longitude=["lon"], time=["time"])
4747
actual = airds.cf.coordinates
4848
assert actual == expected
4949

50-
expected = {"latitude", "longitude"}
50+
expected = dict(latitude=["TLAT", "ULAT"], longitude=["TLONG", "ULONG"])
5151
actual = popds.cf.coordinates
5252
assert actual == expected
5353

5454

55+
def test_cell_measures():
56+
expected = dict(area=["cell_area"])
57+
actual = airds["air"].cf.cell_measures
58+
assert actual == expected
59+
60+
with pytest.raises(AssertionError, match=r"this only works with DataArrays"):
61+
popds.cf.cell_measures
62+
63+
5564
def test_standard_names():
56-
expected = ["air_temperature", "latitude", "longitude", "time"]
65+
expected = dict(
66+
air_temperature=["air"], latitude=["lat"], longitude=["lon"], time=["time"]
67+
)
5768
actual = airds.cf.standard_names
5869
assert actual == expected
5970

6071
dsnew = xr.Dataset()
6172
dsnew["a"] = ("a", np.arange(10), {"standard_name": "a"})
6273
dsnew["b"] = ("a", np.arange(10), {"standard_name": "a"})
63-
assert dsnew.cf.standard_names == ["a"]
74+
assert dsnew.cf.standard_names == dict(a=["a", "b"])
6475

6576

6677
def test_getitem_standard_name():

doc/whats-new.rst

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ What's New
44

55
v0.4.0 (unreleased)
66
===================
7-
- Added ``.axes`` to return available Axis names for an xarray object, ``.coordinates`` for Coordinate names,
8-
``.standard_names`` for standard names, ``.cell_measures`` for cell measures,
9-
and changed ``get_valid_keys()`` to ``.keys()``. `Kristen Thyng`_.
7+
- Added ``.axes`` to return a dictionary mapping available Axis standard names to variable names of an xarray object, ``.coordinates`` for Coordinates,
8+
``.cell_measures`` for Cell Measures, and ``.standard_names`` for all variables. `Kristen Thyng`_ and `Mattia Almansi`_.
9+
- Changed ``get_valid_keys()`` to ``.keys()``. `Kristen Thyng`_.
1010
- Added ``.cf.decode_vertical_coords`` for decoding of parameterized vertical coordinate variables.
1111
(:issue:`34`, :pr:`103`). `Deepak Cherian`_.
1212
- Added top-level ``bounds_to_vertices`` and ``vertices_to_bounds`` as well as ``.cf.bounds_to_vertices``
@@ -61,6 +61,7 @@ v0.1.3
6161

6262
- Support expanding key to multiple dimension names.
6363

64+
.. _`Mattia Almansi`: https://github.com/malmans2
6465
.. _`Anderson Banihirwe`: https://github.com/andersy005
6566
.. _`Pascal Bourgault`: https://github.com/aulemahal
6667
.. _`Deepak Cherian`: https://github.com/dcherian

0 commit comments

Comments
 (0)