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 35 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
2 changes: 1 addition & 1 deletion .ruff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ exclude = [
[lint]
select = [
"E",
#"F",
"F",
"W",
#"UP",
#"PT"
Expand Down
1 change: 1 addition & 0 deletions changelog/684.breaking.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"dimensions" property on ~ndcube.NDCube` and `~ndcube.NDCubeSequence` have been deprecated and replaced by "shape"
1 change: 1 addition & 0 deletions changelog/684.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added `ndcube.NDCube.shape` as a replacement for "dimensions".
4 changes: 2 additions & 2 deletions docs/explaining_ndcube/coordinates.rst
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@ For example:
.. code-block:: python

>>> celestial = my_cube.axis_world_coords('lon')[0] # Must extract object from returned tuple with [0]
>>> my_cube.dimensions
<Quantity [4., 4., 5.] pix>
>>> my_cube.shape
(4, 4, 5)
>>> celestial.shape
(4, 4)
>>> celestial
Expand Down
43 changes: 21 additions & 22 deletions docs/explaining_ndcube/data_classes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -110,21 +110,21 @@ To instantiate a more complex `~ndcube.NDCube` with metadata, a data unit, uncer

Attaching coordinates in addition to those described by ``.wcs`` via `~ndcube.ExtraCoords` and `~ndcube.GlobalCoords` is discussed in the :ref:`extra_coords` and :ref:`global_coords` sections.

Dimensions and Physical Types
-----------------------------
Shape and physical types
------------------------

`~ndcube.NDCube` has useful properties for inspecting its axes: `~ndcube.NDCube.dimensions` and `~ndcube.NDCube.array_axis_physical_types`.
`~ndcube.NDCube` has useful properties for inspecting its axes: `~ndcube.NDCube.shape` and `~ndcube.NDCube.array_axis_physical_types`.

.. code-block:: python

>>> my_cube.dimensions
<Quantity [4., 4., 5.] pix>
>>> my_cube.shape
(4, 4, 5)
>>> my_cube.array_axis_physical_types
[('custom:pos.helioprojective.lat', 'custom:pos.helioprojective.lon'),
('custom:pos.helioprojective.lat', 'custom:pos.helioprojective.lon'),
('em.wl',)]

`~ndcube.NDCube.dimensions` returns a `~astropy.units.Quantity` in pixel units giving the length of each dimension in the `~ndcube.NDCube`.
`~ndcube.NDCube.shape` returns a tuple giving the length of each dimension in the `~ndcube.NDCube`.
`~ndcube.NDCube.array_axis_physical_types` returns tuples of strings denoting the types of physical properties represented by each array axis.
The tuples are arranged in array axis order, while the physical types inside each tuple are returned in world order.
As more than one physical type can be associated with an array axis, the length of each tuple can be greater than 1.
Expand All @@ -151,8 +151,8 @@ This returns an `~ndcube.NDCubeSequence` where the sequence axis acts as the wav

.. code-block:: python

>>> exploded.dimensions
(<Quantity 5. pix>, <Quantity 4. pix>, <Quantity 4. pix>)
>>> exploded.shape
(5, 4, 4)
>>> exploded.array_axis_physical_types
[('meta.obs.sequence',),
('custom:pos.helioprojective.lat', 'custom:pos.helioprojective.lon'),
Expand Down Expand Up @@ -268,23 +268,22 @@ Let's reinstantiate the `~ndcube.NDCubeSequence` with the common axis as the fir

.. _dimensions:

Dimensions and physical types
-----------------------------
Shape and physical types
------------------------

Analogous to `ndcube.NDCube.dimensions`, there is also a `ndcube.NDCubeSequence.dimensions` property for easily inspecting the shape of an `~ndcube.NDCubeSequence` instance.
Analogous to `ndcube.NDCube.shape`, there is also a `ndcube.NDCubeSequence.shape` property for easily inspecting the shape of an `~ndcube.NDCubeSequence` instance.

.. code-block:: python

>>> my_sequence.dimensions
(<Quantity 4. pix>, <Quantity 4. pix>, <Quantity 4. pix>, <Quantity 5. pix>)
>>> my_sequence.shape
(4, 4, 4, 5)

Slightly differently to `ndcube.NDCube.dimensions`, `ndcube.NDCubeSequence.dimensions` returns a tuple of `astropy.units.Quantity` instances in pixel units, giving the length of each axis.
To see the dimensionality of the sequence in the cube-like paradigm, i.e. taking into account the common axis, use the `ndcube.NDCubeSequence.cube_like_dimensions` property.
To see the dimensionality of the sequence in the cube-like paradigm, i.e. taking into account the common axis, use the `ndcube.NDCubeSequence.cube_like_shape` property.

.. code-block:: python

>>> my_sequence.cube_like_dimensions
<Quantity [16., 4., 5.] pix>
>>> my_sequence.cube_like_shape
[16, 4, 5]

Equivalent to `ndcube.NDCube.array_axis_physical_types`, `ndcube.NDCubeSequence.array_axis_physical_types` returns a list of tuples of physical axis types.
The same `IVOA UCD1+ controlled words <http://www.ivoa.net/documents/REC/UCD/UCDlist-20070402.html>`__ are used for the cube axes.
Expand Down Expand Up @@ -327,10 +326,10 @@ So to explode along the wavelength axis, we should use an array axis index of ``
>>> exploded_sequence = my_sequence.explode_along_axis(2)

>>> # Check old and new shapes of the sequence
>>> my_sequence.dimensions
(<Quantity 4. pix>, <Quantity 4. pix>, <Quantity 4. pix>, <Quantity 5. pix>)
>>> exploded_sequence.dimensions
(<Quantity 20. pix>, <Quantity 4. pix>, <Quantity 4. pix>)
>>> my_sequence.shape
(4, 4, 4, 5)
>>> exploded_sequence.shape
(20, 4, 4)

Note that an `~ndcube.NDCubeSequence` can be exploded along any axis.
A common axis need not be defined and if one is it need not be the axis along which the `~ndcube.NDCubeSequence` is exploded.
Expand Down Expand Up @@ -457,7 +456,7 @@ Because aligned axes must have the same lengths, we can get the lengths of the a
.. code-block:: python

>>> my_collection.aligned_dimensions
<Quantity [4., 4.] pix>
array([4, 4], dtype=object)

Note that this only tells us the lengths of the aligned axes.
To see the lengths of the non-aligned axes, e.g. the spectral axis of the ``observations`` object, you must inspect that ND object individually.
Expand Down
43 changes: 20 additions & 23 deletions docs/explaining_ndcube/slicing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Slicing NDCubes
The `~ndcube.NDCube` slicing infrastructure returns a new `~ndcube.NDCube` with all relevant information consistently altered.
This includes the data, WCS transformations, `~ndcube.ExtraCoords`, uncertainty and mask.
This is achieved by applying the standard slicing API to the `~ndcube.NDCube`.
Because many of the inspection properties such `~ndcube.NDCube.dimensions` and `~ndcube.NDCube.array_axis_physical_types` are calculated on the fly, the information they return after slicing is also consistent with the sliced `~ndcube.NDCube`.
Because many of the inspection properties such `~ndcube.NDCube.shape` and `~ndcube.NDCube.array_axis_physical_types` are calculated on the fly, the information they return after slicing is also consistent with the sliced `~ndcube.NDCube`.
This makes `~ndcube.NDCube`'s slicing infrastructure very powerful.

To slice ``my_cube``, simply do something like:
Expand Down Expand Up @@ -208,12 +208,12 @@ The arrays (blue) in each cube have been sliced and the coordinate objects (red/
:width: 800
:alt: Schematic of an NDCubeSequence after slicing.

We can confirm the dimensionality and physical types of the new sequence by checking the ``.dimensions`` and ``.array_axis_physical_types`` properties.
We can confirm the dimensionality and physical types of the new sequence by checking the ``.shape`` and ``.array_axis_physical_types`` properties.

.. code-block:: python

>>> my_sequence_roi.dimensions
(<Quantity 3. pix>, <Quantity 2. pix>, <Quantity 2. pix>, <Quantity 2. pix>)
>>> my_sequence_roi.shape
(3, 2, 2, 2)
>>> my_sequence_roi.array_axis_physical_types
[('meta.obs.sequence',), ('custom:pos.helioprojective.lat', 'custom:pos.helioprojective.lon'), ('custom:pos.helioprojective.lat', 'custom:pos.helioprojective.lon'), ('em.wl',)]

Expand All @@ -222,8 +222,8 @@ If we want our region of interest to only apply to a single sub-cube, and we ind
.. code-block:: python

>>> single_cube_roi = my_sequence[1, 0, 1:3, 1:4]
>>> single_cube_roi.dimensions
<Quantity [2., 3.] pix>
>>> single_cube_roi.shape
(2, 3)
>>> single_cube_roi.array_axis_physical_types
[('custom:pos.helioprojective.lat', 'custom:pos.helioprojective.lon'),
('em.wl',)]
Expand All @@ -234,8 +234,8 @@ This sequence will still represent the same region of interest from the same sin
.. code-block:: python

>>> roi_length1_sequence = my_sequence[0:1, 0, 1:3, 1:4]
>>> roi_length1_sequence.dimensions
(<Quantity 1. pix>, <Quantity 2. pix>, <Quantity 3. pix>)
>>> roi_length1_sequence.shape
(1, 2, 3)
>>> roi_length1_sequence.array_axis_physical_types
[('meta.obs.sequence',),
('custom:pos.helioprojective.lat', 'custom:pos.helioprojective.lon'),
Expand All @@ -252,22 +252,22 @@ Note that if a common axis is set, we do not have to slice this way.
Instead, we simply have the option of using regular slicing or `ndcube.NDCubeSequence.index_as_cube`.

In the above example, we set the common axis to ``0``.
Recall that, ``my_sequence`` has a shape of ``(<Quantity 4. pix>, <Quantity 4. pix>, <Quantity 4. pix>, <Quantity 5. pix>)``.
Therefore it has ``cube-like`` dimensions of ``(<Quantity 16. pix>, <Quantity 4. pix>, <Quantity 5. pix>)`` where the first sub-cube extends along the 0th cube-like axis from 0 to 4, the second from 4 to 8 and the third from 8 to 12, and the fourth from 12 to 16.
Recall that, ``my_sequence`` has a shape of ``(4, 4, 4, 5)``.
Therefore it has ``cube-like`` dimensions of ``[16, 4, 5]`` where the first sub-cube extends along the 0th cube-like axis from 0 to 4, the second from 4 to 8 and the third from 8 to 12, and the fourth from 12 to 16.

.. code-block:: python

>>> my_sequence.cube_like_dimensions
<Quantity [16., 4., 5.] pix>
>>> my_sequence.cube_like_shape
[16, 4, 5]

Now say we want to extract the same region of interest as above, i.e. ``my_sequence[1, 0, 1:3, 1:4]``.
This can be achieved by entering:

.. code-block:: python

>>> single_cube_roi = my_sequence.index_as_cube[4, 1:3, 1:4]
>>> single_cube_roi.dimensions
<Quantity [2., 3.] pix>
>>> single_cube_roi.shape
(2, 3)
>>> single_cube_roi.array_axis_physical_types
[('custom:pos.helioprojective.lat', 'custom:pos.helioprojective.lon'),
('em.wl',)]
Expand All @@ -279,8 +279,8 @@ As before, the same region of interest from the same sub-cube is represented, ju
.. code-block:: python

>>> roi_length1_sequence = my_sequence.index_as_cube[4:5, 1:3, 1:4]
>>> roi_length1_sequence.dimensions
(<Quantity 1. pix>, <Quantity 1. pix>, <Quantity 2. pix>, <Quantity 3. pix>)
>>> roi_length1_sequence.shape
(1, 1, 2, 3)
>>> roi_length1_sequence.array_axis_physical_types
[('meta.obs.sequence',),
('custom:pos.helioprojective.lat', 'custom:pos.helioprojective.lon'),
Expand All @@ -295,11 +295,8 @@ In cube-like indexing this corresponds to slices 3 to 9 along to their 1st cube
.. code-block:: python

>>> roi_across_cubes = my_sequence.index_as_cube[3:9, 1:3, 1:4]
>>> roi_across_cubes.dimensions
(<Quantity 3. pix>,
<Quantity [1., 4., 1.] pix>,
<Quantity 2. pix>,
<Quantity 3. pix>)
>>> roi_across_cubes.shape
(3, (1, 4, 1), 2, 3)
>>> roi_across_cubes.array_axis_physical_types
[('meta.obs.sequence',),
('custom:pos.helioprojective.lat', 'custom:pos.helioprojective.lon'),
Expand Down Expand Up @@ -363,7 +360,7 @@ Thus a self-consistent result is obtained.
>>> sliced_collection.keys()
dict_keys(['observations', 'linewidths'])
>>> sliced_collection.aligned_dimensions
<Quantity [2.0, 1.0] pix>
array([2, 1], dtype=object)

This is true even if the aligned axes are not in order.
Let's say we axis order of the ``linewidths`` cube was reversed.
Expand All @@ -386,6 +383,6 @@ Let's say we axis order of the ``linewidths`` cube was reversed.
>>> sliced_collection_reversed.keys()
dict_keys(['observations', 'linewidths'])
>>> sliced_collection_reversed.aligned_dimensions
<Quantity [2.0, 1.0] pix>
array([2, 1], dtype=object)

The same result is obtained.
2 changes: 1 addition & 1 deletion examples/creating_ndcube_from_fitsfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
##########################################################################
# and we can also inspect the dimensions.

print(example_ndcube.dimensions)
print(example_ndcube.shape)

##########################################################################
# We can also quickly visualize the data using the :meth:`~ndcube.NDCube.plot` method.
Expand Down
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
2 changes: 1 addition & 1 deletion ndcube/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ def ndcube_2d_ln_lt_uncert_ec(wcs_2d_lt_ln):
cube = NDCube(data_cube, wcs=wcs_2d_lt_ln, uncertainty=uncertainty)
cube.extra_coords.add(
"time", 0,
Time("2000-01-01 00:00", scale="utc") + Timedelta(np.arange(shape[0])*60, format="sec"))
Time("2000-01-01 00:00", scale="utc") + TimeDelta(np.arange(shape[0])*60, format="sec"))
return cube


Expand Down
4 changes: 2 additions & 2 deletions ndcube/extra_coords/extra_coords.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@
# The mapping is from the array index (position in the list) to the
# pixel dimensions (numbers in the list)
lts = [list([lt[0]] if isinstance(lt[0], Integral) else lt[0]) for lt in self._lookup_tables]
converter = partial(convert_between_array_and_pixel_axes, naxes=len(self._ndcube.dimensions))
converter = partial(convert_between_array_and_pixel_axes, naxes=len(self._ndcube.shape))

Check warning on line 264 in ndcube/extra_coords/extra_coords.py

View check run for this annotation

Codecov / codecov/patch

ndcube/extra_coords/extra_coords.py#L264

Added line #L264 was not covered by tests
pixel_indicies = [list(converter(np.array(ids))) for ids in lts]
return tuple(reduce(list.__add__, pixel_indicies))

Expand Down Expand Up @@ -527,7 +527,7 @@
@property
def _cube_array_axes_without_extra_coords(self):
"""Return the array axes not associated with any extra coord."""
return set(range(len(self._ndcube.dimensions))) - set(self.mapping)
return set(range(len(self._ndcube.shape))) - set(self.mapping)

Check warning on line 530 in ndcube/extra_coords/extra_coords.py

View check run for this annotation

Codecov / codecov/patch

ndcube/extra_coords/extra_coords.py#L530

Added line #L530 was not covered by tests

def __str__(self):
classname = self.__class__.__name__
Expand Down
Loading