-
-
Notifications
You must be signed in to change notification settings - Fork 50
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
Added new features to the ndcube._add_ method #794
base: main
Are you sure you want to change the base?
Conversation
ndcube/ndcube.py
Outdated
# addition | ||
new_data = self.data + value_data |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The addition should be done as part of the masked array addition. You've already done this below, you just need to extract the added data from the results as well as the mask.
ndcube/ndcube.py
Outdated
return self._new_instance( | ||
data=new_data, uncertainty=new_uncertainty, mask=new_mask | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of having a separate return
here for the NDData
case, I think we should build a dictionary of kwargs
that we can give self._new_instance
, here. So, you can create an empty kwargs dictionary at the start of the method, and add the new data, uncertainty, etc. in the relevant place, e.g.
kwargs["uncertainty"] = new_uncertainty
Then the final line of the method would become
return self._new_instance(**kwargs)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me know if this doesn't make sense
Co-authored-by: DanRyanIrish <[email protected]>
Co-authored-by: DanRyanIrish <[email protected]>
…o nddataArithmetic and further modify the _add_ method.
ndcube/ndcube.py
Outdated
if self.uncertainty is not None and value.uncertainty is not None: | ||
new_uncertainty = self.uncertainty.propagate( | ||
np.add, value.uncertainty, correlation=0 | ||
np.add, value.uncertainty, result_data = value.data, correlation=0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The result_data
needs to be the result of the operation. So, assuming you moved the addition of the datas using the masked array to before the uncertainty propagation, you could do:
np.add, value.uncertainty, result_data = value.data, correlation=0 | |
np.add, value.uncertainty, result_data = kwargs["data"], correlation=0 |
ndcube/ndcube.py
Outdated
# combine mask | ||
self_ma = np.ma.MaskedArray(self.data, mask=self.mask) | ||
value_ma = np.ma.MaskedArray(value_data, mask=value.mask) | ||
|
||
# addition | ||
result_ma = self_ma + value_ma | ||
new_mask = result_ma.mask | ||
|
||
# extract new mask and new data | ||
kwargs["mask"] = result_ma.mask | ||
kwargs["data"] = result_ma.data |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As mentioned in above comment, I think it makes sense to do this before the uncertainty propagation so you can use the kwargs["data"]
value in that propagation.
ndcube/ndcube.py
Outdated
kwargs["data"] = result_ma.data | ||
|
||
# return the new NDCube instance | ||
return self._new_instance(**kwargs) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move this line to the end of the method and use the kwargs
approach when handling the other cases, e.g. Quantity
. So, for example, L1082 would become:
kwargs["data"] = self.data + value.to_value(cube_unit)
A changelog file needs to be added. And your branch needs to be updated with the latest version of main. |
Hi @DanRyanIrish, as we have discussed in our project meetings, below are the issues we encountered and may need further discussions with others in the community: The issue is mainly around how NumPy handles masks when performing an addition for two NumPy.MaskedArray. However, from experimentation, it can be seen that I find this confusing because even if it does combine the mask and then apply it on the result, it should be: Please correct me if there is anything wrong in my understanding. |
@DanRyanIrish, Secondly, we also encountered some issues around the propagate method:
|
Hi @PCJY. I think the first thing we need to do is decide what behaviours we want to implement in the case where at least one of the Firstly, if an object has no mask, that is equivalent to all pixels being unmasked.
Alternatives for parts of the scheme could include:
Once we agree on a scheme, the way forward on your uncertainty questions will become clear. @Cadair what are your thoughts on this scheme. I also think we should bring this up at the sunpy weekly meeting to get other thoughts. |
This is where I also find numpy masked arrays counter-intuitive. However, the logic is as follows:
Notice that this is not the same as the scheme I've proposed in my previous comment, in part because it's confusing, as you've found. |
@PCJY, until we agree a way forward with the mask, you should proceed by implementing in the case for which neither object has a mask. So no need for masked arrays. |
I haven't thought too much each of these individual cases, but the fact there is a list is enough to make me think we probably need a way for the user to choose. This is obviously not possible with the Is this also not a problem for other operators as well? |
I think this is a good idea. As well as As far as I can see, this ambiguity only arises when there are masks involved. So we could still implement the dunder methods as wrappers around the above methods, but have the raise and error/return |
I am not sure how I feel about adding methods to the API for every arithmetic operation. How about functions instead? |
I'm open to considering that. It might go well in that analysis subpackage I've proposed. I can also see an argument for making it a method as the first argument must always be the Either way, a method can be converted to a function and vice versa quite easily. So for now, I think we should proceed with a |
…o nddataArithmetic
…rtainty function.
PR Description