Support downsampling of image data with NaNs#3061
Conversation
j9ac9k
left a comment
There was a problem hiding this comment.
The has_nans keyword argument is propagated into the recursive call on line 1776 in functions.py
|
Hi @nup002 I posted a couple of comments, the other thing I'm not sure about is if we want the keyword argument to be |
The only thing I can think of which is similar is Scipy's |
|
If there is a potential discussion on the desired behavior, then isn't the parameter really |
|
Yes, but |
|
Sorry, I misunderstood the discussion on the keyword naming, I thought this is exposed to the user. But the change is really that the existing 'has_nans' information is now passed on to the downsampling function which takes on a different behaviour then. It seems to me that you are introducing a new downsampleMethod, like we already have in PlotDataItem: Would it be reasonable to add a similar switch here, creating a user choice between I tend to agree that the current behaviour is probably not the most helpful in most of the cases where NaN values might appear in the image, but it does seem at least mathematically valid and well defined. |
|
I propose that we expose an optional keyword argument to the user in |
|
I have implemented If this feature request is approved, I will work on adding matching functionality to |
pyqtgraph/functions.py
Outdated
|
|
||
|
|
||
| def downsample(data, n, axis=0, xvals='subsample'): | ||
| def downsample(data, n, axis=0, xvals='subsample', *, nanPolicy='ignore'): |
There was a problem hiding this comment.
renaming 'nan_policy' to 'nanPolicy' seems like a good idea for matching our Qt-inspired case style.
Shouldn't 'ignore' be 'omit' if we are aiming to resemble SciPy conventions?
Do we have any additional opinions for what should be the default policy?
Although I agree that 'omit' is probably the most commonly useful policy, maybe we should stick with 'propagate' as the default to better match existing behavior.
NilsNemitz
left a comment
There was a problem hiding this comment.
Dear @nup002 , sorry for the delay.
I like the approach of matching the SciPy solution, and I think that's a fantastic upgrade for ImageItem.
I am not usually doing code review, but I've added a few comments.
I think the main items are that 'ignore' is 'omit' in the SciPy convention, and that it is probably best to leave the default behavior on 'propagate' for backwards compatibility and possibly speed.
The documentation comments are not too serious, ImageItem will need a documentation pass in the future. If you want someplace to look for reference, the most up-to-date piece of documentation style we have is probably PlotDataItem after PRs #3039 and #3057 .
pyqtgraph/functions.py
Outdated
| elif nanPolicy == 'propagate': | ||
| d2 = d1.mean(axis+1) | ||
| else: | ||
| raise ValueError(f"Keyword argument {nanPolicy=} must be one of ['propagate', 'ignore'].") |
There was a problem hiding this comment.
@j9ac9k has been looking into documentation styles recently and is clearer on the conventional markups, but I think this might be "Keyword argument 'nanPolicy' must be one of 'propagate' or 'ignore'."
And this might not need an f string?
There was a problem hiding this comment.
If a keyword only argument can be one of several things (in this case string) the syntax is:
{ 'option_1', 'option_2' }
You can see an example of it here (but you have to scroll down a bit) https://numpydoc.readthedocs.io/en/latest/format.html#parameters
pyqtgraph/graphicsItems/ImageItem.py
Outdated
| self.levels = None ## [min, max] or [[redMin, redMax], ...] | ||
| self.lut = None | ||
| self.autoDownsample = False | ||
| self.nanPolicy = 'ignore' |
There was a problem hiding this comment.
default to 'propagate' ?
pyqtgraph/graphicsItems/ImageItem.py
Outdated
| Parameters | ||
| ---------- | ||
| nanPolicy : str | ||
| Must be one of ['ignore`, 'propagate']. If 'nanPolicy' is 'ignore', NaNs |
There was a problem hiding this comment.
I think we have control strings like this marked up elsewhere in the style of
Must be one of ``'propagate'`` or ``'omit'``. For ``nanPolicy='omit'``, NaNs are automatically ignored...I would suggest changing the markup to that. But the docstyle is currently in flux and not very clearly defined yet.
There was a problem hiding this comment.
I think it should be:
Parameters
----------
nanPolicy : { 'ignore', 'propagate' }
Put here a description of each option.
pyqtgraph/graphicsItems/ImageItem.py
Outdated
| Must be one of ['ignore`, 'propagate']. If 'nanPolicy' is 'ignore', NaNs | ||
| are automatically ignored during downsampling, at the expense of | ||
| performance. If 'nanPolicy' is 'propagate', NaNs are kept during | ||
| downsampling. ImageItem is instantiated with nanPolicy set to 'ignore'. |
There was a problem hiding this comment.
You've actually added a switch to make a new ImageItem with any nanPolicy. Maybe:
Unless a different policy was specified, a new ImageItem is created with ``nanPolicy='propagate'``.
pyqtgraph/graphicsItems/ImageItem.py
Outdated
| """ | ||
| assert nanPolicy in ['propagate', 'ignore'], (f"{nanPolicy=} must be one " | ||
| f"of ['propagate', 'ignore']") | ||
| self.nanPolicy = nanPolicy |
There was a problem hiding this comment.
Since this is catching user errors, it should probably raise a ValueError (instead of using assert).
There was a problem hiding this comment.
Agreed if nanPolicy is not in the above, we can raise a ValueError, let's not have an assert check here.
There was a problem hiding this comment.
Also rename the attribute to ._nanPolicy, see comment below about getter/setter.
pyqtgraph/graphicsItems/ImageItem.py
Outdated
| if 'autoDownsample' in kwargs: | ||
| self.setAutoDownsample(kwargs['autoDownsample']) | ||
| if 'nanPolicy' in kwargs: | ||
| self.nanPolicy = kwargs['nanPolicy'] |
There was a problem hiding this comment.
I think we want to call self.setNanPolicy(kwargs['nanPolicy']).
self.nanPolicy should not be an attribute, but should be a method that gets the currently stored value. I would suggest renaming to self._nanPolicy and have another method
def nanPolicy(self):
return self._nanPolicyThat will be more in line w/ Qt's getters and setters.
|
Hi @nup002 My apologies for sitting on this for so long before commenting. I recently underwent a major international move, and it's taken me a while to get sorted out in my current home. I agree with @NilsNemitz 's comments. I have a couple of nit-pick items
|
|
@j9ac9k All of your suggestions, and those of @NilsNemitz, are reasonable. I will get on them once I can find some time. |
nup002
left a comment
There was a problem hiding this comment.
All suggestions implemented.
|
Thanks @nup002 for this PR, this looks good to me, merging. I really appreciate your patience on this! |
* np.nanmean downsampling of image data with NaNs. * Make has_nans keyword argument only. Propagate has_nans in recursive call. * Implement nanPolicy for use control of NaNs during downsampling. * Change default nanPolicy to 'propagate' * Change nanPolicy option 'ignore' to 'omit' * Implement getter method for NanPolicy * Rename method GetNanPolicy to NanPolicy
This PR adds tolerance for NaNs in ImageItem autodownsampling.
In the original code, NaNs in the image data would dominate after autodownsampling. With these changes, NaNs are ignored during downsampling.