You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Incorrect placement of Colorbar ticks using LogNorm #12155
After upgrading to Matplotlib v3.0.0, the tick marks on the colorbar are no longer placed correctly when using a matplotlib.colors.LogNorm normalization.
Code for reproduction
The code is embedded in a class defined within the NeXpy application, but uses standard Matplotlib function calls. Here are the relevant extracted lines:
If you mean self.colorbar.ax.set_yscale('log'), that compresses the colorbar vertically to a thin strip. I don't thinkself.colorbar has a set_yscale function.
You may notice that the ticklabels look differently compared to the example which makes me think that the code shown in the original post is not complete.(NeXpy uses LogFormatterSciNotation)
A Short, Self Contained, Correct (Compilable), Example would come handy here, else it's probably hard to ever find out what's going on.
I will see if I can reproduce it outside the NeXpy context. The reason I posted is that this specific code has been untouched since Matplotlib v1.5, and it has worked without problem up until v2.2.3. I was hoping someone could point to the changes in the Matplotlib API that are likely to be relevant.
I don't know if this is helpful or not, but looking at the API changes, I added a colorbar with the extend='neither' option (plotview is a reference to the plotting class instance, i.e., self in the above examples):
I still can't work out why it doesn't show up in the code example by @ImportanceOfBeingErnest but it does suggest that the underlying LogNorm instance is fine. For some reason, adding extend='neither' to the NeXpy code doesn't change anything so it may depend on the order everything is invoked. I will have to look at it in the debugger.
I've managed to produce a simple example demonstrating the problem. It turns out that the issue shows up if you make a plot with a linear color scale first and then transform it to a log scale.
The issue is that before #9903 the colorbar drawing logic was all linear, and the log-space ticks were drawn by hand. This made automatic scaling of the colorbar cumbersome. However, it did allow you to simply change the locator and formatter in place, because the axes was always linear. I had no idea people would change the locator in place, but its not an unreasonable thing to do (switching the norm from linear to logarithmic). However, the axes now has no way of knowing that it is now a logarithmic axes because it doesn't check the norm again.
Thanks for giving such quick feedback. NeXpy has a checkbox for switching between linear and log scales, which we use all the time when looking for features on different intensity scales. It looks like replacing update_normal with update_bruteforce fixes my problem in NeXpy - if I use update_normal and config_axis, then the height of my color bar changes when I change the intensity scale. Is this what the shrink option is for?
What do you do to change the intensity scale? If I do the following, it all seems to work fine, but maybe I'm misunderstanding. You shouldn't have to play around w/ shrink after you have initially made the colorbar.
norm=LogNorm(z.min(), z.max())
im.set_norm(norm)
cbar.set_norm(norm)
cbar.update_normal(im)
cbar.config_axis()
# display in here, and then user changes zmax:norm=LogNorm(z.min(), z.max()*10)
im.set_norm(norm)
cbar.set_norm(norm)
cbar.update_normal(im)
cbar.config_axis()
I think #12159 fixes this, but I guess I think if a GUI is going to change so much of the colorbar, it should indeed call cbar.update_bruteforce(im). I'm not thrilled with the name of this function, but it does what the OP wanted, which is to remove the old colorbar and make a new colorbar based on im. The info about locators etc is lost, but presumably the user has kept track of those.
That it used to work before is because we drew colorbars ticks by hand, basically. That it doesn't work now is that colorbars behave more like a normal axes and let the normal tick creation do its thing.
Note that self.locator = LogLocator() will tend to add extra ticks now if extend='both'. We added colorbar._ColorbarLogLocator to trim the extra ticks. We are trying to add that functionality to all the locators soon, but we are not there yet.
Activity
tacaswell commentedon Sep 18, 2018
That looks like the 'log' scale did not get propogated to the color bar axex correctly.
If you do
self.colorbar.set_yscale('log')does it look right?rayosborn commentedon Sep 18, 2018
If you mean
self.colorbar.ax.set_yscale('log'), that compresses the colorbar vertically to a thin strip. I don't thinkself.colorbarhas aset_yscalefunction.ImportanceOfBeingErnest commentedon Sep 18, 2018
I do have problems reproducing this. I ran the following in the current development version
and this is the result which looks expected:
You may notice that the ticklabels look differently compared to the example which makes me think that the code shown in the original post is not complete.(NeXpy uses LogFormatterSciNotation)A Short, Self Contained, Correct (Compilable), Example would come handy here, else it's probably hard to ever find out what's going on.
rayosborn commentedon Sep 18, 2018
I will see if I can reproduce it outside the NeXpy context. The reason I posted is that this specific code has been untouched since Matplotlib v1.5, and it has worked without problem up until v2.2.3. I was hoping someone could point to the changes in the Matplotlib API that are likely to be relevant.
jklymak commentedon Sep 18, 2018
Sorry this broke for you. Colorbar tick handling has definitely changed, though the outward facing API should not have.
https://github.com/matplotlib/matplotlib/blob/master/doc/api/api_changes.rst#the-ticks-for-colorbar-now-adjust-for-the-size-of-the-colorbar
The most intrusive PR would be #9903 but there were a couple of others.
OTOH this PR works with log scales, as @ImportanceOfBeingErnest points out above, so its hard to tell what broke for
NeXpy.(downstream package developers can subscribe to https://mail.python.org/mailman/listinfo/matplotlib-devel for updates on matplotlib release candidates - 3.0 rc1 was released 11 Aug. )
Thanks!
rayosborn commentedon Sep 18, 2018
I don't know if this is helpful or not, but looking at the API changes, I added a colorbar with the
extend='neither'option (plotviewis a reference to the plotting class instance, i.e.,selfin the above examples):I got the following:

I still can't work out why it doesn't show up in the code example by @ImportanceOfBeingErnest but it does suggest that the underlying LogNorm instance is fine. For some reason, adding
extend='neither'to the NeXpy code doesn't change anything so it may depend on the order everything is invoked. I will have to look at it in the debugger.rayosborn commentedon Sep 18, 2018
I've managed to produce a simple example demonstrating the problem. It turns out that the issue shows up if you make a plot with a linear color scale first and then transform it to a log scale.
ImportanceOfBeingErnest commentedon Sep 18, 2018
An immediate solution is to use
instead.
jklymak commentedon Sep 18, 2018
@rayosborn OK, thats great. Thanks.
The issue is that before #9903 the colorbar drawing logic was all linear, and the log-space ticks were drawn by hand. This made automatic scaling of the colorbar cumbersome. However, it did allow you to simply change the locator and formatter in place, because the axes was always linear. I had no idea people would change the locator in place, but its not an unreasonable thing to do (switching the norm from linear to logarithmic). However, the axes now has no way of knowing that it is now a logarithmic axes because it doesn't check the norm again.
As @ImportanceOfBeingErnest says above, you can do:
cbar.update_bruteforce(im)or
I'm not sure what a "good" solution for 3.0.x is for this. I guess at draw time we could call
cbar.config_axis()I don't think its too expensive...rayosborn commentedon Sep 18, 2018
Thanks for giving such quick feedback. NeXpy has a checkbox for switching between linear and log scales, which we use all the time when looking for features on different intensity scales. It looks like replacing
update_normalwithupdate_bruteforcefixes my problem in NeXpy - if I useupdate_normalandconfig_axis, then the height of my color bar changes when I change the intensity scale. Is this what theshrinkoption is for?jklymak commentedon Sep 18, 2018
What do you do to change the intensity scale? If I do the following, it all seems to work fine, but maybe I'm misunderstanding. You shouldn't have to play around w/ shrink after you have initially made the colorbar.
ImportanceOfBeingErnest commentedon Sep 18, 2018
To reproduce the shrinking:
jklymak commentedon Sep 18, 2018
Huh. That’s a very strange bug. I’ll check it out soon.
jklymak commentedon Sep 18, 2018
jklymak commentedon Sep 19, 2018
Fix in #12159 now fixes the above as well. Needs a test I guess...
jklymak commentedon Sep 19, 2018
I think #12159 fixes this, but I guess I think if a GUI is going to change so much of the colorbar, it should indeed call
cbar.update_bruteforce(im). I'm not thrilled with the name of this function, but it does what the OP wanted, which is to remove the old colorbar and make a new colorbar based onim. The info about locators etc is lost, but presumably the user has kept track of those.That it used to work before is because we drew colorbars ticks by hand, basically. That it doesn't work now is that colorbars behave more like a normal axes and let the normal tick creation do its thing.
Note that
self.locator = LogLocator()will tend to add extra ticks now ifextend='both'. We addedcolorbar._ColorbarLogLocatorto trim the extra ticks. We are trying to add that functionality to all the locators soon, but we are not there yet.