Skip to content

Commit

Permalink
Implements new shape property on class
Browse files Browse the repository at this point in the history
Depricates `dimensions` property
  • Loading branch information
CyclingNinja committed Apr 23, 2024
1 parent 3a62c59 commit 8467e17
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 40 deletions.
18 changes: 9 additions & 9 deletions examples/slicing_ndcube.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@
example_cube.plot()

##############################################################################
# We can also inspect the dimensions of the cube:
example_cube.dimensions
# We can also inspect the shape of the cube:
example_cube.shape

##############################################################################
# We can also inspect the world coordinates for all array elements:
Expand All @@ -77,14 +77,14 @@

sliced_cube = example_cube[1, :, :]
# here we can see we are left with a 2-D cube which is an image at one wavelength.
sliced_cube.dimensions
sliced_cube.shape

# We can also index a region of interest of the cube at a particular wavelength.
# Again note that we are slicing here based on the ``array`` index rather than cropping by
# real world value

sliced_cube = example_cube[1, 10:20, 20:40]
sliced_cube.dimensions
sliced_cube.shape

# Now we can inspect the sliced cube, and see it's now a smaller region of interest.
sliced_cube.plot()
Expand Down Expand Up @@ -114,9 +114,9 @@
cropped_cube = example_cube.crop(point1, point2)

##############################################################################
# Similar to before, we can inspect the dimensions of the sliced cube:
# Similar to before, we can inspect the dimensions of the sliced cube via the shape property:

cropped_cube.dimensions
cropped_cube.shape

##############################################################################
# and we can visualize it:
Expand All @@ -134,7 +134,7 @@
##############################################################################
# we can inspect the dimensions of the cropped cube:

cropped_cube.dimensions
cropped_cube.shape

##############################################################################
# and again visualize it:
Expand All @@ -151,7 +151,7 @@
##############################################################################
# Check dimensions:

cropped_cube.dimensions
cropped_cube.shape

##############################################################################
# Here we can just see how powerful this can be to easily crop over different world coordinates.
Expand All @@ -162,4 +162,4 @@
point6 = [SkyCoord(200*u.arcsec, 100*u.arcsec, frame=frames.Helioprojective), SpectralCoord(10.6*u.angstrom)]

cropped_cube = example_cube.crop(point5, point6)
cropped_cube.dimensions
cropped_cube.shape
6 changes: 6 additions & 0 deletions ndcube/ndcube.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from collections.abc import Mapping, Iterable

import numpy as np
from deprecated import deprecated

import astropy.nddata
import astropy.units as u
Expand Down Expand Up @@ -413,10 +414,15 @@ def combined_wcs(self):
CompoundLowLevelWCS(self.wcs.low_level_wcs, self._extra_coords.wcs, mapping=mapping)
)

@deprecated(version='3.0.0', reason='Quantity removed. Use `ndcube.NDCube.shape` instead.')
@property
def dimensions(self):
return u.Quantity(self.data.shape, unit=u.pix)

@property
def shape(self):
return self.data.shape

@property
def array_axis_physical_types(self):
# Docstring in NDCubeABC.
Expand Down
4 changes: 2 additions & 2 deletions ndcube/ndcube_sequence.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,15 @@ def dimensions(self):

@property
def _dimensions(self):
dimensions = [len(self.data) * u.pix] + list(self.data[0].dimensions)
dimensions = [len(self.data) * u.pix] + list(self.data[0].shape)
if len(dimensions) > 1:
# If there is a common axis, length of cube's along it may not
# be the same. Therefore if the lengths are different,
# represent them as a tuple of all the values, else as an int.
if self._common_axis is not None:
common_axis_lengths = [cube.data.shape[self._common_axis] for cube in self.data]
if len(np.unique(common_axis_lengths)) != 1:
common_axis_dimensions = [cube.dimensions[self._common_axis]
common_axis_dimensions = [cube.shape[self._common_axis]
for cube in self.data]
dimensions[self._common_axis + 1] = u.Quantity(
common_axis_dimensions, unit=common_axis_dimensions[0].unit)
Expand Down
18 changes: 9 additions & 9 deletions ndcube/tests/test_ndcube.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def test_wcs_object(all_ndcubes):
indirect=("ndc",))
def test_slicing_ln_lt(ndc, item):
sndc = ndc[item]
assert len(sndc.dimensions) == 2
assert len(sndc.shape) == 2
assert set(sndc.wcs.world_axis_physical_types) == {"custom:pos.helioprojective.lat",
"custom:pos.helioprojective.lon"}
if sndc.uncertainty is not None:
Expand Down Expand Up @@ -82,7 +82,7 @@ def test_slicing_ln_lt(ndc, item):
indirect=("ndc",))
def test_slicing_wave(ndc, item):
sndc = ndc[item]
assert len(sndc.dimensions) == 1
assert len(sndc.shape) == 1
assert set(sndc.wcs.world_axis_physical_types) == {"em.wl"}
if sndc.uncertainty is not None:
assert np.allclose(sndc.data, sndc.uncertainty.array)
Expand Down Expand Up @@ -117,7 +117,7 @@ def test_slicing_wave(ndc, item):
indirect=("ndc",))
def test_slicing_split_celestial(ndc, item):
sndc = ndc[item]
assert len(sndc.dimensions) == 2
assert len(sndc.shape) == 2
if sndc.uncertainty is not None:
assert np.allclose(sndc.data, sndc.uncertainty.array)
if sndc.mask is not None:
Expand Down Expand Up @@ -815,7 +815,7 @@ def test_rebin(ndcube_3d_l_ln_lt_ectime):
expected_time.format = "fits"

# Confirm output is as expected.
assert (output.dimensions.value == np.array([1, 2, 8])).all()
assert (output.shape.value == np.array([1, 2, 8])).all()
assert (output.data == expected_data).all()
assert (output.mask == expected_mask).all()
assert output.uncertainty == expected_uncertainty
Expand Down Expand Up @@ -1143,11 +1143,11 @@ def test_to_dask(ndcube_2d_dask):


def test_squeeze(ndcube_4d_ln_l_t_lt):
assert np.array_equal(ndcube_4d_ln_l_t_lt.squeeze().dimensions, ndcube_4d_ln_l_t_lt.dimensions)
assert np.array_equal(ndcube_4d_ln_l_t_lt[:,:,0,:].dimensions, ndcube_4d_ln_l_t_lt[:,:,0:1,:].squeeze().dimensions)
assert np.array_equal(ndcube_4d_ln_l_t_lt[:,:,0,:].dimensions, ndcube_4d_ln_l_t_lt[:,:,0:1,:].squeeze(2).dimensions)
assert np.array_equal(ndcube_4d_ln_l_t_lt[:,0,0,:].dimensions, ndcube_4d_ln_l_t_lt[:,0:1,0:1,:].squeeze([1,2]).dimensions)
assert np.array_equal(ndcube_4d_ln_l_t_lt[:,0:1,0,:].dimensions, ndcube_4d_ln_l_t_lt[:,0:1,0:1,:].squeeze(2).dimensions)
assert np.array_equal(ndcube_4d_ln_l_t_lt.squeeze().shape, ndcube_4d_ln_l_t_lt.shape)
assert np.array_equal(ndcube_4d_ln_l_t_lt[:,:,0,:].shape, ndcube_4d_ln_l_t_lt[:,:,0:1,:].squeeze().shape)
assert np.array_equal(ndcube_4d_ln_l_t_lt[:,:,0,:].shape, ndcube_4d_ln_l_t_lt[:,:,0:1,:].squeeze(2).shape)
assert np.array_equal(ndcube_4d_ln_l_t_lt[:,0,0,:].shape, ndcube_4d_ln_l_t_lt[:,0:1,0:1,:].squeeze([1,2]).shape)
assert np.array_equal(ndcube_4d_ln_l_t_lt[:,0:1,0,:].shape, ndcube_4d_ln_l_t_lt[:,0:1,0:1,:].squeeze(2).shape)


def test_squeeze_error(ndcube_4d_ln_l_t_lt):
Expand Down
40 changes: 20 additions & 20 deletions ndcube/tests/test_ndcubesequence.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ def derive_sliced_cube_dims(orig_cube_dims, tuple_item):
def test_slice_sequence_axis(ndc, item):
# Calculate expected dimensions of cubes with sequence after slicing.
tuple_item = item if isinstance(item, tuple) else (item,)
expected_cube0_dims = derive_sliced_cube_dims(ndc.data[tuple_item[0]][0].dimensions, tuple_item)
expected_cube0_dims = derive_sliced_cube_dims(ndc.data[tuple_item[0]][0].shape, tuple_item)
# Assert output is as expected.
sliced_sequence = ndc[item]
assert isinstance(sliced_sequence, NDCubeSequence)
assert int(sliced_sequence.dimensions[0].value) == tuple_item[0].stop - tuple_item[0].start
assert (sliced_sequence[0].dimensions == expected_cube0_dims).all()
assert int(sliced_sequence.shape[0].value) == tuple_item[0].stop - tuple_item[0].start
assert (sliced_sequence[0].shape == expected_cube0_dims).all()


@pytest.mark.parametrize("ndc, item",
Expand All @@ -54,9 +54,9 @@ def test_slice_sequence_axis(ndc, item):
def test_extract_ndcube(ndc, item):
cube = ndc[item]
tuple_item = item if isinstance(item, tuple) else (item,)
expected_cube_dims = derive_sliced_cube_dims(ndc.data[tuple_item[0]].dimensions, tuple_item)
expected_cube_dims = derive_sliced_cube_dims(ndc.data[tuple_item[0]].shape, tuple_item)
assert isinstance(cube, NDCube)
assert (cube.dimensions == expected_cube_dims).all()
assert (cube.shape == expected_cube_dims).all()


@pytest.mark.parametrize("ndc, item, expected_common_axis",
Expand All @@ -72,7 +72,7 @@ def test_slice_common_axis(ndc, item, expected_common_axis):
assert sliced_sequence._common_axis == expected_common_axis


@pytest.mark.parametrize("ndc, item, expected_dimensions",
@pytest.mark.parametrize("ndc, item, expected_shape",
(
("ndcubesequence_4c_ln_lt_l_cax1", np.s_[1:7], (3 * u.pix,
2 * u.pix,
Expand All @@ -96,14 +96,14 @@ def test_slice_common_axis(ndc, item, expected_common_axis):
2 * u.pix))
),
indirect=("ndc",))
def test_index_as_cube(ndc, item, expected_dimensions):
def test_index_as_cube(ndc, item, expected_shape):
sliced_sequence = ndc.index_as_cube[item]
sliced_dims = sliced_sequence.dimensions
for dim, expected_dim in zip(sliced_dims, expected_dimensions):
sliced_dims = sliced_sequence.shape
for dim, expected_dim in zip(sliced_dims, expected_shape):
(dim == expected_dim).all()


@pytest.mark.parametrize("ndc, axis, expected_dimensions",
@pytest.mark.parametrize("ndc, axis, expected_shape",
(
("ndcubesequence_4c_ln_lt_l", 0, (8 * u.pix,
3 * u.pix,
Expand All @@ -113,46 +113,46 @@ def test_index_as_cube(ndc, item, expected_dimensions):
4 * u.pix))
),
indirect=("ndc",))
def test_explode_along_axis_common_axis_None(ndc, axis, expected_dimensions):
def test_explode_along_axis_common_axis_None(ndc, axis, expected_shape):
exploded_sequence = ndc.explode_along_axis(axis)
assert exploded_sequence.dimensions == expected_dimensions
assert exploded_sequence.shape == expected_shape
assert exploded_sequence._common_axis is None


@pytest.mark.parametrize("ndc", (('ndcubesequence_4c_ln_lt_l_cax1',)), indirect=("ndc",))
def test_explode_along_axis_common_axis_same(ndc):
exploded_sequence = ndc.explode_along_axis(2)
assert exploded_sequence.dimensions == (16*u.pix, 2*u.pix, 3*u.pix)
assert exploded_sequence.shape == (16*u.pix, 2*u.pix, 3*u.pix)
assert exploded_sequence._common_axis == ndc._common_axis


@pytest.mark.parametrize("ndc", (('ndcubesequence_4c_ln_lt_l_cax1',)), indirect=("ndc",))
def test_explode_along_axis_common_axis_changed(ndc):
exploded_sequence = ndc.explode_along_axis(0)
assert exploded_sequence.dimensions == (8*u.pix, 3*u.pix, 4*u.pix)
assert exploded_sequence.shape == (8*u.pix, 3*u.pix, 4*u.pix)
assert exploded_sequence._common_axis == ndc._common_axis - 1


@pytest.mark.parametrize("ndc, expected_dimensions",
@pytest.mark.parametrize("ndc, expected_shape",
(
("ndcubesequence_4c_ln_lt_l_cax1", (4 * u.pix,
2. * u.pix,
3. * u.pix,
4. * u.pix)),
),
indirect=("ndc",))
def test_dimensions(ndc, expected_dimensions):
def test_shape(ndc, expected_shape):
unit_tester = unittest.TestCase()
unit_tester.assertEqual(ndc.dimensions, expected_dimensions)
unit_tester.assertEqual(ndc.shape, expected_shape)


@pytest.mark.parametrize("ndc, expected_dimensions",
@pytest.mark.parametrize("ndc, expected_shape",
(
("ndcubesequence_4c_ln_lt_l_cax1", [2., 12, 4] * u.pix),
),
indirect=("ndc",))
def test_cube_like_dimensions(ndc, expected_dimensions):
assert (ndc.cube_like_dimensions == expected_dimensions).all()
def test_cube_like_shape(ndc, expected_shape):
assert (ndc.cube_like_dimensions == expected_shape).all()


@pytest.mark.parametrize("ndc", (("ndcubesequence_4c_ln_lt_l",)), indirect=("ndc",))
Expand Down

0 comments on commit 8467e17

Please sign in to comment.