Skip to content

Commit 2bf15f8

Browse files
dcherianbenbovy
andauthored
Allow opening datasets with nD dimenson coordinate variables. (#7989)
* Warn instead of raising for nD index variable Avoid automatic creating of Index variable when nD variable shares name with one of its dimensions. Closes #2233 * fix tests * Remove warning * Add invariants check Co-authored-by: Benoit Bovy <[email protected]> * Add whats-new --------- Co-authored-by: Benoit Bovy <[email protected]>
1 parent 90d5cd4 commit 2bf15f8

File tree

6 files changed

+24
-21
lines changed

6 files changed

+24
-21
lines changed

doc/whats-new.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ v2023.07.1 (unreleased)
2121

2222
New Features
2323
~~~~~~~~~~~~
24+
- Allow creating Xarray objects where a multidimensional variable shares its name
25+
with a dimension. Examples include output from finite volume models like FVCOM.
26+
(:issue:`2233`, :pull:`7989`)
27+
By `Deepak Cherian <https://github.com/dcherian>`_ and `Benoit Bovy <https://github.com/benbovy>`_.
2428

2529

2630
Breaking changes

xarray/core/indexes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1645,7 +1645,7 @@ def default_indexes(
16451645
coord_names = set(coords)
16461646

16471647
for name, var in coords.items():
1648-
if name in dims:
1648+
if name in dims and var.ndim == 1:
16491649
index, index_vars = create_default_index_implicit(var, coords)
16501650
if set(index_vars) <= coord_names:
16511651
indexes.update({k: index for k in index_vars})

xarray/core/variable.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -156,14 +156,8 @@ def as_variable(obj, name=None) -> Variable | IndexVariable:
156156
f"explicit list of dimensions: {obj!r}"
157157
)
158158

159-
if name is not None and name in obj.dims:
160-
# convert the Variable into an Index
161-
if obj.ndim != 1:
162-
raise MissingDimensionsError(
163-
f"{name!r} has more than 1-dimension and the same name as one of its "
164-
f"dimensions {obj.dims!r}. xarray disallows such variables because they "
165-
"conflict with the coordinates used to label dimensions."
166-
)
159+
if name is not None and name in obj.dims and obj.ndim == 1:
160+
# automatically convert the Variable into an Index
167161
obj = obj.to_index_variable()
168162

169163
return obj

xarray/testing.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -364,14 +364,13 @@ def _assert_dataset_invariants(ds: Dataset, check_default_indexes: bool):
364364
assert all(
365365
ds._dims[k] == v.sizes[k] for v in ds._variables.values() for k in v.sizes
366366
), (ds._dims, {k: v.sizes for k, v in ds._variables.items()})
367-
assert all(
368-
isinstance(v, IndexVariable)
369-
for (k, v) in ds._variables.items()
370-
if v.dims == (k,)
371-
), {k: type(v) for k, v in ds._variables.items() if v.dims == (k,)}
372-
assert all(v.dims == (k,) for (k, v) in ds._variables.items() if k in ds._dims), {
373-
k: v.dims for k, v in ds._variables.items() if k in ds._dims
374-
}
367+
368+
if check_default_indexes:
369+
assert all(
370+
isinstance(v, IndexVariable)
371+
for (k, v) in ds._variables.items()
372+
if v.dims == (k,)
373+
), {k: type(v) for k, v in ds._variables.items() if v.dims == (k,)}
375374

376375
if ds._indexes is not None:
377376
_assert_indexes_invariants_checks(

xarray/tests/test_dataset.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
from xarray.core.indexes import Index, PandasIndex
3636
from xarray.core.pycompat import array_type, integer_types
3737
from xarray.core.utils import is_scalar
38+
from xarray.testing import _assert_internal_invariants
3839
from xarray.tests import (
3940
DuckArrayWrapper,
4041
InaccessibleArray,
@@ -467,13 +468,16 @@ def test_constructor(self) -> None:
467468

468469
with pytest.raises(ValueError, match=r"conflicting sizes"):
469470
Dataset({"a": x1, "b": x2})
470-
with pytest.raises(ValueError, match=r"disallows such variables"):
471-
Dataset({"a": x1, "x": z})
472471
with pytest.raises(TypeError, match=r"tuple of form"):
473472
Dataset({"x": (1, 2, 3, 4, 5, 6, 7)})
474473
with pytest.raises(ValueError, match=r"already exists as a scalar"):
475474
Dataset({"x": 0, "y": ("x", [1, 2, 3])})
476475

476+
# nD coordinate variable "x" sharing name with dimension
477+
actual = Dataset({"a": x1, "x": z})
478+
assert "x" not in actual.xindexes
479+
_assert_internal_invariants(actual, check_default_indexes=True)
480+
477481
# verify handling of DataArrays
478482
expected = Dataset({"x": x1, "z": z})
479483
actual = Dataset({"z": expected["z"]})

xarray/tests/test_variable.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1247,8 +1247,10 @@ def test_as_variable(self):
12471247
expected = Variable(("x", "y"), data)
12481248
with pytest.raises(ValueError, match=r"without explicit dimension names"):
12491249
as_variable(data, name="x")
1250-
with pytest.raises(ValueError, match=r"has more than 1-dimension"):
1251-
as_variable(expected, name="x")
1250+
1251+
# name of nD variable matches dimension name
1252+
actual = as_variable(expected, name="x")
1253+
assert_identical(expected, actual)
12521254

12531255
# test datetime, timedelta conversion
12541256
dt = np.array([datetime(1999, 1, 1) + timedelta(days=x) for x in range(10)])

0 commit comments

Comments
 (0)