From 512250da1ce15187ca96e4ffd572931d7e1d617e Mon Sep 17 00:00:00 2001 From: Will Benfold Date: Thu, 20 Jan 2022 14:39:09 +0000 Subject: [PATCH 1/5] Removed iris.util.as_compatible_shape and now unused imports --- docs/src/whatsnew/latest.rst | 6 ++ lib/iris/util.py | 129 ----------------------------------- 2 files changed, 6 insertions(+), 129 deletions(-) diff --git a/docs/src/whatsnew/latest.rst b/docs/src/whatsnew/latest.rst index f4d6d6ab42..0fd091d249 100644 --- a/docs/src/whatsnew/latest.rst +++ b/docs/src/whatsnew/latest.rst @@ -200,6 +200,12 @@ This document explains the changes made to Iris for this release deprecation warnings invite users to contact the Iris Developers if this isn't the case. (:pull:`4515`) +#. `@wjbenfold`_ removed the :func:`iris.util.as_compatible_shape` function, + which was deprecated in ``v3.0``. Instead use + :class:`iris.common.resolve.Resolve`. For example, rather than calling + ``as_compatible_shape(src_cube, target_cube)`` replace with + ``Resolve(src_cube, target_cube)(target_cube.core_data())``. (:pull:`XXXX`) + 🔗 Dependencies =============== diff --git a/lib/iris/util.py b/lib/iris/util.py index d08c29d4f8..71ff95a2c9 100644 --- a/lib/iris/util.py +++ b/lib/iris/util.py @@ -10,7 +10,6 @@ from abc import ABCMeta, abstractmethod from collections.abc import Hashable, Iterable -import copy import functools import inspect import os @@ -23,7 +22,6 @@ import numpy as np import numpy.ma as ma -from iris._deprecation import warn_deprecated from iris._lazy_data import as_concrete_data, is_lazy_data import iris.exceptions @@ -1167,133 +1165,6 @@ def new_axis(src_cube, scalar_coord=None): return new_cube -def as_compatible_shape(src_cube, target_cube): - """ - Return a cube with added length one dimensions to match the dimensionality - and dimension ordering of `target_cube`. - - This function can be used to add the dimensions that have been collapsed, - aggregated or sliced out, promoting scalar coordinates to length one - dimension coordinates where necessary. It operates by matching coordinate - metadata to infer the dimensions that need modifying, so the provided - cubes must have coordinates with the same metadata - (see :class:`iris.common.CoordMetadata`). - - .. note:: This function will load and copy the data payload of `src_cube`. - - .. deprecated:: 3.0.0 - - Instead use :class:`~iris.common.resolve.Resolve`. For example, rather - than calling ``as_compatible_shape(src_cube, target_cube)`` replace - with ``Resolve(src_cube, target_cube)(target_cube.core_data())``. - - Args: - - * src_cube: - An instance of :class:`iris.cube.Cube` with missing dimensions. - - * target_cube: - An instance of :class:`iris.cube.Cube` with the desired dimensionality. - - Returns: - A instance of :class:`iris.cube.Cube` with the same dimensionality as - `target_cube` but with the data and coordinates from `src_cube` - suitably reshaped to fit. - - """ - from iris.cube import Cube - - wmsg = ( - "iris.util.as_compatible_shape has been deprecated and will be " - "removed, please use iris.common.resolve.Resolve instead." - ) - warn_deprecated(wmsg) - - dim_mapping = {} - for coord in target_cube.aux_coords + target_cube.dim_coords: - dims = target_cube.coord_dims(coord) - try: - collapsed_dims = src_cube.coord_dims(coord) - except iris.exceptions.CoordinateNotFoundError: - continue - if collapsed_dims: - if len(collapsed_dims) == len(dims): - for dim_from, dim_to in zip(dims, collapsed_dims): - dim_mapping[dim_from] = dim_to - elif dims: - for dim_from in dims: - dim_mapping[dim_from] = None - - if len(dim_mapping) != target_cube.ndim: - raise ValueError( - "Insufficient or conflicting coordinate " - "metadata. Cannot infer dimension mapping " - "to restore cube dimensions." - ) - - new_shape = [1] * target_cube.ndim - for dim_from, dim_to in dim_mapping.items(): - if dim_to is not None: - new_shape[dim_from] = src_cube.shape[dim_to] - - new_data = src_cube.data.copy() - - # Transpose the data (if necessary) to prevent assignment of - # new_shape doing anything except adding length one dims. - order = [v for k, v in sorted(dim_mapping.items()) if v is not None] - if order != sorted(order): - new_order = [order.index(i) for i in range(len(order))] - new_data = np.transpose(new_data, new_order).copy() - - new_cube = Cube(new_data.reshape(new_shape)) - new_cube.metadata = copy.deepcopy(src_cube.metadata) - - # Record a mapping from old coordinate IDs to new coordinates, - # for subsequent use in creating updated aux_factories. - coord_mapping = {} - - reverse_mapping = {v: k for k, v in dim_mapping.items() if v is not None} - - def add_coord(coord): - """Closure used to add a suitably reshaped coord to new_cube.""" - all_dims = target_cube.coord_dims(coord) - src_dims = [ - dim - for dim in src_cube.coord_dims(coord) - if src_cube.shape[dim] > 1 - ] - mapped_dims = [reverse_mapping[dim] for dim in src_dims] - length1_dims = [dim for dim in all_dims if new_cube.shape[dim] == 1] - dims = length1_dims + mapped_dims - shape = [new_cube.shape[dim] for dim in dims] - if not shape: - shape = [1] - points = coord.points.reshape(shape) - bounds = None - if coord.has_bounds(): - bounds = coord.bounds.reshape(shape + [coord.nbounds]) - new_coord = coord.copy(points=points, bounds=bounds) - # If originally in dim_coords, add to dim_coords, otherwise add to - # aux_coords. - if target_cube.coords(coord, dim_coords=True): - try: - new_cube.add_dim_coord(new_coord, dims) - except ValueError: - # Catch cases where the coord is an AuxCoord and therefore - # cannot be added to dim_coords. - new_cube.add_aux_coord(new_coord, dims) - else: - new_cube.add_aux_coord(new_coord, dims) - coord_mapping[id(coord)] = new_coord - - for coord in src_cube.aux_coords + src_cube.dim_coords: - add_coord(coord) - for factory in src_cube.aux_factories: - new_cube.add_aux_factory(factory.updated(coord_mapping)) - - return new_cube - - def squeeze(cube): """ Removes any dimension of length one. If it has an associated DimCoord or From 7ef20f5e644e89472fd263ec6513d4b3a21c17b2 Mon Sep 17 00:00:00 2001 From: Will Benfold Date: Thu, 20 Jan 2022 14:43:42 +0000 Subject: [PATCH 2/5] Removed tests --- lib/iris/tests/test_util.py | 98 ------------------------------------- 1 file changed, 98 deletions(-) diff --git a/lib/iris/tests/test_util.py b/lib/iris/tests/test_util.py index cf921ae210..ec7f8d1023 100644 --- a/lib/iris/tests/test_util.py +++ b/lib/iris/tests/test_util.py @@ -276,103 +276,5 @@ def test_output_file(self): self.assertFilesEqual(filename, "incompatible_cubes.str.txt") -@tests.skip_data -class TestAsCompatibleShape(tests.IrisTest): - def test_slice(self): - cube = tests.stock.realistic_4d() - sliced = cube[1, :, 2, :-2] - expected = cube[1:2, :, 2:3, :-2] - res = iris.util.as_compatible_shape(sliced, cube) - self.assertEqual(res, expected) - - def test_transpose(self): - cube = tests.stock.realistic_4d() - transposed = cube.copy() - transposed.transpose() - expected = cube - res = iris.util.as_compatible_shape(transposed, cube) - self.assertEqual(res, expected) - - def test_slice_and_transpose(self): - cube = tests.stock.realistic_4d() - sliced_and_transposed = cube[1, :, 2, :-2] - sliced_and_transposed.transpose() - expected = cube[1:2, :, 2:3, :-2] - res = iris.util.as_compatible_shape(sliced_and_transposed, cube) - self.assertEqual(res, expected) - - def test_collapsed(self): - cube = tests.stock.realistic_4d() - collapsed = cube.collapsed("model_level_number", iris.analysis.MEAN) - expected_shape = list(cube.shape) - expected_shape[1] = 1 - expected_data = collapsed.data.reshape(expected_shape) - res = iris.util.as_compatible_shape(collapsed, cube) - self.assertCML( - res, ("util", "as_compatible_shape_collapsed.cml"), checksum=False - ) - self.assertMaskedArrayEqual(expected_data, res.data) - - def test_reduce_dimensionality(self): - # Test that as_compatible_shape() can demote - # length one dimensions to scalars. - cube = tests.stock.realistic_4d() - src = cube[:, 2:3] - expected = reduced = cube[:, 2] - res = iris.util.as_compatible_shape(src, reduced) - self.assertEqual(res, expected) - - def test_anonymous_dims(self): - cube = tests.stock.realistic_4d() - # Move all coords from dim_coords to aux_coords. - for coord in cube.dim_coords: - dim = cube.coord_dims(coord) - cube.remove_coord(coord) - cube.add_aux_coord(coord, dim) - - sliced = cube[1, :, 2, :-2] - expected = cube[1:2, :, 2:3, :-2] - res = iris.util.as_compatible_shape(sliced, cube) - self.assertEqual(res, expected) - - def test_scalar_auxcoord(self): - def dim_to_aux(cube, coord_name): - """Convert coordinate on cube from DimCoord to AuxCoord.""" - coord = cube.coord(coord_name) - coord = iris.coords.AuxCoord.from_coord(coord) - cube.replace_coord(coord) - - cube = tests.stock.realistic_4d() - src = cube[:, :, 3] - dim_to_aux(src, "grid_latitude") - expected = cube[:, :, 3:4] - dim_to_aux(expected, "grid_latitude") - res = iris.util.as_compatible_shape(src, cube) - self.assertEqual(res, expected) - - def test_2d_auxcoord_transpose(self): - dim_coord1 = iris.coords.DimCoord(range(3), long_name="first_dim") - dim_coord2 = iris.coords.DimCoord(range(4), long_name="second_dim") - aux_coord_2d = iris.coords.AuxCoord( - np.arange(12).reshape(3, 4), long_name="spanning" - ) - aux_coord_2d_T = iris.coords.AuxCoord( - np.arange(12).reshape(3, 4).T, long_name="spanning" - ) - src = iris.cube.Cube( - np.ones((3, 4)), - dim_coords_and_dims=[(dim_coord1, 0), (dim_coord2, 1)], - aux_coords_and_dims=[(aux_coord_2d, (0, 1))], - ) - target = iris.cube.Cube( - np.ones((4, 3)), - dim_coords_and_dims=[(dim_coord1, 1), (dim_coord2, 0)], - aux_coords_and_dims=[(aux_coord_2d_T, (0, 1))], - ) - - res = iris.util.as_compatible_shape(src, target) - self.assertEqual(res[0], target[0]) - - if __name__ == "__main__": tests.main() From a96ee189b8518b67698bec483bf114c74a0386fe Mon Sep 17 00:00:00 2001 From: Will Benfold Date: Thu, 20 Jan 2022 14:45:22 +0000 Subject: [PATCH 3/5] Update whatsnew with PR num --- docs/src/whatsnew/latest.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/whatsnew/latest.rst b/docs/src/whatsnew/latest.rst index 0fd091d249..861731d244 100644 --- a/docs/src/whatsnew/latest.rst +++ b/docs/src/whatsnew/latest.rst @@ -204,7 +204,7 @@ This document explains the changes made to Iris for this release which was deprecated in ``v3.0``. Instead use :class:`iris.common.resolve.Resolve`. For example, rather than calling ``as_compatible_shape(src_cube, target_cube)`` replace with - ``Resolve(src_cube, target_cube)(target_cube.core_data())``. (:pull:`XXXX`) + ``Resolve(src_cube, target_cube)(target_cube.core_data())``. (:pull:`4513`) 🔗 Dependencies From c199a1f0070c519faf3d52e3863e560ba8b64b7d Mon Sep 17 00:00:00 2001 From: Will Benfold Date: Tue, 25 Jan 2022 14:14:46 +0000 Subject: [PATCH 4/5] Remove unused cml --- .../util/as_compatible_shape_collapsed.cml | 144 ------------------ 1 file changed, 144 deletions(-) delete mode 100644 lib/iris/tests/results/util/as_compatible_shape_collapsed.cml diff --git a/lib/iris/tests/results/util/as_compatible_shape_collapsed.cml b/lib/iris/tests/results/util/as_compatible_shape_collapsed.cml deleted file mode 100644 index 07eeb53157..0000000000 --- a/lib/iris/tests/results/util/as_compatible_shape_collapsed.cml +++ /dev/null @@ -1,144 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 33eee4b6411083b8d90eb779d420db4c1b1f9406 Mon Sep 17 00:00:00 2001 From: Will Benfold Date: Tue, 25 Jan 2022 15:04:20 +0000 Subject: [PATCH 5/5] Restore import --- lib/iris/util.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/iris/util.py b/lib/iris/util.py index 71ff95a2c9..53cd78724e 100644 --- a/lib/iris/util.py +++ b/lib/iris/util.py @@ -22,6 +22,7 @@ import numpy as np import numpy.ma as ma +from iris._deprecation import warn_deprecated from iris._lazy_data import as_concrete_data, is_lazy_data import iris.exceptions