Skip to content

Commit dfde8e3

Browse files
malmans2dcherian
andauthored
Improve rename_like (#222)
Co-authored-by: Deepak Cherian <[email protected]>
1 parent 17f37b7 commit dfde8e3

File tree

3 files changed

+71
-27
lines changed

3 files changed

+71
-27
lines changed

cf_xarray/accessor.py

Lines changed: 55 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1326,7 +1326,7 @@ def rename_like(
13261326
Variables will be renamed to match variable names in this xarray object
13271327
skip: str, Iterable[str], optional
13281328
Limit the renaming excluding
1329-
("axes", "cell_measures", "coordinates", "standard_names")
1329+
("axes", "bounds", cell_measures", "coordinates", "standard_names")
13301330
or a subset thereof.
13311331
13321332
Returns
@@ -1341,24 +1341,48 @@ def rename_like(
13411341
good_keys = ourkeys & theirkeys
13421342
keydict = {}
13431343
for key in good_keys:
1344-
ours = set(_get_all(self._obj, key))
1345-
theirs = set(_get_all(other, key))
1344+
ours = set(apply_mapper(_get_all, self._obj, key))
1345+
theirs = set(apply_mapper(_get_all, other, key))
13461346
for attr in skip:
1347-
ours -= set(getattr(self, attr).get(key, []))
1348-
theirs -= set(getattr(other.cf, attr).get(key, []))
1347+
ours.difference_update(getattr(self, attr).get(key, []))
1348+
theirs.difference_update(getattr(other.cf, attr).get(key, []))
13491349
if ours and theirs:
13501350
keydict[key] = dict(ours=list(ours), theirs=list(theirs))
13511351

1352-
conflicts = {}
1353-
for k0, v0 in keydict.items():
1354-
if len(v0["ours"]) > 1 or len(v0["theirs"]) > 1:
1355-
conflicts[k0] = v0
1356-
continue
1357-
for v1 in keydict.values():
1358-
# Conflicts have same ours but different theirs or vice versa
1359-
if (v0["ours"] == v1["ours"]) != (v0["theirs"] == v1["theirs"]):
1352+
def get_renamer_and_conflicts(keydict):
1353+
conflicts = {}
1354+
for k0, v0 in keydict.items():
1355+
if len(v0["ours"]) > 1 or len(v0["theirs"]) > 1:
13601356
conflicts[k0] = v0
1361-
break
1357+
continue
1358+
for v1 in keydict.values():
1359+
# Conflicts have same ours but different theirs or vice versa
1360+
if (v0["ours"] == v1["ours"]) != (v0["theirs"] == v1["theirs"]):
1361+
conflicts[k0] = v0
1362+
break
1363+
1364+
renamer = {
1365+
v["ours"][0]: v["theirs"][0]
1366+
for k, v in keydict.items()
1367+
if k not in conflicts
1368+
}
1369+
1370+
return renamer, conflicts
1371+
1372+
# Run get_renamer_and_conflicts twice.
1373+
# The second time add the bounds associated with variables to rename
1374+
renamer, conflicts = get_renamer_and_conflicts(keydict)
1375+
if "bounds" not in skip:
1376+
for k, v in renamer.items():
1377+
ours = set(getattr(self, "bounds", {}).get(k, []))
1378+
theirs = set(getattr(other.cf, "bounds", {}).get(v, []))
1379+
if ours and theirs:
1380+
ours.update(keydict.get(k, {}).get("ours", []))
1381+
theirs.update(keydict.get(k, {}).get("theirs", []))
1382+
keydict[k] = dict(ours=list(ours), theirs=list(theirs))
1383+
renamer, conflicts = get_renamer_and_conflicts(keydict)
1384+
1385+
# Rename and warn
13621386
if conflicts:
13631387
warnings.warn(
13641388
"Conflicting variables skipped:\n"
@@ -1372,23 +1396,28 @@ def rename_like(
13721396
),
13731397
UserWarning,
13741398
)
1375-
1376-
renamer = {
1377-
v["ours"][0]: v["theirs"][0]
1378-
for k, v in keydict.items()
1379-
if k not in conflicts
1380-
}
13811399
newobj = self._obj.rename(renamer)
13821400

1383-
# rename variable names in the coordinates attribute
1401+
# rename variable names in the attributes
13841402
# if present
13851403
ds = self._maybe_to_dataset(newobj)
13861404
for _, variable in ds.variables.items():
1387-
coordinates = variable.attrs.get("coordinates", None)
1388-
if coordinates:
1389-
for k, v in renamer.items():
1390-
coordinates = coordinates.replace(k, v)
1391-
variable.attrs["coordinates"] = coordinates
1405+
for attr in ("bounds", "coordinates", "cell_measures"):
1406+
if attr == "cell_measures":
1407+
varlist = [
1408+
f"{k}: {renamer.get(v, v)}"
1409+
for k, v in parse_cell_methods_attr(
1410+
variable.attrs.get(attr, "")
1411+
).items()
1412+
]
1413+
else:
1414+
varlist = [
1415+
renamer.get(var, var)
1416+
for var in variable.attrs.get(attr, "").split()
1417+
]
1418+
1419+
if varlist:
1420+
variable.attrs[attr] = " ".join(varlist)
13921421
return self._maybe_to_dataarray(ds)
13931422

13941423
def guess_coord_axis(self, verbose: bool = False) -> Union[DataArray, Dataset]:

cf_xarray/tests/test_accessor.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ def test_rename_like():
241241
for k in ["TLONG", "TLAT"]:
242242
assert k not in renamed.coords
243243
assert k in original.coords
244-
assert original.TEMP.attrs["coordinates"] == "TLONG TLAT"
244+
assert original.TEMP.attrs["coordinates"] == "TLONG TLAT"
245245

246246
assert "lon" in renamed.coords
247247
assert "lat" in renamed.coords
@@ -271,6 +271,20 @@ def test_rename_like():
271271
actual = da.cf.rename_like(airds, skip="axes").cf.coordinates
272272
assert expected == actual
273273

274+
# rename bounds
275+
original = airds.cf[["air"]].cf.add_bounds("lon")
276+
other = popds.cf[["TEMP"]].cf.add_bounds("nlon")
277+
renamed = original.cf.rename_like(other, skip="coordinates")
278+
assert renamed.cf.bounds["nlon"] == ["nlon_bounds"]
279+
280+
# rename cell measures
281+
other = airds.cf["air"].cf.rename(area="CELL_AREA")
282+
other.attrs["cell_measures"] = other.attrs["cell_measures"].replace(
283+
"cell_area", "CELL_AREA"
284+
)
285+
renamed = airds.cf["air"].cf.rename_like(other)
286+
assert renamed.cf.cell_measures["area"] == ["CELL_AREA"]
287+
274288

275289
@pytest.mark.parametrize("obj", objects)
276290
@pytest.mark.parametrize(

doc/whats-new.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ v0.5.3 (unreleased)
77
===================
88
- Begin adding support for units with a unit registry for pint arrays. :pr:`197`.
99
By `Jon Thielen`_ and `Justus Magin`_.
10+
- :py:meth:`Dataset.cf.rename_like` also updates the ``bounds`` and ``cell_measures`` attributes. By `Mattia Almansi`_.
1011

1112
v0.5.2 (May 11, 2021)
1213
=====================

0 commit comments

Comments
 (0)