Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds shape property to NDCube #684

Merged
merged 39 commits into from
Apr 24, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
8467e17
Implements new `shape` property on class
CyclingNinja Apr 23, 2024
32b58c4
Merge branch 'main' of github.com:sunpy/ndcube into remove_dimension
CyclingNinja Apr 23, 2024
4e251f8
Update ndcube/ndcube_sequence.py
CyclingNinja Apr 23, 2024
6f18309
Update ndcube/ndcube_sequence.py
CyclingNinja Apr 23, 2024
859690b
Update ndcube/ndcube_sequence.py
CyclingNinja Apr 23, 2024
4e58b82
Adds shape property to NDCubeSequence
CyclingNinja Apr 23, 2024
9151f76
Adds exceptions package
CyclingNinja Apr 23, 2024
9176883
Update ndcube/utils/exceptions.py
CyclingNinja Apr 24, 2024
237a349
Update ndcube/utils/exceptions.py
CyclingNinja Apr 24, 2024
b3067b5
Update ndcube/ndcube_sequence.py
CyclingNinja Apr 24, 2024
5f9d832
Update ndcube/ndcube.py
CyclingNinja Apr 24, 2024
616c111
Update ndcube/ndcube.py
CyclingNinja Apr 24, 2024
8ead4c7
Merge branch 'main' of github.com:sunpy/ndcube into remove_dimension
CyclingNinja Apr 24, 2024
c8676c2
Implements deprecation warnings on dimensions
CyclingNinja Apr 24, 2024
2d66db6
Implements new `shape` property on class
CyclingNinja Apr 23, 2024
3e3aff3
Update ndcube/ndcube_sequence.py
CyclingNinja Apr 23, 2024
75cc8c6
Update ndcube/ndcube_sequence.py
CyclingNinja Apr 23, 2024
2575cd3
Update ndcube/ndcube_sequence.py
CyclingNinja Apr 23, 2024
1c33010
Adds shape property to NDCubeSequence
CyclingNinja Apr 23, 2024
996f157
Adds exceptions package
CyclingNinja Apr 23, 2024
b941d22
Update ndcube/utils/exceptions.py
CyclingNinja Apr 24, 2024
ca9c8b2
Update ndcube/utils/exceptions.py
CyclingNinja Apr 24, 2024
2c5542f
Update ndcube/ndcube_sequence.py
CyclingNinja Apr 24, 2024
e64670a
Update ndcube/ndcube.py
CyclingNinja Apr 24, 2024
59d2e30
Update ndcube/ndcube.py
CyclingNinja Apr 24, 2024
e186d53
Implements deprecation warnings on dimensions
CyclingNinja Apr 24, 2024
69d2cf1
add changelog
nabobalis Apr 24, 2024
22bd1b3
add changelog v2
nabobalis Apr 24, 2024
642dd89
Apply suggestions from code review
nabobalis Apr 24, 2024
f7ee5ee
Applies dimension -> shape refactor
CyclingNinja Apr 24, 2024
b23be65
Merge branch 'remove_dimension' of github.com:CyclingNinja/ndcube int…
CyclingNinja Apr 24, 2024
ba18115
First set of changes
nabobalis Apr 24, 2024
494daaa
Update changelog/684.breaking.rst
nabobalis Apr 24, 2024
c861ab8
Second set of changes - Broken like my heart
nabobalis Apr 24, 2024
26605d2
Third set of changes
nabobalis Apr 24, 2024
ec430bd
Final set of changes
nabobalis Apr 24, 2024
2716bff
Merge remote-tracking branch 'upstream/main' into pr/684
nabobalis Apr 24, 2024
d5eb23c
Bugfixes due to review
nabobalis Apr 24, 2024
8ed4e5f
Apply suggestions from code review
nabobalis Apr 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
7 changes: 7 additions & 0 deletions ndcube/ndcube.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from ndcube.global_coords import GlobalCoords, GlobalCoordsABC
from ndcube.mixins import NDCubeSlicingMixin
from ndcube.ndcube_sequence import NDCubeSequence
from ndcube.utils.exceptions import NDCubeDeprecationWarning
nabobalis marked this conversation as resolved.
Show resolved Hide resolved
from ndcube.utils.wcs_high_level_conversion import values_to_high_level_objects
from ndcube.visualization import PlotterDescriptor
from ndcube.wcs.wrappers import CompoundLowLevelWCS, ResampledLowLevelWCS
Expand Down Expand Up @@ -413,10 +414,16 @@
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.')
CyclingNinja marked this conversation as resolved.
Show resolved Hide resolved
@property
def dimensions(self):
warnings.warn("Quantity removed. Use `ndcube.NDCube.shape` instead.", NDCubeDeprecationWarning)

Check warning on line 420 in ndcube/ndcube.py

View check run for this annotation

Codecov / codecov/patch

ndcube/ndcube.py#L420

Added line #L420 was not covered by tests
CyclingNinja marked this conversation as resolved.
Show resolved Hide resolved
return u.Quantity(self.data.shape, unit=u.pix)

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

Check warning on line 425 in ndcube/ndcube.py

View check run for this annotation

Codecov / codecov/patch

ndcube/ndcube.py#L423-L425

Added lines #L423 - L425 were not covered by tests

@property
def array_axis_physical_types(self):
# Docstring in NDCubeABC.
Expand Down
16 changes: 9 additions & 7 deletions ndcube/ndcube_sequence.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,22 +46,24 @@
"""
The length of each axis including the sequence axis.
"""
return self._dimensions
return tuple([d * u.pix for d in self._shape])

Check warning on line 49 in ndcube/ndcube_sequence.py

View check run for this annotation

Codecov / codecov/patch

ndcube/ndcube_sequence.py#L49

Added line #L49 was not covered by tests

@property
def _dimensions(self):
dimensions = [len(self.data) * u.pix] + list(self.data[0].dimensions)
def shape(self):
return self._shape

Check warning on line 53 in ndcube/ndcube_sequence.py

View check run for this annotation

Codecov / codecov/patch

ndcube/ndcube_sequence.py#L53

Added line #L53 was not covered by tests

@property
def _shape(self):
dimensions = [len(self.data)] + list(self.data[0].shape)

Check warning on line 57 in ndcube/ndcube_sequence.py

View check run for this annotation

Codecov / codecov/patch

ndcube/ndcube_sequence.py#L57

Added line #L57 was not covered by tests
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]
for cube in self.data]
dimensions[self._common_axis + 1] = u.Quantity(
common_axis_dimensions, unit=common_axis_dimensions[0].unit)
common_axis_dimensions = tuple([cube.shape[self._common_axis]

Check warning on line 65 in ndcube/ndcube_sequence.py

View check run for this annotation

Codecov / codecov/patch

ndcube/ndcube_sequence.py#L65

Added line #L65 was not covered by tests
for cube in self.data])
CyclingNinja marked this conversation as resolved.
Show resolved Hide resolved
return tuple(dimensions)

@property
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 == 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 @@ -1170,11 +1170,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
14 changes: 7 additions & 7 deletions ndcube/tests/test_ndcubesequence.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def test_slice_sequence_axis(ndc, 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 int(sliced_sequence.dimensions[0]) == tuple_item[0].stop - tuple_item[0].start
assert (sliced_sequence[0].dimensions == expected_cube0_dims).all()


Expand All @@ -54,7 +54,7 @@ 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()

Expand Down Expand Up @@ -98,7 +98,7 @@ def test_slice_common_axis(ndc, item, expected_common_axis):
indirect=("ndc",))
def test_index_as_cube(ndc, item, expected_dimensions):
sliced_sequence = ndc.index_as_cube[item]
sliced_dims = sliced_sequence.dimensions
sliced_dims = sliced_sequence.shape
for dim, expected_dim in zip(sliced_dims, expected_dimensions):
(dim == expected_dim).all()

Expand All @@ -115,21 +115,21 @@ def test_index_as_cube(ndc, item, expected_dimensions):
indirect=("ndc",))
def test_explode_along_axis_common_axis_None(ndc, axis, expected_dimensions):
exploded_sequence = ndc.explode_along_axis(axis)
assert exploded_sequence.dimensions == expected_dimensions
assert exploded_sequence.shape == expected_dimensions
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


Expand All @@ -151,7 +151,7 @@ def test_dimensions(ndc, expected_dimensions):
("ndcubesequence_4c_ln_lt_l_cax1", [2., 12, 4] * u.pix),
),
indirect=("ndc",))
def test_cube_like_dimensions(ndc, expected_dimensions):
def test_cube_like_shape(ndc, expected_dimensions):
assert (ndc.cube_like_dimensions == expected_dimensions).all()


Expand Down
35 changes: 35 additions & 0 deletions ndcube/utils/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""
This module provides errors/exceptions and warnings of general use for NDCube.

Exceptions that are specific to a given package should **not** be here,
CyclingNinja marked this conversation as resolved.
Show resolved Hide resolved
but rather in the particular package.
"""
import warnings

__all__ = ["NDCubeDeprecationWarning"]
CyclingNinja marked this conversation as resolved.
Show resolved Hide resolved


class NDCubeWarning(UserWarning):
"""
A general NDCube warning
"""

class NDCubeDeprecationWarning(FutureWarning, NDCubeWarning):
"""
A warning class to indicate a deprecated feature.
"""

def warn_deprecated(msg, stacklevel=1):
"""
Raise a `NDCubeDeprecationWarning`.

Parameters
----------
msg : str
Warning message.
stacklevel : int
This is interpreted relative to the call to this function,
e.g. ``stacklevel=1`` (the default) sets the stack level in the
code that calls this function.
"""
warnings.warn(msg, NDCubeDeprecationWarning , stacklevel + 1)

Check warning on line 35 in ndcube/utils/exceptions.py

View check run for this annotation

Codecov / codecov/patch

ndcube/utils/exceptions.py#L35

Added line #L35 was not covered by tests
Loading