Skip to content
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

Colorbar with imshow(logNorm) shows unexpected minor ticks #8307

Closed
billtau opened this issue Mar 16, 2017 · 9 comments

Comments

Projects
None yet
8 participants
@billtau
Copy link

commented Mar 16, 2017

Bug report

Bug summary

When using colorbar with imshow(image, norm=LogNorm()), the produced colorbar automatically adds minorticks, and cannot be turned off with cbar.ax.minorticks_off(). Also the font of the tick label is not the same as the font set in matplotlibrc.

Code for reproduction

# This is modified from an example in matplotlib gallery.

import matplotlib.pyplot as plt
import numpy as np
from numpy import ma
from matplotlib import colors, ticker, cm
from matplotlib.mlab import bivariate_normal

N = 100
x = np.linspace(-3.0, 3.0, N)
y = np.linspace(-2.0, 2.0, N)

X, Y = np.meshgrid(x, y)

z = (bivariate_normal(X, Y, 0.1, 0.2, 1.0, 1.0)
     + 0.1 * bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0))

z = ma.masked_where(z <= 0, z)

fig, ax = plt.subplots()

from matplotlib.colors import LogNorm

cs = ax.imshow(z,norm=LogNorm())

cbar = fig.colorbar(cs)

cbar.ax.minorticks_off()

plt.show()

Actual outcome

screen shot 2017-03-16 at 9 02 34 pm

Expected outcome

I would expect the colorbar to not have minorticks and to use the same font as other tick labels (I set font to be Arial in matplotlibrc).

Matplotlib version

  • Matplotlib version, Python version and Platform (Windows, OSX, Linux ...)

2.0.0_1, OSX

  • How did you install Matplotlib and Python (pip, anaconda, from source ...)
    Macports

Edit by @afvincent: removed the unnecessary instructions coming from the bug report template.

@QuLogic

This comment has been minimized.

Copy link
Member

commented Mar 17, 2017

It appears you also have some custom rcParam settings; the ticks should not be pointing inward. In any case, I can still reproduce myself.

These minor ticks are not supposed to be shown with so many major decades visible, right @efiring? @NelleV?

The font weirdness is probably #8017.

@billtau

This comment has been minimized.

Copy link
Author

commented Mar 17, 2017

Oh, yes, I changed rcParam so that so ticks point inward. Sorry I didn't mention that. First time to report something here.

@NelleV

This comment has been minimized.

Copy link
Member

commented Mar 17, 2017

Indeed, I would not have expected minor ticks to appear.

@tacaswell tacaswell added this to the 2.1 (next point release) milestone Mar 17, 2017

@tacaswell tacaswell changed the title Colorbar with imshow(logNorm) produce incorrect results Colorbar with imshow(logNorm) shows unexpected minor ticks Mar 17, 2017

@tacaswell tacaswell modified the milestones: 2.1 (next point release), 2.1.1 (next bug fix release) Sep 24, 2017

@tacaswell tacaswell modified the milestones: 2.1.1 (next bug fix release), 2.2 (next feature release) Oct 9, 2017

@afvincent

This comment has been minimized.

Copy link
Contributor

commented Oct 23, 2017

FWIW this issue still present on Matplotlib 2.1.

If that was not obvious, please note that the problem does not come from ax.minorticks_off as the subticks are actually major ticks in the case of LogLocator. I did not find where is located the problem in LogLocator for the moment, but in the meantime a possible workaround is:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.mlab import bivariate_normal
from matplotlib.ticker import LogLocator
from matplotlib.colors import LogNorm

N = 100
x = np.linspace(-3.0, 3.0, N)
y = np.linspace(-2.0, 2.0, N)
X, Y = np.meshgrid(x, y)

z = (bivariate_normal(X, Y, 0.1, 0.2, 1.0, 1.0)
     + 0.1 * bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0))
z = np.ma.masked_where(z <= 0, z)

fig, ax = plt.subplots()
cs = ax.imshow(z, norm=LogNorm())
cbar = fig.colorbar(cs)
# print(cbar.ax.yaxis.get_major_locator())  # => <matplotlib.ticker.FixedLocator object at 0x7f0691e57748> : beware, not a LogLocator instance!
cbar.ax.yaxis.set_major_locator(LogLocator())  # <- Why? See above.
cbar.set_ticks(cbar.ax.yaxis.get_major_locator().tick_values(z.min(), z.max()))
fig.show()

workaround_8307

@afvincent

This comment has been minimized.

Copy link
Contributor

commented Oct 24, 2017

Well, the handling of the tick locator seems a bit convoluted with colorbars, at least for my current understanding ^^... Nevertheless, here is a workaround that is bit simpler than my previous one (it was not obvious to me that one could simply pass a Locator instance to cbar.set_ticks...):

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.mlab import bivariate_normal
from matplotlib.ticker import LogLocator
from matplotlib.colors import LogNorm

N = 100
x = np.linspace(-3.0, 3.0, N)
y = np.linspace(-2.0, 2.0, N)
X, Y = np.meshgrid(x, y)

z = (bivariate_normal(X, Y, 0.1, 0.2, 1.0, 1.0)
     + 0.1 * bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0))
z = np.ma.masked_where(z <= 0, z)

fig, ax = plt.subplots()
cs = ax.imshow(z, norm=LogNorm())
cbar = fig.colorbar(cs)
# One-line workaround (NB: this parameter can also be directly passed to *fig.colorbar*)
cbar.set_ticks(LogLocator())  # defaults are more or less what one wants (otherwise adapt the parameters)

fig.show()
@afvincent

This comment has been minimized.

Copy link
Contributor

commented Oct 24, 2017

Ok, if I understood it correctly, the “culprit” may be the following line in colorbar.ColorbarBase._ticker:

                    locator = ticker.LogLocator(subs='all')

that was introduced by 1fe5820.

Pinging @efiring, who is apparently our LogLocator & Colorbar expert :). TBH I am not sure that there is much that one can do about this “surprising behavior” (i.e. cbar.ax.minorticks_off having no effect because colorbar.ColorbarBase._ticker is called anyway in colorbar.ColorbarBase.update_ticks). Except maybe adding some proper example, something like:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import LogLocator
from matplotlib.colors import LogNorm

# Dummy data
N = 101
x = np.linspace(-3.0, 3.0, N)[np.newaxis, :]
y = np.linspace(-1.0, 1.0, N)[:, np.newaxis]
z = (x.max() - np.abs(x))*(y.max() - np.abs(y))**2 + 1e-3

fig, (ax0, ax1) = plt.subplots(ncols=2, figsize=(9.6, 4.8))

for ax, title, ticks in [
        (ax0, "Default colorbar\nwith “minor” log ticks", None),
        (ax1, "Colorbar with\nonly base log ticks", LogLocator())
        ]:
    cs = ax.imshow(z, norm=LogNorm())
    ax.set_title(title)
    cbar = fig.colorbar(cs, ax=ax, ticks=ticks)

fig.show()

tentative_example

@jklymak

This comment has been minimized.

Copy link
Contributor

commented Oct 24, 2017

ping @efiring who said he was looking at colorbar ticks...

@efiring

This comment has been minimized.

Copy link
Member

commented Oct 24, 2017

Yes. Short of a major overhaul of the whole tick system, what is needed is to add full support for minor ticks to the colorbar. In reality this should be needed only for a log scale, but it probably makes more sense to put in all the machinery instead of special-casing log scales. Then colorbar ticking would be more similar to normal axis ticking. It will never be identical, though, because a colorbar simply is not a normal Axis in a normal Axes.

@dstansby

This comment has been minimized.

Copy link
Contributor

commented May 4, 2019

This is working fine in 3.1; my guess is it was fixed by recent changes to how colorbar axes are handled.

@dstansby dstansby closed this May 4, 2019

@dstansby dstansby modified the milestones: needs sorting, v3.0.0 May 4, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.