-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Enh: SymNorm for normalizing symmetrical data around a center #17709
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
Conversation
This seems fine, plus or minus the name which I'm not sure about. Do you need to lock out setting vmin somehow? What happens if I set norm.vmin=-42 after I've initialized the norm? |
I got the naming idea from the existing And yes, good point about |
Thank you for your work on this @jpmattern ! As you noted, we have had several attempts at getting functionality like this in over the years, hopefully it works this time! If the I think that using properties for both vmin/vmax to keep them in sync with each other is reasonable. This will definitely need tests + documentation. I'm not wild about the name, I also thought of the analogy to SymLog immediately, but my knee jerk reaction was concern (because of the funny linear / log transition in SymLog). Could we get the same effect by adding a
but that would lose what I think is the most important feature of this proposal which is that auto scaling works. Maybe we want the above + a very thin sub-class that changes the autoscale rules? |
I like the idea of using As for changing the name, I would be in favour of that but I haven't come up with a better name so far. One thing that throws me off a bit is the single "m" in the name when both "symmetrical" and "symmetry" have two "m"s. Changing the entire structure of |
I’m not a fan of one Normalize method with conflicting kwargs. CenteredNorm? |
I'd go for halfrange instead of range. Center +/- halfrange is what one (at least me) typically thinks about. CenteredNorm is good. SymmetricNorm would also work. SymNorm is a bit of an awkward abbreviation (despite the precedence of SymLogNorm). |
I like the idea of using I am personally not sure if I like |
You can count my vote as half - if you prefer SymmetricalNorm, that's fine w/ me. I didn't like SymNorm - probably residual trauma from SymLogNorm. |
This would need a note & example in the What's new document. |
As an example, I'd suggest the image in my first post above, adjusted for the new name. I modified en example from the gallery to create it, code would look something like this:
|
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 example looks good, but I would use the idiom
fig, (ax1, ax2, ax3) = plt.subplots(...)
and use ax1
etc. in the following. This saves the ax = ax[i]
statements and makes the example more compact.
Where would the example for the code go? Is |
There is a tutorial on colormap norms. It also has the examples broken out into |
I like where this is heading! This definitly needs a test, I suggest starting with @pytest.mark.style(style)
@check_figures_equal(extensions=['png'], st)
def test_centered_norm(fig_ref, fig_test):
ax_test = fig_test.subplots()
ax_ref = fig_ref.subplots()
# make the "same" plot using CenteredNorm and Normalize I see that at call time it fixes the limits, I think we should also raise or warn if we do change it. If a user does not realize that they have ended up with a |
Demo and test code sound good, I can add those. @tacaswell, I think to produce a warning or raise and error is a sensible idea but the only way to implement it I can think of involves changes to the super class |
Closes #1806. |
The prominent use of "Symmetric logarithmic" in the tutorial and the call for a "symmetric" norm in #1806 makes me think that One of the most difficult issues with adding the new norm to the tutorial was where to place it. It seems more commonly applicable than |
I have added a new gallery example and a few tests to the code. Those appear to run without issues in my virtual environment. I cannot quite wrap my head around the errors reported above. They may be related to me adding code to the gallery but I am not sure what to do about them, can someone help? This is the error message that CircleCI is showing me:
|
😱 I would find this so useful! In terms of the failing tests, could you rebase this on top of the current master branch, and we can see if the tests are still failing. |
Sounds good, I will try this in the next few days. |
Ok, I went through the rebasing process and the tests I did on my end succeeded (generating the gallery image). The automated test above fails in an odd place with an error message that is not very helpful to me, it appears to finish building the documentation and then exits with an error:
|
I'm not sure the rebase was quite right, as you shouldn't have so many unrelated commits in the PR. I see the last commit is a merge, which shouldn't be there. That means you pulled the old commits from GitHub back into your local copy. After rebasing, you'll want to force push back to GitHub to delete the old commits, instead of pulling. The doc error is earlier in the log:
|
Thanks @QuLogic, that was helpful. The doc error was related to a missing entry for I don't know what to do about the rebase though. This is my first one and it appeared to go well until I accidentally performed a |
Yes, after a rebase, you don't want to pull, you want to force push to replace the old versions on GitHub. You can see this using To drop the old commits, do the following:
PS, git on your computer says your name is |
Co-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com>
True, I changed the author names to the commits in question. |
norm = mcolors.CenteredNorm(vcenter=0.5, halfrange=0.5) | ||
assert_array_almost_equal(x, norm(x)) | ||
norm = mcolors.CenteredNorm(vcenter=1, halfrange=1) | ||
assert_array_almost_equal(x, 2 * norm(x)) |
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.
Can you test the setters and getters in here? I know they are trivial, but....
@@ -69,6 +70,46 @@ | ||
fig.colorbar(pcm, ax=ax[1], extend='max') | ||
plt.show() | ||
|
||
############################################################################### |
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.
This should definitely be moved under TwoSlopeNorm, please, as it is a specialization of that norm.
This would need a What's new entry as well. |
Co-authored-by: Elliott Sales de Andrade <quantum.analyst@gmail.com>
Co-authored-by: Elliott Sales de Andrade <quantum.analyst@gmail.com>
Ok, I
@jklymak, I agree that it would make sense to place the tutorial entry below or closer to |
lib/matplotlib/colors.py
Outdated
self.vmax = None | ||
else: | ||
self._halfrange = abs(halfrange) | ||
#self._set_vmin_vmax() |
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.
Was this intentionally commented out?
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.
Good catch again! Yes, left it in to check and meant to delete the line as it is unnecessary and may sometimes lead to undesired results (when invoking the setter for halfrange
first and then the setter for vcenter
). I'll delete that line now and include that change in my next commit.
In my most recent commit, I expanded testing to cover 100% of the |
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'll leave it to @jklymak to decide on the doc position.
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 agree w/ the doc position, but I can always change in a follow up PR ;-)
Thanks @jpmattern - looks like a useful addition! |
Awesome, thanks! Great to see that this feature its making its way into the code now. |
PR Summary
Adds
SymNorm
, a new norm that automatically creates linear colormaps that are symmetrical around a center. Useful for symmetrical data and in combination with diverging colormaps.I often create plots where I want the color norm to be symmetric around a center, typically for visualizing anomalies. I tend to use one of the diverging colormaps and I often end up with code like this, where the norm is adjusted after creating the plot to make it symmetric:
While the above is not a long piece of code, its more elegant to achieve the same goal automatically using a new norm. This approach also has the advantage of easily being able to set a center different from 0 (using, e.g.:
SymNorm(vcenter=1.0)
).When I was scrolling through the pull requests here, I noticed that a somewhat similar approach has been discussed here. The proposed
SymNorm
goes a slightly different direction, in that the norm may extend beyond the data on one side ofvcenter
. I personally like this, as it, for example, gives the colorbar a symmetrical look (see figure above).A quick note regarding the new code: The implementation of SymNorm makes use of
@property
syntax to implement a setter forvcenter
. This syntax does not appear in other parts ofcolors.py
and I would be happy to change the implementation, for example by overriding the__call__
method which is currently not overridden.PR Checklist