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

Extraneous invalid ticks with colorbar extend keyword #4181

Closed
u55 opened this issue Mar 2, 2015 · 4 comments
Closed

Extraneous invalid ticks with colorbar extend keyword #4181

u55 opened this issue Mar 2, 2015 · 4 comments
Assignees

Comments

@u55
Copy link
Contributor

u55 commented Mar 2, 2015

Hi matplotlib developers,
I discovered that the tick values and tick locations on a colorbar can be incorrect when the colorbar extend keyword is set to anything other than the default value of 'neither'. The ticks are sometimes displayed when they should be out of range, and even so, they are located in the wrong places, often overlapping with other correctly-placed ticks. Here is a minimal working example:

from __future__ import print_function
import matplotlib.pyplot as plt
from matplotlib.colors import LogNorm
from matplotlib import cm
from numpy.random import rand

Z = rand(6, 10)*0.25
cmap = cm.jet
cmap.set_bad('k')
cmap.set_under('k')
cmap.set_over('w')

plt.subplot(221)
im0 = plt.pcolormesh(Z,cmap=cmap)
im0.set_clim(0.02,0.2)
cb0 = plt.colorbar(im0,extend='both')
print(cb0.ax.get_yticks())
print([i.get_text() for i in cb0.ax.get_yticklabels()])

plt.subplot(222)
im1 = plt.pcolormesh(Z,cmap=cmap)
im1.set_clim(0.02,0.2)
cb1 = plt.colorbar(im1,extend='neither')
print(cb1.ax.get_yticks())
print([i.get_text() for i in cb1.ax.get_yticklabels()])

plt.subplot(223)
im2 = plt.pcolormesh(Z,cmap=cmap,norm=LogNorm())
im2.set_clim(0.02,0.2)
cb2 = plt.colorbar(im2,extend='both')
print(cb2.ax.get_yticks())
print([i.get_text() for i in cb2.ax.get_yticklabels()])

plt.subplot(224)
im3 = plt.pcolormesh(Z,cmap=cmap,norm=LogNorm())
im3.set_clim(0.02,0.2)
cb3 = plt.colorbar(im3,extend='neither')
print(cb3.ax.get_yticks())
print([i.get_text() for i in cb3.ax.get_yticklabels()])

plt.show()

bug_colorbar_extend

Tested with matplotlib 1.4.3, python 2.7, on Linux and Windows.

In the example above, the colorbars on the left subplots, created with extend='both', have extra ticks that are incorrect and are not present in the colorbars on the right created using extend='neither'. The existence of incorrect ticks depends strongly on the chosen clim range and the chosen tick Locator. On a linear colorscale, in the example above, the tick Locator adds an extra tick value of 0.22 even though the maximum clim value is set to 0.2, and the extra tick overlaps with the valid tick at 0.2. On a log colorscale, in the example above, the tick Locator add two extra ticks with values of 1e-2 and 1e-3, even though the minimum clim value is set to 2e-2.

As a temporary workaround, I have been using this function to filter out the bad ticks and then set them manually:

import matplotlib.ticker as ticker
def bugfix_colorbar_extend_ticks(colorbar):
    vmin,vmax = colorbar.get_clim()
    if isinstance(colorbar.norm,LogNorm):
        cticks = ticker.LogLocator().tick_values(vmin,vmax)
    else:
        cticks = ticker.MaxNLocator().tick_values(vmin,vmax)
    cticks = cticks[cticks >= vmin]
    cticks = cticks[cticks <= vmax]
    return colorbar.set_ticks(cticks)

But this only fixes the symptom, not the underlying problem, which I believe has something to do with the way the colorbar axes view limits are handled at draw time.

I appreciate anyone's help in finding a more permanent fix to this bug.
Thanks.

@efiring efiring self-assigned this Mar 2, 2015
@efiring
Copy link
Member

efiring commented Mar 2, 2015

I see where the problem is coming from, I think: it's a matter of a slight tweak to try to handle the inexact nature of floating point arithmetic. The tweak bounds are too loose. I'm not sure yet whether the solution will be tightening the bounds, or changing the strategy. Thank you, @u55, for a nice, clear example.

@u55
Copy link
Contributor Author

u55 commented Mar 2, 2015

You are welcome, and thank you @efiring for the fast reply. Speaking of strategy, I find it odd that the tick Locator is allowed to return ticks that are outside of the requested range given by the tick_values(vmin,vmax) method, such as:

import matplotlib.ticker as ticker
print(ticker.MaxNLocator().tick_values(0.02,0.2))
>>> [ 0.02  0.04  0.06  0.08  0.1   0.12  0.14  0.16  0.18  0.2   0.22]

Why is the last tick, 0.22, even allowed? I realize that it is convenient for matplotlib to allow users to manually enter ticks that may be outside of the current view limits, and therefore matplotlib must dismiss those ticks at draw time. But I assume that the tick_values(vmin,vmax) method of the tick Locator gets called with new vmin and vmax values each time the interactive view limits change, so I am surprised that it does not force the ticks to be within the requested range.

@WeatherGod
Copy link
Member

IIRC, some of this probably came about in a fix for a big problem where
limits of 0 to 0.5 would drop the 0.5 tick because of floating point
errors. So, we changed it to be permissive in what ticks it returns so that
the final decision on what to keep is made later in the draw process -- I
think. My memory is hazy on this. Does this sound familiar @efiring?

On Mon, Mar 2, 2015 at 3:09 PM, u55 notifications@github.com wrote:

You are welcome, and thank you @efiring https://github.com/efiring for
the fast reply. Speaking of strategy, I find it odd that the tick Locator
is allowed to return ticks that are outside of the requested range given by
the tick_values(vmin,vmax) method, such as:

import matplotlib.ticker as tickerprint(ticker.MaxNLocator().tick_values(0.02,0.2))>>> [ 0.02 0.04 0.06 0.08 0.1 0.12 0.14 0.16 0.18 0.2 0.22]

Why is the last tick, 0.22, even allowed? I realize that it is convenient
for matplotlib to allow users to manually enter ticks that may be outside
of the current view limits, and therefore matplotlib must dismiss those
ticks at draw time. But I assume that the tick_values(vmin,vmax) method
of the tick Locator gets called with new vmin and vmax values each time
the interactive view limits change, so I am surprised that it does not
force the ticks to be within the requested range.


Reply to this email directly or view it on GitHub
#4181 (comment)
.

@efiring
Copy link
Member

efiring commented Mar 2, 2015

I think this is to facilitate auto-scaling based on expanding the range to tick values. Probably the Locator classes should all implement a "strict" option, maybe with a tolerance parameter, so I wouldn't have to put this in the colorbar itself. @WeatherGod, I think your hypothesis is an additional rationale.

tacaswell added a commit that referenced this issue Mar 12, 2015
BUG : edit tick locations based on vmin and vmax; closes #4181
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants