Skip to content

Commit e0b8690

Browse files
authored
Add guess_coord_axis. (#67)
* Add guess_coord_axis. * small doc updates
1 parent b01fd9f commit e0b8690

File tree

4 files changed

+120
-11
lines changed

4 files changed

+120
-11
lines changed

cf_xarray/accessor.py

Lines changed: 87 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -92,17 +92,6 @@
9292
"degreesE",
9393
),
9494
},
95-
# "regular_expression": {
96-
# "time": r"time[0-9]*",
97-
# "vertical": (
98-
# r"(lv_|bottom_top|sigma|h(ei)?ght|altitude|depth|isobaric|pres|"
99-
# r"isotherm)[a-z_]*[0-9]*"
100-
# ),
101-
# "y": r"y",
102-
# "latitude": r"x?lat[a-z0-9]*",
103-
# "x": r"x",
104-
# "longitude": r"x?lon[a-z0-9]*",
105-
# },
10695
}
10796

10897
# "vertical" is just an alias for "Z"
@@ -112,6 +101,54 @@
112101
# "long_name" and "standard_name" criteria are the same. For convenience.
113102
coordinate_criteria["long_name"] = coordinate_criteria["standard_name"]
114103

104+
105+
#: regular expressions for guess_coord_axis
106+
regex = {
107+
"time": "time[0-9]*",
108+
"vertical": (
109+
"(lv_|bottom_top|sigma|h(ei)?ght|altitude|depth|isobaric|pres|"
110+
"isotherm)[a-z_]*[0-9]*"
111+
),
112+
"Y": "y",
113+
"latitude": "y?lat[a-z0-9]*",
114+
"X": "x",
115+
"longitude": "x?lon[a-z0-9]*",
116+
}
117+
regex["Z"] = regex["vertical"]
118+
regex["T"] = regex["time"]
119+
120+
121+
attrs = {
122+
"X": {"axis": "X"},
123+
"T": {"axis": "T", "standard_name": "time"},
124+
"Y": {"axis": "Y"},
125+
"Z": {"axis": "Z"},
126+
"latitude": {"units": "degrees_north", "standard_name": "latitude"},
127+
"longitude": {"units": "degrees_east", "standard_name": "longitude"},
128+
}
129+
attrs["time"] = attrs["T"]
130+
attrs["vertical"] = attrs["Z"]
131+
132+
133+
def _is_datetime_like(da: DataArray) -> bool:
134+
import numpy as np
135+
136+
if np.issubdtype(da.dtype, np.datetime64) or np.issubdtype(
137+
da.dtype, np.timedelta64
138+
):
139+
return True
140+
141+
try:
142+
import cftime
143+
144+
if isinstance(da.data[0], cftime.datetime):
145+
return True
146+
except ImportError:
147+
pass
148+
149+
return False
150+
151+
115152
# Type for Mapper functions
116153
Mapper = Callable[[Union[DataArray, Dataset], str], List[str]]
117154

@@ -1090,6 +1127,45 @@ def rename_like(
10901127
variable.attrs["coordinates"] = coordinates
10911128
return self._maybe_to_dataarray(ds)
10921129

1130+
def guess_coord_axis(self, verbose: bool = False) -> Union[DataArray, Dataset]:
1131+
"""
1132+
Automagically guesses X, Y, Z, T, latitude, longitude, and adds
1133+
appropriate attributes. Uses regexes from Metpy and inspired by Iris
1134+
function of same name.
1135+
1136+
Existing attributes will not be modified.
1137+
1138+
Parameters
1139+
----------
1140+
verbose: bool
1141+
Print extra info to screen
1142+
1143+
Returns
1144+
-------
1145+
DataArray or Dataset with appropriate attributes added
1146+
"""
1147+
import re
1148+
1149+
obj = self._obj.copy(deep=True)
1150+
for dim in obj.dims:
1151+
if _is_datetime_like(obj[dim]):
1152+
if verbose:
1153+
print(
1154+
f"I think {dim!r} is of type 'time' since it has a datetime-like type."
1155+
)
1156+
obj[dim].attrs = dict(ChainMap(obj[dim].attrs, attrs["time"]))
1157+
continue # prevent second detection
1158+
1159+
for axis, pattern in regex.items():
1160+
# match variable names
1161+
if re.match(pattern, dim.lower()):
1162+
if verbose:
1163+
print(
1164+
f"I think {dim!r} is of type {axis!r} since it matched {pattern!r}"
1165+
)
1166+
obj[dim].attrs = dict(ChainMap(obj[dim].attrs, attrs[axis]))
1167+
return obj
1168+
10931169

10941170
@xr.register_dataset_accessor("cf")
10951171
class CFDatasetAccessor(CFAccessor):

cf_xarray/tests/test_accessor.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,3 +402,25 @@ def test_add_bounds(obj, dims):
402402
def test_docstring():
403403
assert "One of ('X'" in airds.cf.groupby.__doc__
404404
assert "One or more of ('X'" in airds.cf.mean.__doc__
405+
406+
407+
def test_guess_axis_coord():
408+
ds = xr.Dataset()
409+
ds["time"] = ("time", pd.date_range("2001-01-01", "2001-04-01"))
410+
ds["lon_rho"] = ("lon_rho", [1, 2, 3, 4, 5])
411+
ds["lat_rho"] = ("lat_rho", [1, 2, 3, 4, 5])
412+
ds["x1"] = ("x1", [1, 2, 3, 4, 5])
413+
ds["y1"] = ("y1", [1, 2, 3, 4, 5])
414+
415+
dsnew = ds.cf.guess_coord_axis()
416+
assert dsnew.time.attrs == {"standard_name": "time", "axis": "T"}
417+
assert dsnew.lon_rho.attrs == {
418+
"standard_name": "longitude",
419+
"units": "degrees_east",
420+
}
421+
assert dsnew.lat_rho.attrs == {
422+
"standard_name": "latitude",
423+
"units": "degrees_north",
424+
}
425+
assert dsnew.x1.attrs == {"axis": "X"}
426+
assert dsnew.y1.attrs == {"axis": "Y"}

doc/api.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ DataArray
2020
DataArray.cf.describe
2121
DataArray.cf.get_standard_names
2222
DataArray.cf.get_valid_keys
23+
DataArray.cf.guess_coord_axis
2324
DataArray.cf.rename_like
2425

2526
Dataset
@@ -37,4 +38,5 @@ Dataset
3738
Dataset.cf.describe
3839
Dataset.cf.get_standard_names
3940
Dataset.cf.get_valid_keys
41+
Dataset.cf.guess_coord_axis
4042
Dataset.cf.rename_like

doc/whats-new.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
What's New
22
----------
3+
v0.2.1
4+
======
5+
6+
- Add ``.cf.guess_coord_axis`` to automagically guess axis and coord names, and add
7+
appropriate attributes. (:pr:`67`, :issue:`46`). `Deepak Cherian`_.
38

49
v0.2.0 (Jul 28, 2020)
510
=====================
611

12+
``cf_xarray`` is now available on conda-forge. Thanks to `Anderson Banihirwe`_ and `Filipe Fernandes`_
13+
714
- Remap datetime accessor syntax for groupby. E.g. ``.cf.groupby("T.month")`` → ``.cf.groupby("ocean_time.month")``.
815
(:pr:`64`, :issue:`6`). `Julia Kent`_.
916
- Added ``.cf.rename_like`` to rename matching variables. Only coordinate variables
@@ -33,5 +40,7 @@ v0.1.3
3340

3441
- Support expanding key to multiple dimension names.
3542

43+
.. _`Anderson Banihirwe`: https://github.com/andersy005
3644
.. _`Deepak Cherian`: https://github.com/dcherian
45+
.. _`Filipe Fernandes`: https://github.com/ocefpaf
3746
.. _`Julia Kent`: https://github.com/jukent

0 commit comments

Comments
 (0)