Skip to content

Commit 9803061

Browse files
Avoid overlapping bounds and/or gaps when points are irregularly spaced (#622)
Co-authored-by: Deepak Cherian <dcherian@users.noreply.github.com>
1 parent 017306f commit 9803061

2 files changed

Lines changed: 41 additions & 10 deletions

File tree

cf_xarray/helpers.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,16 @@ def _guess_bounds_1d(da, dim):
2323
da = da.assign_coords({dim: da[dim]})
2424
ADDED_INDEX = True
2525

26-
diff = da.diff(dim)
27-
lower = da - diff / 2
28-
upper = da + diff / 2
29-
bounds = xr.concat([lower, upper], dim="bounds")
26+
bound_position = 0.5
27+
diff = da.diff(dim).pad({dim: (1, 1)}, mode="edge").reset_index(dim)
28+
lower = (
29+
da.reset_index(dim) - bound_position * diff.isel({dim: slice(0, -1)})
30+
).assign_coords({dim: da[dim]})
31+
upper = (
32+
da.reset_index(dim) + bound_position * diff.isel({dim: slice(1, None)})
33+
).assign_coords({dim: da[dim]})
34+
result = xr.concat([lower, upper], dim="bounds").transpose(..., "bounds")
3035

31-
first = (bounds.isel({dim: 0}) - diff.isel({dim: 0})).assign_coords(
32-
{dim: da[dim][0]}
33-
)
34-
result = xr.concat([first, bounds], dim=dim).transpose(..., "bounds")
3536
if ADDED_INDEX:
3637
result = result.drop_vars(dim)
3738
return result.drop_attrs(deep=False)

cf_xarray/tests/test_accessor.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,36 @@ def test_add_bounds(dims):
839839
_check_unchanged(original, ds)
840840

841841

842+
def test_add_irregularly_spaced_bounds_do_not_overlap() -> None:
843+
# Test that added bounds with irregular spacing do not overlap.
844+
ds = airds
845+
original = ds.copy(deep=True)
846+
ds["time"] = ds["time"].copy(data=pd.date_range("2013-01", "2013-04", freq="MS"))
847+
expected = xr.DataArray(
848+
data=[
849+
pd.to_datetime(t)
850+
for t in [
851+
["2012-12-16T12", "2013-01-16T12"],
852+
["2013-01-16T12", "2013-02-15T00"],
853+
["2013-02-15T00", "2013-03-16T12"],
854+
["2013-03-16T12", "2013-04-16T12"],
855+
]
856+
],
857+
dims=["time", "bounds"],
858+
name="time_bounds",
859+
coords={"time": ds.time.data},
860+
)
861+
added = ds.copy(deep=False)
862+
added = added.cf.add_bounds("time")
863+
864+
name = "time_bounds"
865+
assert name in added.coords
866+
assert added["time"].attrs["bounds"] == name
867+
assert_allclose(added[name].reset_coords(drop=True), expected)
868+
869+
_check_unchanged(original, ds)
870+
871+
842872
def test_add_bounds_multiple() -> None:
843873
# Test multiple dimensions
844874
assert not {"x1_bounds", "x2_bounds"} <= set(multiple.variables)
@@ -882,11 +912,11 @@ def test_add_bounds_nd_variable() -> None:
882912
expected = (
883913
xr.concat([ds.z - 1.5, ds.z + 1.5], dim="bounds")
884914
.rename("z_bounds")
885-
.transpose("bounds", "y", "x")
915+
.transpose(..., "bounds")
886916
)
887917

888918
actual = ds.cf.add_bounds("z", dim="x").z_bounds.reset_coords(drop=True)
889-
xr.testing.assert_identical(expected.transpose(..., "bounds"), actual)
919+
xr.testing.assert_identical(expected, actual)
890920

891921
# Requesting bounds on a non-variable dimension
892922
with pytest.raises(ValueError, match="are dimensions with no index."):

0 commit comments

Comments
 (0)