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
Arithmetic operations between two `~ndcube.NDCube` instances (or equivalently, an `~ndcube.NDCube` and another coordinate-aware object) are not supported because of the possibility of supporting non-sensical operations.
239
+
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.
218
240
For example, what does it mean to multiply a spectrum and an image in a coordinate-aware way?
219
241
Getting the difference between two images may make physical sense, but only in certain circumstances.
220
242
For example, subtracting two sequential images of the same region of the Sun is a common step in many solar image analysis workflows.
@@ -223,7 +245,7 @@ Even when subtracting two images of the Sun, drift in the telescope's pointing m
223
245
In this case, it is questionable whether this operation makes physical sense after all.
224
246
Moreover, in all of these cases, it is not at all clear what the resulting WCS object should be.
225
247
226
-
In many cases, a simple solution would be to extract the data (an optionally the unit) of one of the `~ndcube.NDCube` instances and perform the operation as described in the above section on :ref:`arithmetic_standard`:
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`:
227
249
228
250
.. expanding-code-block:: python
229
251
:summary: Expand to see definition of cube1 and cube2.
@@ -235,7 +257,7 @@ In many cases, a simple solution would be to extract the data (an optionally the
235
257
236
258
>>> new_cube = cube1 - cube2.data * cube2.unit
237
259
238
-
However, this does not allow for the propagation of uncertainties or masks associated with the data in ``cube2``.
260
+
However, this does not allow for the propagation of uncertainties or masks associated with ``cube2``.
239
261
Therefore, `~ndcube.NDCube` does support arithmetic operations with instances of `~astropy.nddata.NDData` subclasses whose ``wcs`` attribute is ``None``.
240
262
This makes users explicitly aware that they are dispensing with coordinate-awareness on one of their operands.
241
263
It also leaves only one WCS involved in the operation, thus removing ambiguity regarding the WCS of the `~ndcube.NDCube` resulting from the operation.
@@ -251,3 +273,71 @@ Users who would like to drop coordinate-awareness from an `~ndcube.NDCube` can s
251
273
252
274
Performing Arithmetic Operations with Coordinate-less NDData
Addition, subtraction, multiplication and division between `~ndcube.NDCube` and coordinate-less `~astropy.nddata.NDData` classes are all supported via the ``+``, ``-``, ``*``, and ``/`` operators.
278
+
With respect to the ``data`` and ``unit`` attributes, the behaviors are the same as for arrays and `~astropy.units.Quantity`.
279
+
The power of using coordinate-less `~astropy.nddata.NDData` classes is the ability to handle uncertainties and masks.
280
+
281
+
Uncertainty Propagation
282
+
***********************
283
+
284
+
The uncertainty associated with the `~ndcube.NDCube` resulting from the arithmetic operation depends on the uncertainty types of the operands:
285
+
286
+
- ``NDCube.uncertainty`` and ``NDData.uncertainty`` are both ``None`` => ``new_cube.uncertainty`` is ``None``;
287
+
- ``NDCube`` or ``NDData`` have uncertainty, but not both => the existing uncertainty is assigned to ``new_cube`` as is;
288
+
- ``NDCube`` and ``NDData`` both have uncertainty => uncertainty propagation is delegated to the ``NDCube.uncertainty.propagate`` method.
289
+
290
+
* Note that not all uncertainty classes support error propagation, e.g. `~astropy.nddata.UnknownUncertainty`. In such cases, uncertainties are dropped altogether and ``new_cube.uncertainty`` is set to ``None``.
291
+
292
+
If users would like to remove uncertainty from one of the operands in order to propagate the other without alteration, this can be done before the arithmetic operation via:
293
+
294
+
.. code-block:: python
295
+
296
+
>>># Remove uncertainty from NDCube
297
+
>>> cube1_nouncert = NDCube(cube2, wcs=None)
298
+
>>> new_cube = cube1_nouncert + cube2_nocoords
299
+
300
+
>>># Remove uncertainty from coordinate-less NDData
The mask associated with the `~ndcube.NDCube` resulting from the arithmetic operation depends on the mask types of the operands:
308
+
309
+
- ``NDCube.mask`` and ``NDData.mask`` are both ``None`` => ``new_cube.mask`` is ``None``;
310
+
- ``NDCube`` or ``NDData`` have a mask, but not both => the existing mask is assigned to ``new_cube`` as is;
311
+
- ``NDCube`` and ``NDData`` both have masks => The masks are combined via `numpy.logical_or`.
312
+
313
+
The mask values do not affect the ``data`` values output by the operation.
314
+
However, in some cases, the mask may be used to identify regions of unreliable data that should not be included in the operation.
315
+
This can be achieved by altering the masked data values before the operation via the `ndcube.NDCube.fill_masked` method.
316
+
In the case of addition and subtraction, the ``fill_value`` should be ``0``.
317
+
318
+
.. code-block:: python
319
+
320
+
>>> cube_filled = cube1.fill_masked(0)
321
+
>>> new_cube = cube_filled + cube2_nocoords
322
+
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.
324
+
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
+
Propagation of masked uncertainties can also be suppressed by setting the optional kwarg, ``fill_uncertainty_value=0``.
326
+
By default, the mask of ``cube_filled`` is not changed, and therefore is incorporated into the mask of the output cube.
327
+
However, mask propagation can also be suppressed by setting the optional kwarg, ``unmask=True``, which sets ``cube_filled0.mask`` to ``False``.
328
+
329
+
In the case of multiplication and division, and ``fill_value`` of ``1`` will prevent masked values being including in the operations:
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.
0 commit comments