Skip to content

Commit 9c5eb0a

Browse files
authored
Allow concatenation of string coordinates of differing width (#6676) (#7125)
* Allow concatenation of string coordinates of differing width Concatenation compared coordinate dtypes exactly, so two cubes whose string coordinate differed only in width (e.g. '<U1' vs '<U5') were reported as having differing metadata and could not be concatenated. When comparing the coordinate signatures, collapse string dtypes to their kind so differing widths no longer block concatenation; numpy promotes the points to a common width when they are joined. Genuine dtype-kind differences (e.g. string vs integer) are still rejected. Fixes #6676. * Address string coordinate concat review comments
1 parent 746f0a6 commit 9c5eb0a

3 files changed

Lines changed: 42 additions & 0 deletions

File tree

changelog/7125.bugfix.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
:user:`gaoflow` fixed :meth:`iris.cube.CubeList.concatenate` so that cubes
2+
with string coordinates of differing width, such as dtypes ``<U1`` and
3+
``<U5``, can be concatenated. The result coordinate is promoted to the wider
4+
dtype. (:issue:`6676`)

lib/iris/_concatenate.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,11 @@ def __new__(mcs, coord, dims):
102102
bounds_dtype = (
103103
coord.core_bounds().dtype if coord.core_bounds() is not None else None
104104
)
105+
# Ignore string width; joined coordinates promote to a common width.
106+
if points_dtype.kind in ("U", "S"):
107+
points_dtype = np.dtype(points_dtype.kind)
108+
if bounds_dtype is not None and bounds_dtype.kind in ("U", "S"):
109+
bounds_dtype = np.dtype(bounds_dtype.kind)
105110
kwargs = {}
106111
# Add scalar flag metadata.
107112
kwargs["scalar"] = coord.core_points().size == 1

lib/iris/tests/unit/concatenate/test_concatenate.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,39 @@ def test_desc_bounds_all_singleton(self):
389389
assert result1 == result2
390390

391391

392+
class TestStringAuxCoordWidths:
393+
# Cubes with string auxiliary coordinates of differing width (dtype) should
394+
# still concatenate, with the result promoted to the wider dtype (#6676).
395+
def _make_cube(self, dim_points, aux_points):
396+
data = np.arange(len(dim_points))
397+
cube = iris.cube.Cube(data, long_name="test")
398+
cube.add_dim_coord(iris.coords.DimCoord(dim_points, long_name="dim"), 0)
399+
cube.add_aux_coord(
400+
iris.coords.AuxCoord(np.array(aux_points), long_name="example"), 0
401+
)
402+
return cube
403+
404+
def test_different_widths(self):
405+
cube_a = self._make_cube([0, 1], ["1", "2"])
406+
cube_b = self._make_cube([10, 11, 12], ["1", "123", "12345"])
407+
assert cube_a.coord("example").dtype != cube_b.coord("example").dtype
408+
409+
(result,) = concatenate([cube_a, cube_b], True)
410+
411+
coord = result.coord("example")
412+
assert coord.dtype == np.dtype("<U5")
413+
np.testing.assert_array_equal(coord.points, ["1", "2", "1", "123", "12345"])
414+
415+
def test_different_dtype_kind_still_rejected(self):
416+
# A genuine dtype-kind difference (string vs integer) must still block
417+
# concatenation.
418+
cube_a = self._make_cube([0, 1], ["1", "2"])
419+
cube_b = self._make_cube([2, 3], [1, 2])
420+
421+
with pytest.raises(ConcatenateError):
422+
_ = concatenate([cube_a, cube_b], True)
423+
424+
392425
class TestConcatenate__dask:
393426
@pytest.fixture
394427
def sample_lazy_cubes(self):

0 commit comments

Comments
 (0)