You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/explaining_ndcube/arithmetic.rst
+53-35Lines changed: 53 additions & 35 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -6,25 +6,25 @@ Arithmetic Operations
6
6
7
7
Arithmetic operations are a crucial tool in n-dimensional data analysis.
8
8
Applications include subtracting a background from a 1-D timeseries or spectrum, scaling an image by a vignetting function, and many others.
9
-
To aid with such workflows, `~ndcube.NDCube` supports addition, subtraction, multiplication, and division with numbers, arrays, `~astropy.units.Quantity`.
9
+
To aid with such workflows, `~ndcube.NDCube` supports addition, subtraction, multiplication, and division with numbers, arrays, and `~astropy.units.Quantity`.
10
10
Raising an `~ndcube.NDCube` to a power is also supported.
11
11
These operations return a new `~ndcube.NDCube` with the data array (and, where appropriate, the uncertainties) altered in accordance with the arithmetic operation.
12
12
Other attributes of the `~ndcube.NDCube` remain unchanged.
13
13
14
-
In addition, combining `~ndcube.NDCube` with coordinate-less `~astropy.nddata.NDData` subclasses via these operations is also supported.
15
-
Such operations can be more complicated. See the section below on :ref:`arithmetic_nddata` for a discussion separate, more detailed discussion.
14
+
In addition, arithmetic operations between `~ndcube.NDCube` and coordinate-less `~astropy.nddata.NDData` subclasses is supported.
15
+
See the section below on :ref:`arithmetic_nddata` for a separate, more detailed discussion.
16
16
17
17
.. _arithmetic_standard:
18
18
19
-
Standard Arithmetic Operations
20
-
==============================
19
+
Arithmetic Operations with Numbers, Arrays and Quantities
An `~ndcube.NDCube` can be multiplied and divided by numbers, arrays, and `~astropy.units.Quantity` via the ``*`` and ``-`` operators.
143
143
These work similarly to addition and subtraction with a few minor differences:
144
144
145
145
- The uncertainties of the resulting `~ndcube.NDCube` are scaled by the same factor as the data.
146
-
- Classes with different units can be combined.
146
+
- Classes with different non-equivalent units can be combined.
147
147
148
-
* e.g. an `~ndcube.NDCube` with a unit of counts divided by an `~astropy.units.Quantity` with a unit is seconds will result in an `~ndcube.NDCube` with a unit of counts per second.
148
+
* e.g. an `~ndcube.NDCube` with a unit of ``ct`` divided by an `~astropy.units.Quantity` with a unit of ``s`` will result in an `~ndcube.NDCube` with a unit of ``ct / s``.
149
149
* This also holds for cases were unitful and unitless classes can be combined. In such cases, the unit of the resulting `~ndcube.NDCube` will be the same as that of the unitful object.
150
150
151
151
Below are some examples.
@@ -180,7 +180,7 @@ Below are some examples.
180
180
StdDevUncertainty([[1. , 2.2, 3.6],
181
181
[5.2, 7. , 9. ]])
182
182
183
-
>>># Divide by an astropy Quantity.
183
+
>>># Divide by a scalar astropy Quantity.
184
184
>>> new_cube = cube_with_unit / (2* u.s)
185
185
186
186
>>># Inspect attributes of resultant cube.
@@ -193,14 +193,22 @@ Below are some examples.
193
193
StdDevUncertainty([[0.5 , 0.55, 0.6 ],
194
194
[0.65, 0.7 , 0.75]])
195
195
196
-
Note that when performing arithmetic operations with `~ndcube.NDCube` and array-like objects, their shapes only have to be broadcastable.
196
+
Note that when performing arithmetic operations with `~ndcube.NDCube` and array-like objects, their shapes only have to be broadcastable, not necessarily the same.
197
197
For example:
198
198
199
+
.. code-block:: python
200
+
201
+
>>> arr[0]
202
+
array([1, 2, 3])
203
+
204
+
>>> new_cube = cube + arr[0]
205
+
>>> new_cube.data
206
+
array([[11, 13, 15],
207
+
[14, 16, 18]])
208
+
199
209
Raising NDCube to a Power
200
210
-------------------------
201
211
202
-
`~ndcube.NDCube` can be raised to a power.
203
-
204
212
.. code-block:: python
205
213
206
214
>>> cube_with_unit.data
@@ -228,24 +236,33 @@ Arithmetic Operations with Coordinate-less NDData
228
236
=================================================
229
237
230
238
Sometimes more advanced arithmetic operations are required.
231
-
For example, we may want to create a sequence of running difference images which highlight changes between frames, and propagate the uncertainties associated with each image.
239
+
For example, we may want to create a sequence of running difference images which highlight changes between frames, and propagate the uncertainties associated with the image subtraction.
232
240
Alternatively, we may want to subtract one image from another, but exclude a certain region of the image with a mask.
233
-
In such cases, numbers, arrays and `~astropy.units.Quantity` are insufficient, and we would like to subtract two `~ndcube.NDCube` objects.
234
-
This is not directly supported, but can still be achieved in practice, as we shall see below.
241
+
In such cases, numbers, arrays and `~astropy.units.Quantity` are insufficient.
242
+
Instead it would be better to subtract two `~ndcube.NDCube` objects.
243
+
This is not directly supported, for reasons we will see below.
244
+
The the effect of the operation can still be achieved in practice, as well shall also see.
235
245
236
246
Why Arithmetic Operations with Coordinate-aware NDData Are Not Directly Supported, and How This Can Be Overcome
Arithmetic operations between two `~ndcube.NDCube` instances (or equivalently, an `~ndcube.NDCube` and another coordinate-aware `~astropy.nddata.NDData` subclass) are not supported because of the possibility of supporting non-sensical operations.
240
-
For example, what does it mean to multiply a spectrum and an image in a coordinate-aware way?
249
+
The remit of the `ndcube` package is support N-dimensional coordinate-aware data astronomical data analysis.
250
+
Arithmetic operations between two `~ndcube.NDCube` instances (or equivalently, an `~ndcube.NDCube` and another coordinate-aware `~astropy.nddata.NDData` subclass) are therefore not directly supported because of the possibility of supporting non-sensical operations.
251
+
(Although they are supported indirectly, as we shall see below.)
252
+
For example, what does it mean to multiply a spectrum and an image?
241
253
Getting the difference between two images may make physical sense, but only in certain circumstances.
242
254
For example, subtracting two sequential images of the same region of the Sun is a common step in many solar image analysis workflows.
243
-
However, subtracting images of different parts of the sky, e.g. the Sun and the Crab Nebula, does not result in a physically meaningful image.
255
+
But subtracting images of different parts of the sky, e.g. the Sun and the Crab Nebula, does not produce a physically meaningful result.
244
256
Even when subtracting two images of the Sun, drift in the telescope's pointing may result in the pixels in each image corresponding to different points in the Sun.
245
-
In this case, it is questionable whether this operation makes physical sense after all.
257
+
In this case, it is questionable whether even this operation makes physical sense.
246
258
Moreover, in all of these cases, it is not at all clear what the resulting WCS object should be.
247
259
248
-
In many cases, a simple solution would be to extract the data (an optionally the unit) from one of the `~ndcube.NDCube` instances and perform the operation as described in the above section on :ref:`arithmetic_standard`:
260
+
One way to ensure physically meaningful, coordinate-aware arithmetic operations between `~ndcube-NDCube` instances would be to compare their WCS objects are the same within a certain tolerance.
261
+
Alternatively, the arithmetic operation could attempt to reproject on `~ndcube-NDCube` to the other's WCS.
262
+
However, these operations are potentially prohibitively slow and operationally expensive.
263
+
264
+
Despite this, arithmetic operations between two `~ndcube.NDCube` instance is supported, provided the coordinate-awareness of one is dropped.
265
+
A simple solution that satisfies many use-cases is to extract the data (an optionally the unit) from one of the `~ndcube.NDCube` instances and perform the operation as described in the above section on :ref:`arithmetic_standard`:
249
266
250
267
.. expanding-code-block:: python
251
268
:summary: Expand to see definition of cube1 and cube2.
@@ -258,8 +275,8 @@ In many cases, a simple solution would be to extract the data (an optionally the
258
275
>>> new_cube = cube1 - cube2.data * cube2.unit
259
276
260
277
However, this does not allow for the propagation of uncertainties or masks associated with ``cube2``.
261
-
Therefore, `~ndcube.NDCube` does support arithmetic operations with instances of `~astropy.nddata.NDData` subclasses whose ``wcs`` attribute is ``None``.
262
-
This makes users explicitly aware that they are dispensing with coordinate-awareness on one of their operands.
278
+
Therefore, `~ndcube.NDCube` also support arithmetic operations with instances of `~astropy.nddata.NDData` subclasses whose ``wcs`` attribute is ``None``.
279
+
Requiring users to remove coordinates in this way makes them explicitly aware that they are dispensing with coordinate-awareness on one of their operands.
263
280
It also leaves only one WCS involved in the operation, thus removing ambiguity regarding the WCS of the `~ndcube.NDCube` resulting from the operation.
264
281
265
282
Users who would like to drop coordinate-awareness from an `~ndcube.NDCube` can so simply by converting it to an `~astropy.nddata.NDData` and setting the ``wcs`` to ``None``:
@@ -281,7 +298,7 @@ The power of using coordinate-less `~astropy.nddata.NDData` classes is the abili
281
298
Uncertainty Propagation
282
299
***********************
283
300
284
-
The uncertainty associated with the `~ndcube.NDCube` resulting from the arithmetic operation depends on the uncertainty types of the operands:
301
+
The uncertainty resulting from the arithmetic operation depends on the uncertainty types of the operands:
285
302
286
303
- ``NDCube.uncertainty`` and ``NDData.uncertainty`` are both ``None`` => ``new_cube.uncertainty`` is ``None``;
287
304
- ``NDCube`` or ``NDData`` have uncertainty, but not both => the existing uncertainty is assigned to ``new_cube`` as is;
@@ -294,7 +311,7 @@ If users would like to remove uncertainty from one of the operands in order to p
294
311
.. code-block:: python
295
312
296
313
>>># Remove uncertainty from NDCube
297
-
>>> cube1_nouncert = NDCube(cube2, wcs=None)
314
+
>>> cube1_nouncert = NDCube(cube1, wcs=None)
298
315
>>> new_cube = cube1_nouncert + cube2_nocoords
299
316
300
317
>>># Remove uncertainty from coordinate-less NDData
@@ -320,22 +337,23 @@ In the case of addition and subtraction, the ``fill_value`` should be ``0``.
320
337
>>> cube_filled = cube1.fill_masked(0)
321
338
>>> new_cube = cube_filled + cube2_nocoords
322
339
323
-
By replacing masked data values with ``0``, these pixels are effectively not included in the addition, and the data values from ``cube2_nocoords`` are passed into ``new_cube`` unchanged.
340
+
By replacing masked data values with ``0``, these values are effectively not included in the addition, and the data values from ``cube2_nocoords`` are passed into ``new_cube`` unchanged.
324
341
In the above example, both operands have uncertainties, which means masked uncertainties are propagated through the addition, even though the masked data values have been set to ``0``.
325
342
Propagation of masked uncertainties can also be suppressed by setting the optional kwarg, ``fill_uncertainty_value=0``.
343
+
326
344
By default, the mask of ``cube_filled`` is not changed, and therefore is incorporated into the mask of the output cube.
327
345
However, mask propagation can also be suppressed by setting the optional kwarg, ``unmask=True``, which sets ``cube_filled0.mask`` to ``False``.
328
346
329
-
In the case of multiplication and division, and ``fill_value`` of ``1`` will prevent masked values being including in the operations:
347
+
In the case of multiplication and division, a ``fill_value`` of ``1`` will prevent masked values being including in the operations. (Also see the optional use of the ``fill_uncertainty_value`` and ``unmask`` kwargs.)
By default, `ndcube.NDCube.fill_masked` returns a new `~ndcube.NDCube` instance.
337
-
However, in some case it may be preferable to fill the masked values in-place, e.g. because the data within the `~ndcube.NDCube` is very large and users want to control the number of copies in RAM.
338
-
In this case, the ``fill_in_place`` can be used.
355
+
However, in some case it may be preferable to fill the masked values in-place, for example because the data within the `~ndcube.NDCube` is very large and users want to control the number of copies in RAM.
356
+
In this case, the ``fill_in_place`` kwarg can be used.
0 commit comments