From b56b06614659abd69a6bfa47fff99d199a2ca5c3 Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Mon, 22 Sep 2025 15:53:40 -0400 Subject: [PATCH 1/6] Order coords to match dims in repr --- xarray/core/formatting.py | 6 +++++- xarray/core/formatting_html.py | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/xarray/core/formatting.py b/xarray/core/formatting.py index 3a06cf18542..a7e379eee94 100644 --- a/xarray/core/formatting.py +++ b/xarray/core/formatting.py @@ -446,8 +446,12 @@ def _mapping_repr( def coords_repr(coords: AbstractCoordinates, col_width=None, max_rows=None): if col_width is None: col_width = _calculate_col_width(coords) + dims = tuple(coords._data.dims) + dim_ordered_coords = sorted( + coords.items(), key=lambda x: dims.index(x[0]) if x[0] in dims else len(dims) + ) return _mapping_repr( - coords, + dict(dim_ordered_coords), title="Coordinates", summarizer=summarize_variable, expand_option_name="display_expand_coords", diff --git a/xarray/core/formatting_html.py b/xarray/core/formatting_html.py index 46c6709d118..9d7d0b14ab1 100644 --- a/xarray/core/formatting_html.py +++ b/xarray/core/formatting_html.py @@ -116,7 +116,11 @@ def summarize_variable(name, var, is_index=False, dtype=None) -> str: def summarize_coords(variables) -> str: li_items = [] - for k, v in variables.items(): + dims = tuple(variables._data.dims) + dim_ordered_coords = sorted( + variables.items(), key=lambda x: dims.index(x[0]) if x[0] in dims else len(dims) + ) + for k, v in dim_ordered_coords: li_content = summarize_variable(k, v, is_index=k in variables.xindexes) li_items.append(f"
  • {li_content}
  • ") From 89d16bcaee0ca9333f9b7728a5e0d7cca99316d1 Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Mon, 22 Sep 2025 17:21:10 -0400 Subject: [PATCH 2/6] Update docstrings with new dim-ordered coords --- xarray/core/common.py | 4 ++-- xarray/core/dataarray.py | 16 ++++++++-------- xarray/core/dataset.py | 24 ++++++++++++------------ xarray/core/groupby.py | 10 +++++----- xarray/structure/concat.py | 6 +++--- 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/xarray/core/common.py b/xarray/core/common.py index b4a2dc1104f..ec61024046c 100644 --- a/xarray/core/common.py +++ b/xarray/core/common.py @@ -619,9 +619,9 @@ def assign_coords( Size: 360B Dimensions: (x: 2, y: 2, time: 4) Coordinates: + * time (time) datetime64[ns] 32B 2014-09-06 ... 2014-09-09 lon (x, y) float64 32B 260.2 260.7 260.2 260.8 lat (x, y) float64 32B 42.25 42.21 42.63 42.59 - * time (time) datetime64[ns] 32B 2014-09-06 ... 2014-09-09 reference_time datetime64[ns] 8B 2014-09-05 Dimensions without coordinates: x, y Data variables: @@ -633,9 +633,9 @@ def assign_coords( Size: 360B Dimensions: (x: 2, y: 2, time: 4) Coordinates: + * time (time) datetime64[ns] 32B 2014-09-06 ... 2014-09-09 lon (x, y) float64 32B -99.83 -99.32 -99.79 -99.23 lat (x, y) float64 32B 42.25 42.21 42.63 42.59 - * time (time) datetime64[ns] 32B 2014-09-06 ... 2014-09-09 reference_time datetime64[ns] 8B 2014-09-05 Dimensions without coordinates: x, y Data variables: diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index d6f5ba149ca..67c09653c0c 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -368,9 +368,9 @@ class DataArray( [[22.60070734, 13.78914233, 14.17424919], [18.28478802, 16.15234857, 26.63418806]]]) Coordinates: + * time (time) datetime64[ns] 24B 2014-09-06 2014-09-07 2014-09-08 lon (x, y) float64 32B -99.83 -99.32 -99.79 -99.23 lat (x, y) float64 32B 42.25 42.21 42.63 42.59 - * time (time) datetime64[ns] 24B 2014-09-06 2014-09-07 2014-09-08 reference_time datetime64[ns] 8B 2014-09-05 Dimensions without coordinates: x, y Attributes: @@ -2139,8 +2139,8 @@ def reindex_like( Size: 24B array([3, 4, 5]) Coordinates: - x int64 8B 20 * y (y) int64 24B 70 80 90 + x int64 8B 20 ...so ``b`` in not added here: @@ -2148,8 +2148,8 @@ def reindex_like( Size: 24B array([3, 4, 5]) Coordinates: - x int64 8B 20 * y (y) int64 24B 70 80 90 + x int64 8B 20 See Also -------- @@ -2363,8 +2363,8 @@ def interp( [3. , nan, 5.75, nan], [5. , nan, 5.25, nan]]) Coordinates: - * y (y) int64 32B 10 12 14 16 * x (x) float64 32B 0.0 0.75 1.25 1.75 + * y (y) int64 32B 10 12 14 16 1D nearest interpolation: @@ -2375,8 +2375,8 @@ def interp( [ 2., 7., 6., nan], [ 6., nan, 5., 8.]]) Coordinates: - * y (y) int64 32B 10 12 14 16 * x (x) float64 32B 0.0 0.75 1.25 1.75 + * y (y) int64 32B 10 12 14 16 1D linear extrapolation: @@ -2391,8 +2391,8 @@ def interp( [ 8. , nan, 4.5, nan], [12. , nan, 3.5, nan]]) Coordinates: - * y (y) int64 32B 10 12 14 16 * x (x) float64 32B 1.0 1.5 2.5 3.5 + * y (y) int64 32B 10 12 14 16 2D linear interpolation: @@ -2639,8 +2639,8 @@ def swap_dims( Size: 16B array([0, 1]) Coordinates: - x (y) >> arr.swap_dims({"x": "z"}) Size: 16B @@ -5368,8 +5368,8 @@ def quantile( [3.6 , 5.75, 6. , 1.7 ], [6.5 , 7.3 , 9.4 , 1.9 ]]) Coordinates: - * y (y) float64 32B 1.0 1.5 2.0 2.5 * quantile (quantile) float64 24B 0.0 0.5 1.0 + * y (y) float64 32B 1.0 1.5 2.0 2.5 References ---------- diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index 5f00fdc2f03..b185a4d83c7 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -315,10 +315,10 @@ class Dataset( Size: 552B Dimensions: (loc: 2, instrument: 3, time: 4) Coordinates: - lon (loc) float64 16B -99.83 -99.32 - lat (loc) float64 16B 42.25 42.21 * instrument (instrument) Self: Size: 48B Dimensions: (time: 3) Coordinates: - pressure (time) float64 24B 1.013 1.2 3.5 * time (time) datetime64[ns] 24B 2023-01-01 2023-01-02 2023-01-03 + pressure (time) float64 24B 1.013 1.2 3.5 Data variables: *empty* @@ -3799,8 +3799,8 @@ def interp( Size: 224B Dimensions: (x: 4, y: 4) Coordinates: - * y (y) int64 32B 10 12 14 16 * x (x) float64 32B 0.0 0.75 1.25 1.75 + * y (y) int64 32B 10 12 14 16 Data variables: a (x) float64 32B 5.0 6.5 6.25 4.75 b (x, y) float64 128B 1.0 4.0 2.0 nan 1.75 ... nan 5.0 nan 5.25 nan @@ -3811,8 +3811,8 @@ def interp( Size: 224B Dimensions: (x: 4, y: 4) Coordinates: - * y (y) int64 32B 10 12 14 16 * x (x) float64 32B 0.0 0.75 1.25 1.75 + * y (y) int64 32B 10 12 14 16 Data variables: a (x) float64 32B 5.0 7.0 7.0 4.0 b (x, y) float64 128B 1.0 4.0 2.0 9.0 2.0 7.0 ... nan 6.0 nan 5.0 8.0 @@ -3827,8 +3827,8 @@ def interp( Size: 224B Dimensions: (x: 4, y: 4) Coordinates: - * y (y) int64 32B 10 12 14 16 * x (x) float64 32B 1.0 1.5 2.5 3.5 + * y (y) int64 32B 10 12 14 16 Data variables: a (x) float64 32B 7.0 5.5 2.5 -0.5 b (x, y) float64 128B 2.0 7.0 6.0 nan 4.0 ... nan 12.0 nan 3.5 nan @@ -4352,8 +4352,8 @@ def swap_dims( Size: 56B Dimensions: (y: 2) Coordinates: - x (y) Size: 152B Dimensions: (quantile: 3, y: 4) Coordinates: - * y (y) float64 32B 1.0 1.5 2.0 2.5 * quantile (quantile) float64 24B 0.0 0.5 1.0 + * y (y) float64 32B 1.0 1.5 2.0 2.5 Data variables: a (quantile, y) float64 96B 0.7 4.2 2.6 1.5 3.6 ... 6.5 7.3 9.4 1.9 @@ -8673,9 +8673,9 @@ def filter_by_attrs(self, **kwargs) -> Self: Size: 192B Dimensions: (x: 2, y: 2, time: 3) Coordinates: + * time (time) datetime64[ns] 24B 2014-09-06 2014-09-07 2014-09-08 lon (x, y) float64 32B -99.83 -99.32 -99.79 -99.23 lat (x, y) float64 32B 42.25 42.21 42.63 42.59 - * time (time) datetime64[ns] 24B 2014-09-06 2014-09-07 2014-09-08 reference_time datetime64[ns] 8B 2014-09-05 Dimensions without coordinates: x, y Data variables: @@ -8688,9 +8688,9 @@ def filter_by_attrs(self, **kwargs) -> Self: Size: 288B Dimensions: (x: 2, y: 2, time: 3) Coordinates: + * time (time) datetime64[ns] 24B 2014-09-06 2014-09-07 2014-09-08 lon (x, y) float64 32B -99.83 -99.32 -99.79 -99.23 lat (x, y) float64 32B 42.25 42.21 42.63 42.59 - * time (time) datetime64[ns] 24B 2014-09-06 2014-09-07 2014-09-08 reference_time datetime64[ns] 8B 2014-09-05 Dimensions without coordinates: x, y Data variables: @@ -9361,8 +9361,8 @@ def argmin(self, dim: Hashable | None = None, **kwargs) -> Self: Size: 84B array(['Bob', 'Bob', 'Alice'], dtype='>> min_score_in_english = dataset["student"].isel( ... student=argmin_indices["english_scores"] @@ -9371,8 +9371,8 @@ def argmin(self, dim: Hashable | None = None, **kwargs) -> Self: Size: 84B array(['Charlie', 'Bob', 'Charlie'], dtype=' T_Xarray: Size: 32B array([9., 3., 4., 5.]) Coordinates: - quantile float64 8B 0.5 * x (x) int64 32B 0 1 2 3 + quantile float64 8B 0.5 See Also -------- @@ -1313,15 +1313,15 @@ def quantile( array([[0.7, 4.2, 0.7, 1.5], [6.5, 7.3, 2.6, 1.9]]) Coordinates: + * x (x) int64 16B 0 1 * y (y) int64 32B 1 1 2 2 quantile float64 8B 0.0 - * x (x) int64 16B 0 1 >>> ds.groupby("y").quantile(0, dim=...) Size: 40B Dimensions: (y: 2) Coordinates: - quantile float64 8B 0.0 * y (y) int64 16B 1 2 + quantile float64 8B 0.0 Data variables: a (y) float64 16B 0.7 0.7 >>> da.groupby("x").quantile([0, 0.5, 1]) @@ -1336,15 +1336,15 @@ def quantile( [2.6 , 2.6 , 2.6 ], [1.9 , 1.9 , 1.9 ]]]) Coordinates: + * x (x) int64 16B 0 1 * y (y) int64 32B 1 1 2 2 * quantile (quantile) float64 24B 0.0 0.5 1.0 - * x (x) int64 16B 0 1 >>> ds.groupby("y").quantile([0, 0.5, 1], dim=...) Size: 88B Dimensions: (y: 2, quantile: 3) Coordinates: - * quantile (quantile) float64 24B 0.0 0.5 1.0 * y (y) int64 16B 1 2 + * quantile (quantile) float64 24B 0.0 0.5 1.0 Data variables: a (y, quantile) float64 48B 0.7 5.35 8.4 0.7 2.25 9.4 diff --git a/xarray/structure/concat.py b/xarray/structure/concat.py index f732502d5c6..fbc90ff6a50 100644 --- a/xarray/structure/concat.py +++ b/xarray/structure/concat.py @@ -224,8 +224,8 @@ def concat( array([[0, 1, 2], [3, 4, 5]]) Coordinates: - x (new_dim) >> xr.concat( @@ -237,9 +237,9 @@ def concat( array([[0, 1, 2], [3, 4, 5]]) Coordinates: - x (new_dim) Date: Wed, 24 Sep 2025 08:59:58 -0400 Subject: [PATCH 3/6] Add tests for _repr_html_ coord ordering --- xarray/tests/test_formatting_html.py | 50 +++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/xarray/tests/test_formatting_html.py b/xarray/tests/test_formatting_html.py index 4af9c69a908..5786de2e86c 100644 --- a/xarray/tests/test_formatting_html.py +++ b/xarray/tests/test_formatting_html.py @@ -43,7 +43,7 @@ def dataset() -> xr.Dataset: "tmin": (("time", "location"), tmin_values), "tmax": (("time", "location"), tmax_values), }, - {"time": times, "location": ["", "IN", "IL"]}, + {"location": ["", "IN", "IL"], "time": times}, attrs={"description": "Test data."}, ) @@ -121,6 +121,43 @@ def test_repr_of_dataarray(dataarray: xr.DataArray) -> None: ) +def test_repr_coords_order_of_datarray() -> None: + da1 = xr.DataArray( + np.empty( + ( + 2, + 2, + ) + ), + coords={"foo": [0, 1], "bar": [0, 1]}, + dims=["foo", "bar"], + ) + da2 = xr.DataArray( + np.empty( + ( + 2, + 2, + ) + ), + coords={"bar": [0, 1], "foo": [0, 1]}, + dims=["bar", "foo"], + ) + ds = xr.Dataset({"da1": da1, "da2": da2}) + + bar_line = ( + "bar
    (bar)" + ) + foo_line = ( + "foo
    (foo)" + ) + + formatted_da1 = fh.array_repr(ds.da1) + assert formatted_da1.index(foo_line) < formatted_da1.index(bar_line) + + formatted_da2 = fh.array_repr(ds.da2) + assert formatted_da2.index(bar_line) < formatted_da2.index(foo_line) + + def test_repr_of_multiindex(multiindex: xr.Dataset) -> None: formatted = fh.dataset_repr(multiindex) assert "(x)" in formatted @@ -160,6 +197,17 @@ def test_repr_text_fallback(dataset: xr.Dataset) -> None: assert "
    " in formatted
     
     
    +def test_repr_coords_order_of_dataset() -> None:
    +    ds = xr.Dataset()
    +    ds.coords["as"] = 10
    +    ds["var"] = xr.DataArray(np.ones((10,)), dims="x", coords={"x": np.arange(10)})
    +    formatted = fh.dataset_repr(ds)
    +
    +    x_line = "x
    (x)" + as_line = "as
    ()" + assert formatted.index(x_line) < formatted.index(as_line) + + def test_variable_repr_html() -> None: v = xr.Variable(["time", "x"], [[1, 2, 3], [4, 5, 6]], {"foo": "bar"}) assert hasattr(v, "_repr_html_") From 319018c806ce509e64f1b672cf8214f0e0c79cb7 Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Wed, 24 Sep 2025 10:08:46 -0400 Subject: [PATCH 4/6] Update what's new --- doc/whats-new.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/whats-new.rst b/doc/whats-new.rst index 1d6a986ceb2..88db5c44294 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -18,6 +18,9 @@ New Features (:pull:`10624`). By `Stephan Hoyer `_. +- Coordinates are ordered to match dims when displaying Xarray objects. (:pull:`10778`). + By `Julia Signell `_. + Breaking changes ~~~~~~~~~~~~~~~~ From 54a86d7a30533fccdf80db1ff01f89631941435c Mon Sep 17 00:00:00 2001 From: Illviljan <14371165+Illviljan@users.noreply.github.com> Date: Fri, 26 Sep 2025 20:31:11 +0200 Subject: [PATCH 5/6] Apply suggestions from code review --- xarray/tests/test_formatting_html.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xarray/tests/test_formatting_html.py b/xarray/tests/test_formatting_html.py index 5786de2e86c..c0a61c0b692 100644 --- a/xarray/tests/test_formatting_html.py +++ b/xarray/tests/test_formatting_html.py @@ -126,7 +126,7 @@ def test_repr_coords_order_of_datarray() -> None: np.empty( ( 2, - 2, + 2 ) ), coords={"foo": [0, 1], "bar": [0, 1]}, @@ -136,7 +136,7 @@ def test_repr_coords_order_of_datarray() -> None: np.empty( ( 2, - 2, + 2 ) ), coords={"bar": [0, 1], "foo": [0, 1]}, From 2ef43f7497fa110b8d5f8f1fb121e8819d421070 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 26 Sep 2025 18:34:43 +0000 Subject: [PATCH 6/6] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- xarray/tests/test_formatting_html.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/xarray/tests/test_formatting_html.py b/xarray/tests/test_formatting_html.py index c0a61c0b692..6bd2fd01610 100644 --- a/xarray/tests/test_formatting_html.py +++ b/xarray/tests/test_formatting_html.py @@ -123,22 +123,12 @@ def test_repr_of_dataarray(dataarray: xr.DataArray) -> None: def test_repr_coords_order_of_datarray() -> None: da1 = xr.DataArray( - np.empty( - ( - 2, - 2 - ) - ), + np.empty((2, 2)), coords={"foo": [0, 1], "bar": [0, 1]}, dims=["foo", "bar"], ) da2 = xr.DataArray( - np.empty( - ( - 2, - 2 - ) - ), + np.empty((2, 2)), coords={"bar": [0, 1], "foo": [0, 1]}, dims=["bar", "foo"], )