Skip to content

Commit 2a47e7c

Browse files
committed
Add proper error message for missing dims in Patch.transpose
1 parent 9d6b3ae commit 2a47e7c

File tree

3 files changed

+95
-10
lines changed

3 files changed

+95
-10
lines changed

dascore/proc/coords.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,14 @@ def transpose(self: PatchType, *dims: str) -> PatchType:
608608
"""
609609
dims = tuple(dims)
610610
old_dims = self.coords.dims
611+
# Filter out ellipsis from dims to validate
612+
dims_to_check = [d for d in dims if d is not ...]
613+
# Check for invalid dimensions
614+
if invalid_dims := set(dims_to_check) - set(old_dims):
615+
invalid_list = sorted(invalid_dims)
616+
valid_list = sorted(old_dims)
617+
msg = f"Dimension(s) {invalid_list} not found in Patch dimensions: {valid_list}"
618+
raise ParameterError(msg)
611619
new_coord = self.coords.transpose(*dims)
612620
new_dims = new_coord.dims
613621
axes = tuple(old_dims.index(x) for x in new_dims)

tests/test_proc/test_basic.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -115,16 +115,6 @@ def test_angle_from_complex(self, random_complex_patch):
115115
assert np.allclose(out.data, np.angle(random_complex_patch.data))
116116

117117

118-
class TestTranspose:
119-
"""Tests for transposing patch."""
120-
121-
def test_transpose_no_args(self, random_patch):
122-
"""Ensure transposing rotates dimensions."""
123-
pa = random_patch.transpose()
124-
assert pa.dims != random_patch.dims
125-
assert pa.dims == random_patch.dims[::-1]
126-
127-
128118
class TestNormalize:
129119
"""Tests for normalization."""
130120

tests/test_proc/test_proc_coords.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -805,3 +805,90 @@ def test_raises(self, random_patch):
805805
match = "has no dimension"
806806
with pytest.raises(CoordError, match=match):
807807
random_patch.get_axis(dim="money")
808+
809+
810+
class TestTranspose:
811+
"""Tests for transposing patches."""
812+
813+
@pytest.fixture(scope="class")
814+
def patch_5d(self):
815+
"""Create a 5D patch for testing transpose permutations."""
816+
shape = (3, 4, 5, 6, 7)
817+
dims = ("time", "distance", "x", "y", "z")
818+
data = np.arange(np.prod(shape)).reshape(shape)
819+
coords = {
820+
"time": np.arange(shape[0]),
821+
"distance": np.arange(shape[1]),
822+
"x": np.arange(shape[2]),
823+
"y": np.arange(shape[3]),
824+
"z": np.arange(shape[4]),
825+
}
826+
patch = dc.Patch(data=data, dims=dims, coords=coords)
827+
return patch
828+
829+
def test_transpose_invalid_dimension_raises_parameter_error(self, random_patch):
830+
"""Ensure transposing with invalid dimension raises clear ParameterError."""
831+
msg = "not_a_dim.*not found in Patch dimensions"
832+
with pytest.raises(ParameterError, match=msg):
833+
random_patch.transpose("time", "not_a_dim")
834+
835+
def test_transpose_multiple_invalid_dimensions_raises(self, random_patch):
836+
"""Ensure multiple invalid dimensions are reported."""
837+
msg = r"bad_dim1.*bad_dim2.*not found in Patch dimensions"
838+
with pytest.raises(ParameterError, match=msg):
839+
random_patch.transpose("bad_dim1", "bad_dim2")
840+
841+
def test_transpose_valid_dimensions_works(self, random_patch):
842+
"""Ensure transpose works with valid dimensions."""
843+
dims = random_patch.dims[::-1]
844+
# Transpose to reverse dimension order
845+
out = random_patch.transpose(*dims)
846+
# Should reverse the dimensions and shape
847+
assert out.dims == dims
848+
assert out.shape == (random_patch.shape[1], random_patch.shape[0])
849+
850+
def test_transpose_no_args(self, random_patch):
851+
"""Ensure transposing rotates dimensions."""
852+
pa = random_patch.transpose()
853+
assert pa.dims != random_patch.dims
854+
assert pa.dims == random_patch.dims[::-1]
855+
856+
def test_transpose_5d_complete_reversal(self, patch_5d):
857+
"""Test complete reversal of all dimensions."""
858+
out = patch_5d.transpose("z", "y", "x", "distance", "time")
859+
assert out.dims == ("z", "y", "x", "distance", "time")
860+
assert out.shape == (7, 6, 5, 4, 3)
861+
862+
def test_transpose_5d_move_first_to_last(self, patch_5d):
863+
"""Test moving first dimension to last."""
864+
out = patch_5d.transpose("distance", "x", "y", "z", "time")
865+
assert out.dims == ("distance", "x", "y", "z", "time")
866+
assert out.shape == (4, 5, 6, 7, 3)
867+
868+
def test_transpose_5d_swap_adjacent(self, patch_5d):
869+
"""Test swapping adjacent dimensions."""
870+
out = patch_5d.transpose("distance", "time", "x", "y", "z")
871+
assert out.dims == ("distance", "time", "x", "y", "z")
872+
assert out.shape == (4, 3, 5, 6, 7)
873+
874+
def test_transpose_5d_arbitrary_permutation(self, patch_5d):
875+
"""Test arbitrary permutation of dimensions."""
876+
out = patch_5d.transpose("y", "time", "z", "x", "distance")
877+
assert out.dims == ("y", "time", "z", "x", "distance")
878+
assert out.shape == (6, 3, 7, 5, 4)
879+
880+
def test_transpose_5d_with_ellipsis(self, patch_5d):
881+
"""Test using ellipsis to move last dimension to first."""
882+
out = patch_5d.transpose("z", ...)
883+
assert out.dims[0] == "z"
884+
assert out.shape[0] == 7
885+
# Remaining dims should be in original order
886+
assert out.dims == ("z", "time", "distance", "x", "y")
887+
888+
def test_transpose_5d_data_integrity(self, patch_5d):
889+
"""Test that transpose preserves data values."""
890+
out = patch_5d.transpose("z", "y", "x", "distance", "time")
891+
# Total size should be unchanged
892+
assert patch_5d.data.size == out.data.size
893+
# Data values should be the same, just rearranged
894+
assert np.array_equal(np.sort(patch_5d.data.flat), np.sort(out.data.flat))

0 commit comments

Comments
 (0)