From 8bb8e9f9ac36fed2d386f1f78d34c2749b1a6bbe Mon Sep 17 00:00:00 2001 From: JunyanHuo Date: Fri, 14 Mar 2025 20:17:16 +0000 Subject: [PATCH 01/25] New PR, copied ndcube.py from the other branch nddataArithmetic. --- ndcube/ndcube.py | 122 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 117 insertions(+), 5 deletions(-) diff --git a/ndcube/ndcube.py b/ndcube/ndcube.py index 5f5f4ee40..cf397e267 100644 --- a/ndcube/ndcube.py +++ b/ndcube/ndcube.py @@ -11,6 +11,7 @@ import astropy.nddata import astropy.units as u +from astropy.nddata import NDData from astropy.units import UnitsError from astropy.wcs.utils import _split_matrix @@ -964,15 +965,87 @@ def _new_instance(self, **kwargs): def __neg__(self): return self._new_instance(data=-self.data) - def __add__(self, value): - if hasattr(value, 'unit'): + def add(self, value, operation_ignores_mask=True, handle_mask=np.logical_and): + """ + Users are allowed to choose whether they want operation_ignores_mask to be True or False, + and are allowed to choose whether they want handle_mask to be AND / OR . + """ + kwargs = {} + + if isinstance(value, NDData) and value.wcs is None: + if self.unit is not None and value.unit is not None: + value_data = (value.data * value.unit).to_value(self.unit) + elif self.unit is None and value.unit is None: + value_data = value.data + else: + raise TypeError("Adding objects requires both have a unit or neither has a unit.") # change the test as well. + + # check whether there is a mask. + # Neither self nor value has a mask + self_unmasked = self.mask is None or self.mask is False or not self.mask.any() + value_unmasked = value.mask is None or value.mask is False or not value.mask.any() + + if (self_unmasked and value_unmasked) or operation_ignores_mask is True: + # addition + kwargs["data"] = self.data + value_data + + # combine the uncertainty; + if self.uncertainty is not None and value.uncertainty is not None: + result_data = kwargs["data"] + if self.unit is not None: + result_data *= self.unit + new_uncertainty = self.uncertainty.propagate( + np.add, value, result_data=result_data, correlation=0 + ) + kwargs["uncertainty"] = new_uncertainty + elif self.uncertainty is not None: + new_uncertainty = self.uncertainty + kwargs["uncertainty"] = new_uncertainty + elif value.uncertainty is not None: + new_uncertainty = value.uncertainty + kwargs["uncertainty"] = new_uncertainty + else: + new_uncertainty = None + else: + # TODO + # When there is a mask, that is when the two new added parameters (OIM and HM) come into the picture. + # Conditional statements to permutate the two different scenarios (when it does not ignore the mask). + kwargs["data"] = self.data + value_data + self_data, value_data = self.data, value.data # May require a copy + self_mask, value_mask = self.mask, value.mask # May require handling/converting of cases when masks aren't boolean arrays but are None, True, or False. + if not operation_ignores_mask: + no_op_value = 0 # Value to set masked values since we are doing addition. (Would need to be 1 if we were doing multiplication.) + if (self_mask is True and value_mask is False): + idx = np.logical_and(self_mask, np.logical_not(value_mask)) + self_data[idx] = no_op_value + elif (self_mask is False and value_mask is True): + idx = np.logical_and(value_mask, np.logical_not(self_mask)) + value_data[idx] = no_op_value + elif (self_mask is True and value_mask is True): + idx = np.logical_and(self_mask, value_mask) + self_data[idx] = no_op_value + value_data[idx] = no_op_value + + #self_data[idx], value_data[idx] = ?, ? # Handle case when both values are masked here. # We are yet to decide the best behaviour here. + # if both are F, no operation of setting the values to be 0 needs to be done. + else: + pass # If operation ignores mask, nothing needs to be done. This line not needed in actual code. Only here for clarity. + + # Perform addition + new_data = self_data + value_data + # Calculate new mask. + new_mask = handle_mask(self_mask, value_mask) if handle_mask else None + kwargs["data"] = new_data + kwargs["mask"] = new_mask + + elif hasattr(value, 'unit'): if isinstance(value, u.Quantity): # NOTE: if the cube does not have units, we cannot # perform arithmetic between a unitful quantity. # This forces a conversion to a dimensionless quantity # so that an error is thrown if value is not dimensionless cube_unit = u.Unit('') if self.unit is None else self.unit - new_data = self.data + value.to_value(cube_unit) + kwargs["data"] = self.data + value.to_value(cube_unit) else: # NOTE: This explicitly excludes other NDCube objects and NDData objects # which could carry a different WCS than the NDCube @@ -980,8 +1053,25 @@ def __add__(self, value): elif self.unit not in (None, u.Unit("")): raise TypeError("Cannot add a unitless object to an NDCube with a unit.") else: - new_data = self.data + value - return self._new_instance(data=new_data) + kwargs["data"] = self.data + value + + # return the new NDCube instance + return self._new_instance(**kwargs) + + def __add__(self, value): + # when value has a mask, raise error and point user to the add method. TODO + # + # check whether there is a mask. + # Neither self nor value has a mask + + self_masked = not(self.mask is None or self.mask is False or not self.mask.any()) + value_masked = not(value.mask is None or value.mask is False or not value.mask.any()) if hasattr(value, "mask") else False + + if (value_masked or (self_masked and hasattr(value,'uncertainty') and value.uncertainty is not None)): # value has a mask, + # let the users call the add method + raise TypeError('Please use the add method.') + + return self.add(value) # the mask keywords cannot be given by users. def __radd__(self, value): return self.__add__(value) @@ -1330,6 +1420,28 @@ def squeeze(self, axis=None): return self[tuple(item)] +def fill(fill_value, unmask=False, uncertainty_fill_value=None, fill_in_place=False): + """ + Replaces masked data values with input value. + + Returns a new instance or alters values in place. + + Parameters + ---------- + fill_value: `numbers.Number` or scalar `astropy.unit.Quantity` + The value to replace masked data with. + unmask: `bool`, optional + If True, the newly filled masked values are unmasked. If False, they remain masked + Default=False + uncertainty_fill_value: `numbers.Number` or scalar `astropy.unit.Quantity`, optional + The value to replace masked uncertainties with. + fill_in_place: `bool`, optional + If `True`, the masked values are filled in place. If `False`, a new instance is returned + with masked values filled. Default=False. + """ + # ...code implementation here. + + def _create_masked_array_for_rebinning(data, mask, operation_ignores_mask): m = None if (mask is None or mask is False or operation_ignores_mask) else mask if m is None: From 4e5dcd06c6c90615d695e7016d281d3b73437bfa Mon Sep 17 00:00:00 2001 From: JunyanHuo Date: Fri, 14 Mar 2025 20:24:24 +0000 Subject: [PATCH 02/25] Changed the code to be consistent with main/ndcube.py instead. --- ndcube/ndcube.py | 100 +++-------------------------------------------- 1 file changed, 5 insertions(+), 95 deletions(-) diff --git a/ndcube/ndcube.py b/ndcube/ndcube.py index cf397e267..e06228960 100644 --- a/ndcube/ndcube.py +++ b/ndcube/ndcube.py @@ -11,7 +11,6 @@ import astropy.nddata import astropy.units as u -from astropy.nddata import NDData from astropy.units import UnitsError from astropy.wcs.utils import _split_matrix @@ -965,87 +964,15 @@ def _new_instance(self, **kwargs): def __neg__(self): return self._new_instance(data=-self.data) - def add(self, value, operation_ignores_mask=True, handle_mask=np.logical_and): - """ - Users are allowed to choose whether they want operation_ignores_mask to be True or False, - and are allowed to choose whether they want handle_mask to be AND / OR . - """ - kwargs = {} - - if isinstance(value, NDData) and value.wcs is None: - if self.unit is not None and value.unit is not None: - value_data = (value.data * value.unit).to_value(self.unit) - elif self.unit is None and value.unit is None: - value_data = value.data - else: - raise TypeError("Adding objects requires both have a unit or neither has a unit.") # change the test as well. - - # check whether there is a mask. - # Neither self nor value has a mask - self_unmasked = self.mask is None or self.mask is False or not self.mask.any() - value_unmasked = value.mask is None or value.mask is False or not value.mask.any() - - if (self_unmasked and value_unmasked) or operation_ignores_mask is True: - # addition - kwargs["data"] = self.data + value_data - - # combine the uncertainty; - if self.uncertainty is not None and value.uncertainty is not None: - result_data = kwargs["data"] - if self.unit is not None: - result_data *= self.unit - new_uncertainty = self.uncertainty.propagate( - np.add, value, result_data=result_data, correlation=0 - ) - kwargs["uncertainty"] = new_uncertainty - elif self.uncertainty is not None: - new_uncertainty = self.uncertainty - kwargs["uncertainty"] = new_uncertainty - elif value.uncertainty is not None: - new_uncertainty = value.uncertainty - kwargs["uncertainty"] = new_uncertainty - else: - new_uncertainty = None - else: - # TODO - # When there is a mask, that is when the two new added parameters (OIM and HM) come into the picture. - # Conditional statements to permutate the two different scenarios (when it does not ignore the mask). - kwargs["data"] = self.data + value_data - self_data, value_data = self.data, value.data # May require a copy - self_mask, value_mask = self.mask, value.mask # May require handling/converting of cases when masks aren't boolean arrays but are None, True, or False. - if not operation_ignores_mask: - no_op_value = 0 # Value to set masked values since we are doing addition. (Would need to be 1 if we were doing multiplication.) - if (self_mask is True and value_mask is False): - idx = np.logical_and(self_mask, np.logical_not(value_mask)) - self_data[idx] = no_op_value - elif (self_mask is False and value_mask is True): - idx = np.logical_and(value_mask, np.logical_not(self_mask)) - value_data[idx] = no_op_value - elif (self_mask is True and value_mask is True): - idx = np.logical_and(self_mask, value_mask) - self_data[idx] = no_op_value - value_data[idx] = no_op_value - - #self_data[idx], value_data[idx] = ?, ? # Handle case when both values are masked here. # We are yet to decide the best behaviour here. - # if both are F, no operation of setting the values to be 0 needs to be done. - else: - pass # If operation ignores mask, nothing needs to be done. This line not needed in actual code. Only here for clarity. - - # Perform addition - new_data = self_data + value_data - # Calculate new mask. - new_mask = handle_mask(self_mask, value_mask) if handle_mask else None - kwargs["data"] = new_data - kwargs["mask"] = new_mask - - elif hasattr(value, 'unit'): + def __add__(self, value): + if hasattr(value, 'unit'): if isinstance(value, u.Quantity): # NOTE: if the cube does not have units, we cannot # perform arithmetic between a unitful quantity. # This forces a conversion to a dimensionless quantity # so that an error is thrown if value is not dimensionless cube_unit = u.Unit('') if self.unit is None else self.unit - kwargs["data"] = self.data + value.to_value(cube_unit) + new_data = self.data + value.to_value(cube_unit) else: # NOTE: This explicitly excludes other NDCube objects and NDData objects # which could carry a different WCS than the NDCube @@ -1053,25 +980,8 @@ def add(self, value, operation_ignores_mask=True, handle_mask=np.logical_and): elif self.unit not in (None, u.Unit("")): raise TypeError("Cannot add a unitless object to an NDCube with a unit.") else: - kwargs["data"] = self.data + value - - # return the new NDCube instance - return self._new_instance(**kwargs) - - def __add__(self, value): - # when value has a mask, raise error and point user to the add method. TODO - # - # check whether there is a mask. - # Neither self nor value has a mask - - self_masked = not(self.mask is None or self.mask is False or not self.mask.any()) - value_masked = not(value.mask is None or value.mask is False or not value.mask.any()) if hasattr(value, "mask") else False - - if (value_masked or (self_masked and hasattr(value,'uncertainty') and value.uncertainty is not None)): # value has a mask, - # let the users call the add method - raise TypeError('Please use the add method.') - - return self.add(value) # the mask keywords cannot be given by users. + new_data = self.data + value + return self._new_instance(data=new_data) def __radd__(self, value): return self.__add__(value) From 4ffb573f2d40ed5b510e7597a61614e4d0953875 Mon Sep 17 00:00:00 2001 From: JunyanHuo Date: Fri, 14 Mar 2025 21:30:40 +0000 Subject: [PATCH 03/25] Implementing NDCube.fill(). --- ndcube/ndcube.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/ndcube/ndcube.py b/ndcube/ndcube.py index e06228960..edb1e209f 100644 --- a/ndcube/ndcube.py +++ b/ndcube/ndcube.py @@ -1330,7 +1330,7 @@ def squeeze(self, axis=None): return self[tuple(item)] -def fill(fill_value, unmask=False, uncertainty_fill_value=None, fill_in_place=False): +def fill(self, fill_value, unmask=False, uncertainty_fill_value=None, fill_in_place=False): """ Replaces masked data values with input value. @@ -1350,6 +1350,23 @@ def fill(fill_value, unmask=False, uncertainty_fill_value=None, fill_in_place=Fa with masked values filled. Default=False. """ # ...code implementation here. + kwargs = {} + kwargs["data"] = self.data + kwargs["mask"] = self.mask + kwargs["uncertainty"] = self.uncertainty + + # If there is a not None mask and a not None fill_value, do: change the corresponding data to fill_value. + if (fill_value is not None and self.mask is not None): + kwargs["data"][self.mask] = fill_value # Boolean indexing in Python. + # else, do nothing. + + # if unmask is True, do: change the True mask values to False, otherwise, do nothing to the mask. + if (unmask): + kwargs["mask"] = False + + if (self.mask is not None and uncertainty_fill_value is not None): + kwargs["uncertainty"][self.mask] = uncertainty_fill_value + def _create_masked_array_for_rebinning(data, mask, operation_ignores_mask): From fb36c8072a42786ac7ed97d83df3bf50bb7cbdcf Mon Sep 17 00:00:00 2001 From: JunyanHuo Date: Fri, 14 Mar 2025 21:58:06 +0000 Subject: [PATCH 04/25] Added changelog, implementing NDCube.fill(). --- ndcube/ndcube.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ndcube/ndcube.py b/ndcube/ndcube.py index edb1e209f..2c1112eba 100644 --- a/ndcube/ndcube.py +++ b/ndcube/ndcube.py @@ -1358,7 +1358,7 @@ def fill(self, fill_value, unmask=False, uncertainty_fill_value=None, fill_in_pl # If there is a not None mask and a not None fill_value, do: change the corresponding data to fill_value. if (fill_value is not None and self.mask is not None): kwargs["data"][self.mask] = fill_value # Boolean indexing in Python. - # else, do nothing. + # else, do nothing to the data. # if unmask is True, do: change the True mask values to False, otherwise, do nothing to the mask. if (unmask): @@ -1368,6 +1368,10 @@ def fill(self, fill_value, unmask=False, uncertainty_fill_value=None, fill_in_pl kwargs["uncertainty"][self.mask] = uncertainty_fill_value + # if fill_in_place is True, do: ; otherwise, do: + + return kwargs + def _create_masked_array_for_rebinning(data, mask, operation_ignores_mask): m = None if (mask is None or mask is False or operation_ignores_mask) else mask From e1919fa924304f6109c36253f9131a4171993c23 Mon Sep 17 00:00:00 2001 From: JunyanHuo Date: Fri, 14 Mar 2025 22:04:46 +0000 Subject: [PATCH 05/25] Added changelog again --- changelog/829.feature.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 changelog/829.feature.rst diff --git a/changelog/829.feature.rst b/changelog/829.feature.rst new file mode 100644 index 000000000..92d31989d --- /dev/null +++ b/changelog/829.feature.rst @@ -0,0 +1,2 @@ +Added ``fill`` method to ``NDCube``, a new feature which allows users to replace masked values and uncertainty values with user-given fill values, +to change the mask values back to False or not (Default), and to set whether the new instance is returned (Default) or not. From b06a1ccc38dce8b22982426747c7915f5e77751d Mon Sep 17 00:00:00 2001 From: JunyanHuo Date: Fri, 14 Mar 2025 22:29:03 +0000 Subject: [PATCH 06/25] If fill_in_place is False, then return kwargs. --- ndcube/ndcube.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/ndcube/ndcube.py b/ndcube/ndcube.py index 2c1112eba..5267e0a7a 100644 --- a/ndcube/ndcube.py +++ b/ndcube/ndcube.py @@ -1355,20 +1355,21 @@ def fill(self, fill_value, unmask=False, uncertainty_fill_value=None, fill_in_pl kwargs["mask"] = self.mask kwargs["uncertainty"] = self.uncertainty - # If there is a not None mask and a not None fill_value, do: change the corresponding data to fill_value. - if (fill_value is not None and self.mask is not None): - kwargs["data"][self.mask] = fill_value # Boolean indexing in Python. - # else, do nothing to the data. - # if unmask is True, do: change the True mask values to False, otherwise, do nothing to the mask. - if (unmask): - kwargs["mask"] = False + if (fill_in_place is False): + # If there is a not None mask and a not None fill_value, do: change the corresponding data to fill_value. + if (fill_value is not None and self.mask is not None): + kwargs["data"][self.mask] = fill_value # Boolean indexing in Python. + # else, do nothing to the data. - if (self.mask is not None and uncertainty_fill_value is not None): - kwargs["uncertainty"][self.mask] = uncertainty_fill_value + # if unmask is True, do: change the True mask values to False, otherwise, do nothing to the mask. + if (unmask): + kwargs["mask"] = False + if (self.mask is not None and uncertainty_fill_value is not None): + kwargs["uncertainty"][self.mask] = uncertainty_fill_value - # if fill_in_place is True, do: ; otherwise, do: + # if fill_in_place is True, then change self directly? without creating kwargs? return kwargs From 73cb945b1faf4402e48ec15be47162442156db9f Mon Sep 17 00:00:00 2001 From: JunyanHuo <59020618+PCJY@users.noreply.github.com> Date: Sun, 16 Mar 2025 00:57:29 +0000 Subject: [PATCH 07/25] Update changelog/829.feature.rst Co-authored-by: DanRyanIrish --- changelog/829.feature.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/829.feature.rst b/changelog/829.feature.rst index 92d31989d..800dc9619 100644 --- a/changelog/829.feature.rst +++ b/changelog/829.feature.rst @@ -1,2 +1,2 @@ -Added ``fill`` method to ``NDCube``, a new feature which allows users to replace masked values and uncertainty values with user-given fill values, +Added ``fill_masked`` method to ``NDCube``, a new feature which allows users to replace masked values and uncertainty values with user-given fill values, to change the mask values back to False or not (Default), and to set whether the new instance is returned (Default) or not. From 1715862a2c9dd3a0b24892840688ab64d3225d72 Mon Sep 17 00:00:00 2001 From: JunyanHuo <59020618+PCJY@users.noreply.github.com> Date: Sun, 16 Mar 2025 00:57:56 +0000 Subject: [PATCH 08/25] Update ndcube/ndcube.py rename the method Co-authored-by: DanRyanIrish --- ndcube/ndcube.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ndcube/ndcube.py b/ndcube/ndcube.py index 5267e0a7a..c37cf6cd9 100644 --- a/ndcube/ndcube.py +++ b/ndcube/ndcube.py @@ -1330,7 +1330,7 @@ def squeeze(self, axis=None): return self[tuple(item)] -def fill(self, fill_value, unmask=False, uncertainty_fill_value=None, fill_in_place=False): +def fill_masked(self, fill_value, unmask=False, uncertainty_fill_value=None, fill_in_place=False): """ Replaces masked data values with input value. From 20945f267b0e135d136400dee195aa562ee2961c Mon Sep 17 00:00:00 2001 From: JunyanHuo <59020618+PCJY@users.noreply.github.com> Date: Sun, 16 Mar 2025 00:58:10 +0000 Subject: [PATCH 09/25] Update ndcube/ndcube.py Co-authored-by: DanRyanIrish --- ndcube/ndcube.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ndcube/ndcube.py b/ndcube/ndcube.py index c37cf6cd9..0844fa282 100644 --- a/ndcube/ndcube.py +++ b/ndcube/ndcube.py @@ -1349,7 +1349,6 @@ def fill_masked(self, fill_value, unmask=False, uncertainty_fill_value=None, fil If `True`, the masked values are filled in place. If `False`, a new instance is returned with masked values filled. Default=False. """ - # ...code implementation here. kwargs = {} kwargs["data"] = self.data kwargs["mask"] = self.mask From 157f4b357f6c1c6a14cb9d12c408694c8aa52210 Mon Sep 17 00:00:00 2001 From: JunyanHuo <59020618+PCJY@users.noreply.github.com> Date: Sun, 16 Mar 2025 02:51:43 +0000 Subject: [PATCH 10/25] Update ndcube/ndcube.py Co-authored-by: DanRyanIrish --- ndcube/ndcube.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/ndcube/ndcube.py b/ndcube/ndcube.py index 0844fa282..8229a7a60 100644 --- a/ndcube/ndcube.py +++ b/ndcube/ndcube.py @@ -1349,10 +1349,14 @@ def fill_masked(self, fill_value, unmask=False, uncertainty_fill_value=None, fil If `True`, the masked values are filled in place. If `False`, a new instance is returned with masked values filled. Default=False. """ - kwargs = {} - kwargs["data"] = self.data - kwargs["mask"] = self.mask - kwargs["uncertainty"] = self.uncertainty + if fill_in_place: + new_data = self.data + new_uncertainty = self.uncertainty + # Unmasking in-place should be handled later. + else: + new_data = copy.deepcopy(self.data) + new_uncertainty = copy.deepcopy(self.uncertainty) + new_mask = False if unmask else copy.deepcopy(self.mask) if (fill_in_place is False): From 41d3b247ee7eaf3fe7376bc47dd08db36a5c81f0 Mon Sep 17 00:00:00 2001 From: JunyanHuo <59020618+PCJY@users.noreply.github.com> Date: Sun, 16 Mar 2025 13:56:35 +0000 Subject: [PATCH 11/25] Update ndcube/ndcube.py remove code about kwargs when fill_in_place is True. Co-authored-by: DanRyanIrish --- ndcube/ndcube.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/ndcube/ndcube.py b/ndcube/ndcube.py index 8229a7a60..9586ebddd 100644 --- a/ndcube/ndcube.py +++ b/ndcube/ndcube.py @@ -1366,8 +1366,6 @@ def fill_masked(self, fill_value, unmask=False, uncertainty_fill_value=None, fil # else, do nothing to the data. # if unmask is True, do: change the True mask values to False, otherwise, do nothing to the mask. - if (unmask): - kwargs["mask"] = False if (self.mask is not None and uncertainty_fill_value is not None): kwargs["uncertainty"][self.mask] = uncertainty_fill_value From 1afe30aa337aee25b80a793727c1c0934c63c279 Mon Sep 17 00:00:00 2001 From: JunyanHuo <59020618+PCJY@users.noreply.github.com> Date: Sun, 16 Mar 2025 13:58:52 +0000 Subject: [PATCH 12/25] Update ndcube/ndcube.py Already know that self.mask is not None, now, check whether uncertainty_fill_value and self.uncertainty is None, raise an error if self.uncertainty is None. Co-authored-by: DanRyanIrish --- ndcube/ndcube.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ndcube/ndcube.py b/ndcube/ndcube.py index 9586ebddd..60cee9d23 100644 --- a/ndcube/ndcube.py +++ b/ndcube/ndcube.py @@ -1367,7 +1367,9 @@ def fill_masked(self, fill_value, unmask=False, uncertainty_fill_value=None, fil # if unmask is True, do: change the True mask values to False, otherwise, do nothing to the mask. - if (self.mask is not None and uncertainty_fill_value is not None): + if uncertainty_fill_value is not None: + if not self.uncertainty: + raise TypeError("Cannot fill uncertainty as uncertainty is None.") kwargs["uncertainty"][self.mask] = uncertainty_fill_value # if fill_in_place is True, then change self directly? without creating kwargs? From f280941f011de19c1d6191eac95614a9faeb3519 Mon Sep 17 00:00:00 2001 From: JunyanHuo <59020618+PCJY@users.noreply.github.com> Date: Sun, 16 Mar 2025 14:13:37 +0000 Subject: [PATCH 13/25] Update ndcube/ndcube.py Filling in the uncertainty_fill_value Co-authored-by: DanRyanIrish --- ndcube/ndcube.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ndcube/ndcube.py b/ndcube/ndcube.py index 60cee9d23..46a8e31e1 100644 --- a/ndcube/ndcube.py +++ b/ndcube/ndcube.py @@ -1370,7 +1370,7 @@ def fill_masked(self, fill_value, unmask=False, uncertainty_fill_value=None, fil if uncertainty_fill_value is not None: if not self.uncertainty: raise TypeError("Cannot fill uncertainty as uncertainty is None.") - kwargs["uncertainty"][self.mask] = uncertainty_fill_value + new_uncertainty.array[idx_mask] = uncertainty_fill_value # if fill_in_place is True, then change self directly? without creating kwargs? From fb00430ff27be528c5e43696e6dfb97d86051ca7 Mon Sep 17 00:00:00 2001 From: JunyanHuo <59020618+PCJY@users.noreply.github.com> Date: Mon, 17 Mar 2025 13:32:38 +0000 Subject: [PATCH 14/25] Update ndcube/ndcube.py Co-authored-by: DanRyanIrish --- ndcube/ndcube.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ndcube/ndcube.py b/ndcube/ndcube.py index 46a8e31e1..1438d8d44 100644 --- a/ndcube/ndcube.py +++ b/ndcube/ndcube.py @@ -1372,9 +1372,10 @@ def fill_masked(self, fill_value, unmask=False, uncertainty_fill_value=None, fil raise TypeError("Cannot fill uncertainty as uncertainty is None.") new_uncertainty.array[idx_mask] = uncertainty_fill_value - # if fill_in_place is True, then change self directly? without creating kwargs? - - return kwargs + if not fill_in_place: + # Create kwargs dictionary and return a new instance. + elif unmask: + self.mask = False def _create_masked_array_for_rebinning(data, mask, operation_ignores_mask): From eb3d0e8b59d55f81a358a1b31f0c1e0e4ecd9f2b Mon Sep 17 00:00:00 2001 From: JunyanHuo <59020618+PCJY@users.noreply.github.com> Date: Mon, 17 Mar 2025 13:44:50 +0000 Subject: [PATCH 15/25] Update ndcube/ndcube.py Co-authored-by: DanRyanIrish --- ndcube/ndcube.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ndcube/ndcube.py b/ndcube/ndcube.py index 1438d8d44..3ee923e31 100644 --- a/ndcube/ndcube.py +++ b/ndcube/ndcube.py @@ -1359,9 +1359,9 @@ def fill_masked(self, fill_value, unmask=False, uncertainty_fill_value=None, fil new_mask = False if unmask else copy.deepcopy(self.mask) - if (fill_in_place is False): - # If there is a not None mask and a not None fill_value, do: change the corresponding data to fill_value. - if (fill_value is not None and self.mask is not None): + masked = False if (self.mask is None or self.mask is False or not self.mask.any()) else True + if masked: + idx_mask = slice(None) is self.mask is True else self.mask # Ensure indexing mask can index the data array. kwargs["data"][self.mask] = fill_value # Boolean indexing in Python. # else, do nothing to the data. From bad2a267c2b3a719b0227dc63469ac0d6df4a669 Mon Sep 17 00:00:00 2001 From: JunyanHuo Date: Mon, 17 Mar 2025 22:54:03 +0000 Subject: [PATCH 16/25] Implementing the fill_masked method. --- ndcube/ndcube.py | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/ndcube/ndcube.py b/ndcube/ndcube.py index 3ee923e31..f02652de0 100644 --- a/ndcube/ndcube.py +++ b/ndcube/ndcube.py @@ -1,4 +1,5 @@ import abc +import copy import inspect import numbers import textwrap @@ -1349,34 +1350,40 @@ def fill_masked(self, fill_value, unmask=False, uncertainty_fill_value=None, fil If `True`, the masked values are filled in place. If `False`, a new instance is returned with masked values filled. Default=False. """ + # variable creations for later use. + # If fill_in_place is true, do: assign data and uncertainty to variables. if fill_in_place: new_data = self.data new_uncertainty = self.uncertainty + new_mask = False if unmask else self.mask # Unmasking in-place should be handled later. + + # If fill_in_place is false, do: create new storage place for data and uncertainty and mask. + # TODO: is the logic repetitive? this else is the same with the if not fill_in_place below? No because the order matters. else: new_data = copy.deepcopy(self.data) new_uncertainty = copy.deepcopy(self.uncertainty) new_mask = False if unmask else copy.deepcopy(self.mask) - masked = False if (self.mask is None or self.mask is False or not self.mask.any()) else True if masked: - idx_mask = slice(None) is self.mask is True else self.mask # Ensure indexing mask can index the data array. - kwargs["data"][self.mask] = fill_value # Boolean indexing in Python. - # else, do nothing to the data. - - # if unmask is True, do: change the True mask values to False, otherwise, do nothing to the mask. + idx_mask = slice(None) if self.mask is True else self.mask # Ensure indexing mask can index the data array. + new_data[idx_mask] = fill_value # Q: can it be None?? - if uncertainty_fill_value is not None: + if uncertainty_fill_value is not None: # Q: can it be None?? if not self.uncertainty: raise TypeError("Cannot fill uncertainty as uncertainty is None.") new_uncertainty.array[idx_mask] = uncertainty_fill_value if not fill_in_place: # Create kwargs dictionary and return a new instance. - elif unmask: - self.mask = False + kwargs = [] + kwargs['data'] = new_data + kwargs['uncertainty'] = new_uncertainty + kwargs['mask'] = new_mask + return kwargs + return None def _create_masked_array_for_rebinning(data, mask, operation_ignores_mask): m = None if (mask is None or mask is False or operation_ignores_mask) else mask From 468fcb2dd12563a6626aa6ec1cb421add2ce258e Mon Sep 17 00:00:00 2001 From: JunyanHuo Date: Mon, 17 Mar 2025 23:15:42 +0000 Subject: [PATCH 17/25] About units. --- ndcube/ndcube.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ndcube/ndcube.py b/ndcube/ndcube.py index f02652de0..0b66af8e0 100644 --- a/ndcube/ndcube.py +++ b/ndcube/ndcube.py @@ -1356,6 +1356,7 @@ def fill_masked(self, fill_value, unmask=False, uncertainty_fill_value=None, fil new_data = self.data new_uncertainty = self.uncertainty new_mask = False if unmask else self.mask + new_unit = self.unit # Unmasking in-place should be handled later. # If fill_in_place is false, do: create new storage place for data and uncertainty and mask. @@ -1364,6 +1365,7 @@ def fill_masked(self, fill_value, unmask=False, uncertainty_fill_value=None, fil new_data = copy.deepcopy(self.data) new_uncertainty = copy.deepcopy(self.uncertainty) new_mask = False if unmask else copy.deepcopy(self.mask) + new_unit = copy.deepcopy(self.unit) masked = False if (self.mask is None or self.mask is False or not self.mask.any()) else True if masked: @@ -1381,6 +1383,7 @@ def fill_masked(self, fill_value, unmask=False, uncertainty_fill_value=None, fil kwargs['data'] = new_data kwargs['uncertainty'] = new_uncertainty kwargs['mask'] = new_mask + kwargs['unit'] = new_unit return kwargs return None From a35feeb74e40889ceaa12a37c850fded9d0d502f Mon Sep 17 00:00:00 2001 From: JunyanHuo Date: Tue, 18 Mar 2025 00:47:06 +0000 Subject: [PATCH 18/25] Further implementing, preparing for testing. --- ndcube/conftest.py | 14 ++++++ ndcube/ndcube.py | 106 ++++++++++++++++++++-------------------- ndcube/tests/helpers.py | 17 ++++++- 3 files changed, 82 insertions(+), 55 deletions(-) diff --git a/ndcube/conftest.py b/ndcube/conftest.py index e52ad7643..3ca3ade55 100644 --- a/ndcube/conftest.py +++ b/ndcube/conftest.py @@ -659,6 +659,20 @@ def ndcube_2d_ln_lt_mask_uncert(wcs_2d_lt_ln): return NDCube(data_cube, wcs=wcs_2d_lt_ln, uncertainty=uncertainty, mask=mask) +@pytest.fixture +def ndcube_2d_ln_lt_mask_uncert_unit(wcs_2d_lt_ln): + shape = (10, 12) + unit = u.ct + data_cube = data_nd(shape) + uncertainty = astropy.nddata.StdDevUncertainty(data_cube * 0.1) + mask = np.zeros(shape, dtype=bool) + mask[1, 0] = True + mask[2, 0] = True + mask[3, 0] = True + mask[4, 0] = True + return NDCube(data_cube, wcs=wcs_2d_lt_ln, uncertainty=uncertainty, mask=mask, unit=unit) + + @pytest.fixture def ndcube_2d_ln_lt_uncert_ec(wcs_2d_lt_ln): shape = (4, 9) diff --git a/ndcube/ndcube.py b/ndcube/ndcube.py index 0b66af8e0..067c4c9b1 100644 --- a/ndcube/ndcube.py +++ b/ndcube/ndcube.py @@ -1331,62 +1331,62 @@ def squeeze(self, axis=None): return self[tuple(item)] -def fill_masked(self, fill_value, unmask=False, uncertainty_fill_value=None, fill_in_place=False): - """ - Replaces masked data values with input value. + def fill_masked(self, fill_value, unmask=False, uncertainty_fill_value=None, fill_in_place=False): + """ + Replaces masked data values with input value. - Returns a new instance or alters values in place. + Returns a new instance or alters values in place. - Parameters - ---------- - fill_value: `numbers.Number` or scalar `astropy.unit.Quantity` - The value to replace masked data with. - unmask: `bool`, optional - If True, the newly filled masked values are unmasked. If False, they remain masked - Default=False - uncertainty_fill_value: `numbers.Number` or scalar `astropy.unit.Quantity`, optional - The value to replace masked uncertainties with. - fill_in_place: `bool`, optional - If `True`, the masked values are filled in place. If `False`, a new instance is returned - with masked values filled. Default=False. - """ - # variable creations for later use. - # If fill_in_place is true, do: assign data and uncertainty to variables. - if fill_in_place: - new_data = self.data - new_uncertainty = self.uncertainty - new_mask = False if unmask else self.mask - new_unit = self.unit - # Unmasking in-place should be handled later. + Parameters + ---------- + fill_value: `numbers.Number` or scalar `astropy.unit.Quantity` + The value to replace masked data with. + unmask: `bool`, optional + If True, the newly filled masked values are unmasked. If False, they remain masked + Default=False + uncertainty_fill_value: `numbers.Number` or scalar `astropy.unit.Quantity`, optional + The value to replace masked uncertainties with. + fill_in_place: `bool`, optional + If `True`, the masked values are filled in place. If `False`, a new instance is returned + with masked values filled. Default=False. + """ + # variable creations for later use. + # If fill_in_place is true, do: assign data and uncertainty to variables. + if fill_in_place: + new_data = self.data + new_uncertainty = self.uncertainty + new_mask = False if unmask else self.mask + new_unit = self.unit + # Unmasking in-place should be handled later. - # If fill_in_place is false, do: create new storage place for data and uncertainty and mask. - # TODO: is the logic repetitive? this else is the same with the if not fill_in_place below? No because the order matters. - else: - new_data = copy.deepcopy(self.data) - new_uncertainty = copy.deepcopy(self.uncertainty) - new_mask = False if unmask else copy.deepcopy(self.mask) - new_unit = copy.deepcopy(self.unit) - - masked = False if (self.mask is None or self.mask is False or not self.mask.any()) else True - if masked: - idx_mask = slice(None) if self.mask is True else self.mask # Ensure indexing mask can index the data array. - new_data[idx_mask] = fill_value # Q: can it be None?? - - if uncertainty_fill_value is not None: # Q: can it be None?? - if not self.uncertainty: - raise TypeError("Cannot fill uncertainty as uncertainty is None.") - new_uncertainty.array[idx_mask] = uncertainty_fill_value - - if not fill_in_place: - # Create kwargs dictionary and return a new instance. - kwargs = [] - kwargs['data'] = new_data - kwargs['uncertainty'] = new_uncertainty - kwargs['mask'] = new_mask - kwargs['unit'] = new_unit - return kwargs - - return None + # If fill_in_place is false, do: create new storage place for data and uncertainty and mask. + # TODO: is the logic repetitive? this else is the same with the if not fill_in_place below? No because the order matters. + else: + new_data = copy.deepcopy(self.data) + new_uncertainty = copy.deepcopy(self.uncertainty) + new_mask = False if unmask else copy.deepcopy(self.mask) + new_unit = copy.deepcopy(self.unit) + + masked = False if (self.mask is None or self.mask is False or not self.mask.any()) else True + if masked: + idx_mask = slice(None) if self.mask is True else self.mask # Ensure indexing mask can index the data array. + new_data[idx_mask] = fill_value # Q: can it be None?? + + if uncertainty_fill_value is not None: # Q: can it be None?? + if not self.uncertainty: + raise TypeError("Cannot fill uncertainty as uncertainty is None.") + new_uncertainty.array[idx_mask] = uncertainty_fill_value + + if not fill_in_place: + # Create kwargs dictionary and return a new instance. + kwargs = {} + kwargs['data'] = new_data + kwargs['uncertainty'] = new_uncertainty + kwargs['mask'] = new_mask + kwargs['unit'] = new_unit + return kwargs + + return None def _create_masked_array_for_rebinning(data, mask, operation_ignores_mask): m = None if (mask is None or mask is False or operation_ignores_mask) else mask diff --git a/ndcube/tests/helpers.py b/ndcube/tests/helpers.py index a9e7d0727..93944921d 100644 --- a/ndcube/tests/helpers.py +++ b/ndcube/tests/helpers.py @@ -122,13 +122,26 @@ def assert_metas_equal(test_input, expected_output): assert test_input[key] == expected_output[key] -def assert_cubes_equal(test_input, expected_cube, check_data=True): +def assert_cubes_equal(test_input, expected_cube, check_data=True, check_uncertainty_values=False): assert isinstance(test_input, type(expected_cube)) assert np.all(test_input.mask == expected_cube.mask) if check_data: np.testing.assert_array_equal(test_input.data, expected_cube.data) assert_wcs_are_equal(test_input.wcs, expected_cube.wcs) - if test_input.uncertainty: + if check_uncertainty_values: + # Check output and expected uncertainty are of same type. Remember they could be None. + # If the uncertainties are not None,... + # Check units, shape, and values of the uncertainty. + if (test_input.uncertainty is not None and expected_cube.uncertainty is not None): + assert type(test_input.uncertainty) is type(expected_cube.uncertainty) + assert np.allclose(test_input.uncertainty.array, expected_cube.uncertainty.array), \ + f"Expected uncertainty: {expected_cube.uncertainty}, but got: {test_input.uncertainty.array}" + elif test_input.uncertainty is None: + assert expected_cube.uncertainty is None, "Test uncertainty should not be None." + elif expected_cube.uncertainty is None: + assert test_input.uncertainty is None, "Test uncertainty should be None." + + elif test_input.uncertainty: assert test_input.uncertainty.array.shape == expected_cube.uncertainty.array.shape assert np.all(test_input.shape == expected_cube.shape) assert_metas_equal(test_input.meta, expected_cube.meta) From 0cbfbd724d5816cb1a5dbdaea97864658f5eaa1f Mon Sep 17 00:00:00 2001 From: JunyanHuo Date: Tue, 18 Mar 2025 00:57:43 +0000 Subject: [PATCH 19/25] Added test for the fill_masked method. --- ndcube/tests/test_ndcube.py | 76 +++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/ndcube/tests/test_ndcube.py b/ndcube/tests/test_ndcube.py index 01f2aa0f2..f02677f4f 100644 --- a/ndcube/tests/test_ndcube.py +++ b/ndcube/tests/test_ndcube.py @@ -1,5 +1,6 @@ import re import copy +import importlib from inspect import signature from textwrap import dedent @@ -21,10 +22,14 @@ from astropy.wcs.wcsapi import BaseHighLevelWCS, BaseLowLevelWCS from astropy.wcs.wcsapi.wrappers import SlicedLowLevelWCS +import ndcube.tests.helpers from ndcube import ExtraCoords, NDCube, NDMeta from ndcube.tests import helpers from ndcube.utils.exceptions import NDCubeUserWarning +importlib.reload(ndcube.tests.helpers) + + def generate_data(shape): data = np.arange(np.prod(shape)) @@ -1384,3 +1389,74 @@ def test_set_data_mask(ndcube_4d_mask): with pytest.raises(TypeError, match="Can not set the .data .* with a numpy masked array"): cube.data = masked_array + +@pytest.mark.parametrize( + ("fill_value", "uncertainty_fill_value", "unmask", "fill_in_place"), + [ + (1.0, 0.1, False, True), # when it changes the cube in place: its data, uncertainty; it does not unmask the mask. + (1.0, None, False, True), # uncertainty_fill_value is None. + + (1.0, 0.1, False, False), # the same as above, but not in place. + (1.0, None, False, False), # uncertainty_fill_value is None. + + (1.0, 0.01, True, False), # unmask is true + (1.0 * u.cm, 0.02, False, False), # what if the fill_value has a unit?? + ] +) +def test_fill_masked(ndcube_2d_ln_lt_mask_uncert_unit, fill_value, uncertainty_fill_value, unmask, fill_in_place): + # What I need to test: + # when the fill_masked method is applied on the fixture argument, does it: + # 1, give me the correct data value and type? + # 2, give me the correct uncertainty? + # 3, give me the correct mask? + # 4, give me the correct unit? + # The above four + # + # when masked with a fill_value + # use assert_cubes_equal????? + + # perform the fill_masked method on the fixture, using parametrized as parameters. + ndc = ndcube_2d_ln_lt_mask_uncert_unit + + expected_data = ndc.data.copy() + expected_data[ndc.mask] = fill_value + expected_uncertainty = ndc.uncertainty.array.copy() + + if uncertainty_fill_value is not None: + expected_uncertainty[ndc.mask] = uncertainty_fill_value + + expected_mask = False if unmask else ndc.mask + + expected_ndc = NDCube( + expected_data, + wcs=ndc.wcs, + uncertainty=astropy.nddata.StdDevUncertainty(expected_uncertainty), + mask=expected_mask, + unit=ndc.unit, + meta=ndc.meta + ) + + # perform the fill_masked operation + if fill_in_place: + ndc.fill_masked(fill_value, uncertainty_fill_value=uncertainty_fill_value, unmask=unmask, fill_in_place=True) + + # check whether ndc has been masked correctly + helpers.assert_cubes_equal(ndc, expected_ndc, check_data=True, check_uncertainty_values=True) + + else: + filled_ndc = ndc.fill_masked(fill_value, uncertainty_fill_value=uncertainty_fill_value, unmask=unmask, fill_in_place=False) + + if isinstance(filled_ndc, dict): # convert it back from dictionary to NDCube + filled_ndc = NDCube( + data=filled_ndc['data'], + uncertainty=filled_ndc.get('uncertainty', None), + mask=filled_ndc.get('mask', None), + unit=filled_ndc['unit'], + wcs=ndc.wcs, + ) + + # check whether ndc has been masked correctly + helpers.assert_cubes_equal(filled_ndc, expected_ndc, check_data=True, check_uncertainty_values=True) + + # ensure the original ndc is not changed + helpers.assert_cubes_equal(ndc, ndcube_2d_ln_lt_mask_uncert_unit, check_data=True, check_uncertainty_values=True) From 0d4055fc759b97cda49a25789bb65b1bf1a16c99 Mon Sep 17 00:00:00 2001 From: JunyanHuo Date: Tue, 18 Mar 2025 01:09:23 +0000 Subject: [PATCH 20/25] Fixed error about docstring. --- ndcube/ndcube.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ndcube/ndcube.py b/ndcube/ndcube.py index 067c4c9b1..12ad6208a 100644 --- a/ndcube/ndcube.py +++ b/ndcube/ndcube.py @@ -1339,12 +1339,12 @@ def fill_masked(self, fill_value, unmask=False, uncertainty_fill_value=None, fil Parameters ---------- - fill_value: `numbers.Number` or scalar `astropy.unit.Quantity` + fill_value: `numbers.Number` or scalar `~astropy.units.Quantity` The value to replace masked data with. unmask: `bool`, optional If True, the newly filled masked values are unmasked. If False, they remain masked Default=False - uncertainty_fill_value: `numbers.Number` or scalar `astropy.unit.Quantity`, optional + uncertainty_fill_value: `numbers.Number` or scalar `~astropy.units.Quantity`, optional The value to replace masked uncertainties with. fill_in_place: `bool`, optional If `True`, the masked values are filled in place. If `False`, a new instance is returned From be4639a327b170e31d86d6d494b63a763ed2f6fd Mon Sep 17 00:00:00 2001 From: JunyanHuo Date: Tue, 18 Mar 2025 01:15:23 +0000 Subject: [PATCH 21/25] Changed the docstring again. --- ndcube/ndcube.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ndcube/ndcube.py b/ndcube/ndcube.py index 12ad6208a..1420ea922 100644 --- a/ndcube/ndcube.py +++ b/ndcube/ndcube.py @@ -1339,12 +1339,12 @@ def fill_masked(self, fill_value, unmask=False, uncertainty_fill_value=None, fil Parameters ---------- - fill_value: `numbers.Number` or scalar `~astropy.units.Quantity` + fill_value: `numbers.Number` or scalar `astropy.units.Quantity` The value to replace masked data with. unmask: `bool`, optional If True, the newly filled masked values are unmasked. If False, they remain masked Default=False - uncertainty_fill_value: `numbers.Number` or scalar `~astropy.units.Quantity`, optional + uncertainty_fill_value: `numbers.Number` or scalar `astropy.units.Quantity`, optional The value to replace masked uncertainties with. fill_in_place: `bool`, optional If `True`, the masked values are filled in place. If `False`, a new instance is returned From 74c648ec3fa0f1b36a91205bc0327d0eeb287057 Mon Sep 17 00:00:00 2001 From: JunyanHuo <59020618+PCJY@users.noreply.github.com> Date: Tue, 18 Mar 2025 15:18:32 +0000 Subject: [PATCH 22/25] Update ndcube/ndcube.py make kwargs ndcube and return it Co-authored-by: DanRyanIrish --- ndcube/ndcube.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ndcube/ndcube.py b/ndcube/ndcube.py index 1420ea922..5885a365c 100644 --- a/ndcube/ndcube.py +++ b/ndcube/ndcube.py @@ -1384,7 +1384,7 @@ def fill_masked(self, fill_value, unmask=False, uncertainty_fill_value=None, fil kwargs['uncertainty'] = new_uncertainty kwargs['mask'] = new_mask kwargs['unit'] = new_unit - return kwargs + return self._new_instance(**kwargs) return None From b7e99bd8371ff19f06087b252a00db2304cedb48 Mon Sep 17 00:00:00 2001 From: JunyanHuo <59020618+PCJY@users.noreply.github.com> Date: Tue, 18 Mar 2025 15:19:29 +0000 Subject: [PATCH 23/25] Update ndcube/conftest.py Co-authored-by: DanRyanIrish --- ndcube/conftest.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ndcube/conftest.py b/ndcube/conftest.py index 3ca3ade55..4ee81a958 100644 --- a/ndcube/conftest.py +++ b/ndcube/conftest.py @@ -666,10 +666,7 @@ def ndcube_2d_ln_lt_mask_uncert_unit(wcs_2d_lt_ln): data_cube = data_nd(shape) uncertainty = astropy.nddata.StdDevUncertainty(data_cube * 0.1) mask = np.zeros(shape, dtype=bool) - mask[1, 0] = True - mask[2, 0] = True - mask[3, 0] = True - mask[4, 0] = True + mask[1:5, 0] = True return NDCube(data_cube, wcs=wcs_2d_lt_ln, uncertainty=uncertainty, mask=mask, unit=unit) From d422668fbb321a4d757e4815d2c955535cf77ad2 Mon Sep 17 00:00:00 2001 From: JunyanHuo <59020618+PCJY@users.noreply.github.com> Date: Tue, 18 Mar 2025 15:23:11 +0000 Subject: [PATCH 24/25] Update ndcube/ndcube.py what's needed to be done regarding units is checking whether units assigned are consistent, if users do assign it Co-authored-by: DanRyanIrish --- ndcube/ndcube.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ndcube/ndcube.py b/ndcube/ndcube.py index 5885a365c..8b6717837 100644 --- a/ndcube/ndcube.py +++ b/ndcube/ndcube.py @@ -1383,7 +1383,6 @@ def fill_masked(self, fill_value, unmask=False, uncertainty_fill_value=None, fil kwargs['data'] = new_data kwargs['uncertainty'] = new_uncertainty kwargs['mask'] = new_mask - kwargs['unit'] = new_unit return self._new_instance(**kwargs) return None From cc65f5d167a0e452891e6f0c2c79a37650f1d219 Mon Sep 17 00:00:00 2001 From: JunyanHuo <59020618+PCJY@users.noreply.github.com> Date: Tue, 18 Mar 2025 15:24:05 +0000 Subject: [PATCH 25/25] Update ndcube/tests/helpers.py Co-authored-by: DanRyanIrish --- ndcube/tests/helpers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ndcube/tests/helpers.py b/ndcube/tests/helpers.py index 93944921d..56170df01 100644 --- a/ndcube/tests/helpers.py +++ b/ndcube/tests/helpers.py @@ -140,7 +140,6 @@ def assert_cubes_equal(test_input, expected_cube, check_data=True, check_uncerta assert expected_cube.uncertainty is None, "Test uncertainty should not be None." elif expected_cube.uncertainty is None: assert test_input.uncertainty is None, "Test uncertainty should be None." - elif test_input.uncertainty: assert test_input.uncertainty.array.shape == expected_cube.uncertainty.array.shape assert np.all(test_input.shape == expected_cube.shape)