From 81f6da890bb957b335f5f29496e25a557bc5e25b Mon Sep 17 00:00:00 2001 From: Vincent Gao Date: Mon, 1 Jun 2026 14:14:37 +0200 Subject: [PATCH] Fix Cube.rolling_window with lazy auxiliary coordinates rolling_window built each window's coordinate bounds with new_bounds[:, (0, -1)]. A tuple index is treated by Dask as a multidimensional index, so a lazy coordinate along the windowed dimension raised a TypeError. Index with the list [0, -1] instead, which numpy and Dask both treat as selecting the first and last column, giving an identical (and still lazy) result. Fixes #6480. --- docs/src/whatsnew/latest.rst | 4 +++- lib/iris/cube.py | 7 +++++-- lib/iris/tests/unit/cube/test_Cube.py | 16 ++++++++++++++++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/docs/src/whatsnew/latest.rst b/docs/src/whatsnew/latest.rst index 825508dca3..e8bdb43243 100644 --- a/docs/src/whatsnew/latest.rst +++ b/docs/src/whatsnew/latest.rst @@ -36,7 +36,9 @@ This document explains the changes made to Iris for this release 🐛 Bugs Fixed ============= -#. N/A +#. :user:`gaoflow` fixed :meth:`iris.cube.Cube.rolling_window` so that it no + longer raises a ``TypeError`` when the cube has a lazy auxiliary coordinate + along the windowed dimension. (:issue:`6480`) 💣 Incompatible Changes diff --git a/lib/iris/cube.py b/lib/iris/cube.py index 44be3a63d7..f5d12d8e78 100644 --- a/lib/iris/cube.py +++ b/lib/iris/cube.py @@ -5243,12 +5243,15 @@ def rolling_window( # window and the bounds are the first and last points in the # window as with numeric coordinates. new_points = np.apply_along_axis(lambda x: "|".join(x), -1, new_bounds) - new_bounds = new_bounds[:, (0, -1)] + # Index with a list rather than a tuple: a tuple is interpreted + # as a multidimensional index by Dask, so lazy coordinates would + # otherwise fail here (see #6480). + new_bounds = new_bounds[:, [0, -1]] else: # Take the first and last element of the rolled window (i.e. # the bounds) and the new points are the midpoints of these # bounds. - new_bounds = new_bounds[:, (0, -1)] + new_bounds = new_bounds[:, [0, -1]] new_points = np.mean(new_bounds, axis=-1) # wipe the coords points and set the bounds diff --git a/lib/iris/tests/unit/cube/test_Cube.py b/lib/iris/tests/unit/cube/test_Cube.py index d91c7e81c0..09aebd29cf 100644 --- a/lib/iris/tests/unit/cube/test_Cube.py +++ b/lib/iris/tests/unit/cube/test_Cube.py @@ -1086,6 +1086,22 @@ def test_lazy(self): ) _shared_utils.assert_masked_array_equal(expected_result, res_cube.data) + def test_lazy_aux_coord(self): + # A lazy aux-coord paralleling the rolled dimension must not cause a + # failure, and should give the same result as a real one, staying lazy + # (see #6480). + self.cube.add_aux_coord(AuxCoord(da.arange(6), long_name="lazy_extra"), 0) + res_cube = self.cube.rolling_window("val", iris.analysis.MEAN, 3) + result_coord = res_cube.coord("lazy_extra") + assert result_coord.has_lazy_points() + assert result_coord.has_lazy_bounds() + expected = AuxCoord( + np.array([1.0, 2.0, 3.0, 4.0]), + bounds=np.array([[0, 2], [1, 3], [2, 4], [3, 5]]), + long_name="lazy_extra", + ) + assert result_coord == expected + def test_ancillary_variables_and_cell_measures_kept(self, dataless): if dataless: self.cube.data = None