Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork7.9k
Mask values < vmin in PowerNorm#25256
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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Conversation
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.
I don't think these should be masked. You should just set to -1 or something less than zero. Otherwise the "under" logic won't work.
I've just copied what importmatplotlib.colorsasmcolornorm=mcolor.LogNorm(vmin=1,vmax=10)x=norm(-1)print(type(x))# <class 'numpy.ma.core.MaskedConstant'>print(x)# --print(x.data)# 0.0print(x.mask)# True |
jklymak commentedFeb 19, 2023 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
But these are not values less than zero - these are values less than vmin. If you set vmin =10 in log norm and pass it a value of 0.1 it returns -2 and the colormapping will give the under colour. In particular in the above |
But the transformation is only well defined for inputs that are >= vmin - any input < vmin will end up with |
resdat = resdat ** gamma | ||
mask = result.mask | under_mask | ||
result = np.ma.array(resdat, mask=mask, copy=False) |
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.
I think this should be
good=resdat>=vminresdat= (resdat-vmin)/ (vmax-vmin)resdat[good]=resdat[good]**gamma
This will make the negative part of the scale linear, but it's better than setting it to be masked.
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.
Why do we want to make the negative part of the scale linear? In the same way a log scale is valid forx > 0
, and masks the output where any values havex <= 0
, thePowerNorm
as implemented is valid forx >= vmin
, so it is consistent to mask forx < vmin
.
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.
Because the way you are doing it here, x < vmin is always masked.
For LogNorm x<vmin isonly masked if x<0. However, 0<x<vmin isnot masked, but set to a number less than 0, and hence shows up as the under-color in the colormapping.
This proposal will allow x<vmin, and always map it to a number less than zero, and hence show up as the under-color in the color mapping.
Again, the reason is that the order of operations is different for PowerNorm. For LogNorm we do
norm = (log10(x) - log10(vmin))/ (log10(vmax)-log10(vmin)
for PowerNorm we do
norm = ((x-vmin)/(vmax-vmin))**gamma
so whether we apply the gamma outside the range [vmin, vmax] doesn't matter too much.
Yes, but thats only because the norm is being done with an order of operations opposite of our other norms. If it were consistent with say But the argument for PowerNorm, as put forward by its proponents, is that it is a gamma correction to the colormap, not the data values. In that case, values less than vmin should map to ourunder color. |
Ah, reading through I'm still not sure I buy the argument that we should map to the under color though - while the implementation of It seems like |
xref creating a powerscale:#20532 Maybe worth trying to resurrect that PR to help here? |
jklymak commentedFeb 19, 2023 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
@greglucas I think thats orthogonal. The problem here is that all ticks < vmin are being mapped to zero. I'm suggesting we map linearly x<vmin, and then use the power norm for x>vmin.@dstansby is proposing we just mask x<vmin. I still believe mapping to something <0 for x<vmin is what we want because it allows the under-color to show. data=np.arange(25.).reshape((5,5))data[0,0]=np.NaNfig,ax=plt.subplots(figsize=(3,2.5),layout='constrained')norm=mcolors.PowerNorm(gamma=0.5,vmin=2,vmax=23)cmap=cm.get_cmap('RdBu_r')cmap.set_over('m')cmap.set_under('g')cmap.set_bad('y')pc=ax.pcolormesh(data,norm=norm,cmap=cmap)fig.savefig('/Users/jklymak/Downloads/New.png')plt.show() If we go the other way then the green square will be blue in the example above (current behaviour) or yellow (@dstansby suggestion). |
I did not look at that PR closely, I just assumed it would help to handle the values below/above vmin/vmax due to the extended "scale", but perhaps not. I agree with@jklymak, I don't think masking is the right fix here. |
Why not? Do you not buy my argument in#25256 (comment)? (sorry to dig my heels in here, but I'm still convinced that if we are going to be consistent with |
No, I don't buy that argument. I don't think vmin/vmax defines the valid domain.
under/over colors are valuable and shouldn't be ignored IMO. I don't want the "bad" value if I provide something less than or greater than vmin/vmax. On the other hand, if you want to mask divide-by-zero values that seems reasonable. |
Just to re-iterate: LogNorm does a non-linear transform first, followed by a linear normalization: whereas PowerNorm does the linear normalization first, followed by a non-linear transformation of the normalization: I think x<v_{min} is just an under value, not a bad data value for PowerNorm, whereas x<0 for LogNorm is a bad data value, so we can't do anything about that in terms of over/under. |
If powernorm was impelmented as I would agree with everything above - for For the current implementation: any values I agree that
But, that's not how it's implemented at the moment, and as implemented Perhaps this is worth a 4th(!) opinion, or discussing on a weekly call? |
I'll just re-iterate the argument that PowerNorm is a linear normalization with a gamma-corrected color scale. That was the justification for the current implementation (the discussion of which I cannot seem to find). If being applied as a color correction, I still feel the under/over colors should behave the same as for a linear norm. Perhaps@tacaswell should just choose one behaviour or the other - he was the last one to change the clipping behaviour. Perhaps he will just keep it the same clip it has always been? To summarize the choices: The Norm does which can be complex if the argument is <0. Choice for x < vmin are
|
Okay, I think I'm coming round now, apologies again for being stubborn till now! For
If this is the case (and I think it is too), does that mean |
I don't think it matters - if we want to map to -1 that is fine. I was just thinking about when inverting the Norm it is nice to have a unique inverse when possible. Obviously it can't have the same scale as inside [vmin, vmax], so I was proposing just making it linear outside that region. |
BTW, we can discuss on today's call if you still feel it needs discussion? |
I agree that values below vmin should be mapped to a negative value (i.e. the under color). I think@jklymak's point that we may as well make it the transform invertible is good; this can be either by linear extrapolation on the negative side, or perhaps |
@dstansby, any further opinion on this? |
Sorry, slipped off my radar and not getting back on it any time soon. Anyone else feel free to replace or finish this PR. |
New PR at#27589 |
PR Summary
Fixes#25239 (thanks@jklymak for the hint there!). I also took the oppurtunity to tidy up the code to make it clearer what's going on.
Given#25239 is a pretty bad bug (the tick labels on a colorbar are wrong) I think it's worth backporting this.
PR Checklist
Documentation and Tests
pytest
passes)Release Notes
.. versionadded::
directive in the docstring and documented indoc/users/next_whats_new/
.. versionchanged::
directive in the docstring and documented indoc/api/next_api_changes/
next_whats_new/README.rst
ornext_api_changes/README.rst