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

Secondary_y axis default limit (top) & bound (upper) not matching ticks #5560

Closed
esvhd opened this issue Nov 25, 2015 · 6 comments

Comments

Projects
None yet
4 participants
@esvhd
Copy link
Contributor

commented Nov 25, 2015

Hi,

I raised this on SO see link here. The comments there provide clarity on this issue.

Started seeing this after I upgraded to 1.5 - but not sure if this is an issue just for 1.5.

The following code shows that, with the default settings, the secondary (RHS) y axis' max tick does not equal to its ylim top.

It doesn't feel like a bug, but it could be a behaviour that is nice to have, for example, when I need to match the grid lines for both axes manually by making sure that the chart actually shows the same number of ticks on both axes. At the moment RHS axis has 8 ticks but the chart only shows 7 of them by default. You can fix this with set_ybound(upper=...) but it's just a little inconvenient.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline

x = [x + 0.5 for x in range(10)]
z = [(y+0.0)/10-0.004 for y in range(10)]
df1 = pd.DataFrame({'x': x})
df2 = pd.DataFrame({'z': z})

fig, ax1 = plt.subplots()
# df1.plot(ax=ax1)
ax1.plot(df1)

ax2= ax.twinx()
# df2.plot(ax=ax2, c='r')
ax2.plot(df2, c='r')

# uncommet this line below to set bounds explicitly which fixes this.
# ax2.set_ybound(upper=1.2)

print('ax1 ticks', ax.get_yticks())
print('ax2 ticks', ax2.get_yticks())

print('ax1 ybound', ax.get_ybound())
print('ax2 ybound', ax2.get_ybound()) # this shows the issue
print('ax2 ylim', ax2.get_ylim()) # this shows the issue

Output prints, Note: ax2's ylim & ybound max value are both 1 rather than 1.2, which is the max of its ticks:

('ax1 ticks', array([  0.,   2.,   4.,   6.,   8.,  10.]))
('ax2 ticks', array([-0.2,  0. ,  0.2,  0.4,  0.6,  0.8,  1. ,  1.2]))
('ax1 ybound', (0.0, 10.0))
('ax2 ybound', (-0.20000000000000001, 1.0000000000000002))
('ax2 ylim', (-0.20000000000000001, 1.0000000000000002))

Screenshot:

so

@tacaswell tacaswell added this to the unassigned milestone Nov 25, 2015

@tacaswell

This comment has been minimized.

Copy link
Member

commented Nov 25, 2015

I do not really understand either this issue or the SO question.

Can you provide an example which does not go through pandas plotting?

@esvhd

This comment has been minimized.

Copy link
Contributor Author

commented Nov 25, 2015

Updated code above to use ax.plot instead of DataFrame.plot.

Managed to upload the chart. Note that the RHS axis (variable ax2 in code) upper bound is at 1.0, but get_ticks() shows array([-0.2, 0. , 0.2, 0.4, 0.6, 0.8, 1. , 1.2]), question is why the upper bound isn't 1.2 by default, as suggested by the maximum tick value?

This can be inconvenient sometimes. For example, one way to align the grid lines from both axes is to make sure both axes have the same number of ticks. (Welcome other suggestions!) The no. of ticks for RHS in the chart displayed do not match len(ax2.get_yticks()), which makes it a little inconvenient when determine what should be the common number of ticks for both axes.

Hope this helps to clarify. Thanks.

@tacaswell

This comment has been minimized.

Copy link
Member

commented Nov 25, 2015

So the minimal example is:

x = [x + 0.5 for x in range(10)]
z = [(y+0.0)/10-0.004 for y in range(10)]
fig, ax = plt.subplots()
ax2 = ax.twinx()

ax.plot(x)
ax2.plot(z, c='r')

ax.grid(True, axis='y')
ax2.grid(True, axis='y')

get_yticks falls back to calling ax2.yaxis.major.locator() which asks the major locator instance where to put ticks. The way this is used internally in the library, this call is allowed to return extra locations (that may not be visible on the graph).

The way that mpl is implemented underneath the tick locators, the tick formatters, and the axis limits are implemented independently (and that is definitely a feature).

@esvhd

This comment has been minimized.

Copy link
Contributor Author

commented Nov 25, 2015

Thanks for the explanation. What's the benefit of allowing ax2.yaxis.major.locator() to return extra locations that may not be visible on the graph?

Also when you say that it is a feature to implement tick locator/formatter/axis limits independently, what are the benefits to do so? Just trying to understand the essence of its design.

Appreciate it!

@WeatherGod

This comment has been minimized.

Copy link
Member

commented Nov 30, 2015

The primary benefit, IIRC, is to better handle situations involving
floating point (im-)precision (i.e., by having extra ticks available in
case one of them may actually be desirable).

On Wed, Nov 25, 2015 at 5:47 PM, esvhd notifications@github.com wrote:

Thanks for the explanation. When you say that it is a feature to implement
tick locator/formatter/axis limits independently, what are the benefits to
do so? Just trying to understand the essence of its design. Appreciate it!


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

@efiring

This comment has been minimized.

Copy link
Member

commented Jan 26, 2018

If I understand the comments correctly, this can be closed now. If I have misunderstood, please reopen it.

@efiring efiring closed this Jan 26, 2018

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.