Skip to content

Commit

Permalink
Merge pull request #684 from CyclingNinja/remove_dimension
Browse files Browse the repository at this point in the history
Adds shape property to NDCube
  • Loading branch information
DanRyanIrish committed Apr 24, 2024
2 parents fc52e0f + 8ed4e5f commit 6d1473c
Show file tree
Hide file tree
Showing 29 changed files with 299 additions and 430 deletions.
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: 0 additions & 2 deletions docs/reference/utils.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,4 @@ utils (`ndcube.utils`)

.. automodapi:: ndcube.utils.sequence

.. automodapi:: ndcube.utils.wcs_high_level_conversion

.. automodapi:: ndcube.utils.wcs
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 @@ def mapping(self):
# 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))
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 @@ def cube_wcs(self):
@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)

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

0 comments on commit 6d1473c

Please sign in to comment.