-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
twinx / twiny inherit autoscale behavior for shared axis #7904
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
self.yaxis.tick_left() | ||
ax2.xaxis.set_visible(False) | ||
ax2.set |
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.
what does this line do?
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.
oops, made a goof there. removed.
@@ -145,6 +145,24 @@ def test_twinx_cla(): | |||
assert ax.yaxis.get_visible() | |||
|
|||
|
|||
@cleanup | |||
def test_twin_inherit_autoscale(): |
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.
As much as I dislike image tests, do you have one of those, or some other inspection of the axis to check that the ticks are acting the way you expect?
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.
Yeah, I could create something motivated by the example in #6860 (i.e. that the plotted points should reflect that the original (visible) axis and twinned (invisible) axis share the same autoscaling behavior.
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.
added a simple image comparison.
Also, how does this intersect with #7528? |
ed9b984
to
b01c31d
Compare
mm, i have to check out #7528. I think I need to understand a bit better what |
I don't think this PR conflicts directly with #7528 in terms of functionality, but maybe testing can be consolidated. Seems like there's some confusion on whether the |
b01c31d
to
fcf88d4
Compare
This looks good to me. I'd just add a bit of documentation on the |
423bf47
to
c5ea558
Compare
@NelleV thanks, added a note about the autoscale setting to the docstrings. |
Thanks! It looks good to me. |
@@ -3902,18 +3902,18 @@ def _make_twin_axes(self, *kl, **kwargs): | |||
|
|||
def twinx(self): | |||
""" | |||
Create a twin Axes sharing the xaxis | |||
Create a twin Axes instance sharing the x-axis with self |
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.
not clear what self is here? (Basically not sure this is any clearer than the original wording)
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.
Addressed
ticks on left and the returned axes will have ticks on the | ||
right. To ensure tick marks of both axis align, see | ||
`~matplotlib.ticker.LinearLocator` | ||
Create a twin Axes instance for generating a plot with a shared |
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.
also not sure this is any clearer. docs support images, so maybe that's the way to go here?
https://github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/axes/_axes.py#L1660
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.
An image would be a good idea. These docs feel like they are about implementation rather than the use case - I think what we want to say is "call this method if you want to create a plot with 1 x-axis and 2 y-axes". To me the extra xaxis object is an artifact which is hidden, but apparently there is another use case...
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.
Since these are the dev docs, they kinda have to stay in implementation land. Your string is great, but more appropriate for a gallery example. Honestly, I'd just prefer the original docstring. I think with a small image, they're clear
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.
If the docstring is going to focus on implementation, it still needs to be revised to reflect what's actually going on behind the scenes. twinx/twiny doesn't really "share" an axis in the sense of sharing the Axis
object, but instead creates a 2nd, invisible one..I can try to encapsulate this aspect and revise.
|
||
Notes | ||
------ | ||
----- | ||
For those who are 'picking' artists while using twiny, pick | ||
events are only called for the artists in the top-most axes. |
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.
top-most relative to what?
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 think this means the "original" Axes.
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.
Actually I'm not really familiar with the Artist/picker API, I'm not really sure what top-most is in this context..
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 Axes
are rendered in some order (determined by the z-order and sorted in the Figure
draw method). Which ever one draws last (has highest z-order) is the one that gets the pick event.
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.
If I remember correctly, the new axis ends up on top. I remember having some issues with legends getting obscured by gridlines because of this.
c5ea558
to
3a14787
Compare
@@ -3902,18 +3902,18 @@ def _make_twin_axes(self, *kl, **kwargs): | |||
|
|||
def twinx(self): | |||
""" | |||
Create a twin Axes sharing the xaxis | |||
Create a twin Axes instance sharing the x-axis with the parent |
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.
with the parent what?
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.
is it that unclear? maybe "parent" isn't the exact terminology, but after all twinx()
is a method called on an existing Axes
. I am going to revert this part of the docstring edit.
self.yaxis.tick_left() | ||
ax2.xaxis.set_visible(False) | ||
ax2.patch.set_visible(False) | ||
return ax2 | ||
|
||
def twiny(self): | ||
""" | ||
Create a twin Axes sharing the yaxis | ||
Create a twin Axes instance sharing the y-axis with the parent |
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.
same as above
ead4817
to
1492d30
Compare
OK, I've revised the However this makes me wonder if the implementation is flawed in that the scales of the twinned axis will diverge if autoscale is turned on, since plotting is done on either the original or the new |
`~matplotlib.ticker.LinearLocator` | ||
|
||
Returns | ||
------- | ||
Axis |
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.
wow, good catch!
This is making my head hurt. I'm wondering, and you likely know better than me at this point, but what I'm getting from you is that one y is hooked into the displayed x and the other is y hooked into the hidden x? if so, I agree that they they should just both be hooked into a single x, but I wonder if that's possible... |
Actually, sorry it seems to work fine with the separate xaxis instances - the autoscaling does stay in sync between the "original" and the "twinned" x-axes. So while the implementation is still confusing to me, I think this will work OK. import numpy as np
import matplotlib.pyplot as plt
fig, ax1 = plt.subplots()
t = np.arange(0.01, 10.0, 0.01)
s1 = np.exp(t)
ax1.plot(t, s1, 'b-')
ax1.set_xlabel('original x-axis')
ax1.set_ylabel('exp', color='b')
ax1.tick_params('y', colors='b')
ax1.tick_params('x', colors='b')
ax2 = ax1.twinx()
ax2.set_ylabel('sin', color='r')
ax2.tick_params('y', colors='r')
ax2.tick_params('x', colors='r')
# Manually set the twinned x-axis visible and shift it down
ax2.xaxis.set_visible(True)
ax2.spines['bottom'].set_position(('axes', -0.2))
ax2.set_xlabel('twinned x-axis')
t2 = np.arange(0.01, 20.0, 0.01)
s2 = np.sin(2 * np.pi * t2)
ax2.plot(t2, s2, 'r.')
plt.show() |
Don't apologize! There are loads of moving pieces here and it gets confusing quickly. Ok, then far as I can tell, the goal of #7376 is to treat original and twin x as two semi-independent axis, in that they should have the same min & max but can be labeled individually. And so making a shared x-axis would probably make that functionality impossible. @madphysicist wanna chime in? |
Yep, making a shared x This should be ready now - if it gets merged @madphysicist will have to sort a merge conflict in the docstring, but nothing functionally impactful, I think. |
Looks good to me, just waiting on gitter.im for how to deal with the appveyor failure that has nothing to do with your test. |
`~matplotlib.ticker.LinearLocator` | ||
|
||
Returns | ||
------- | ||
Axis | ||
The newly created axis | ||
Axes |
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 think that this should be lowercase.
x-axis positioned opposite to the original one (i.e. at top). The | ||
y-axis autoscale setting will be inherited from the original Axes. | ||
To ensure that the tick marks of both x-axes align, see | ||
`~matplotlib.ticker.LinearLocator` |
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 am not sure that I understand the last sentence. Specifically, how does looking at the documentation for LinearLocator ensure tick alignment? :)
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 is the original wording - do you have any suggestions for a better sentence? (or you could incorporate it in your PR since that deals with tickers)
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 will try to figure out what that sentence means exactly and attempt to improve it.
|
||
Returns | ||
------- | ||
Axis | ||
The newly created axis | ||
Axes |
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.
Lowercase?
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.
revised
1492d30
to
0c0c3d6
Compare
@naoyak Until fairly recently, I thought that twinning actually meant sharing the axis. Since that is not the case, your earlier comment makes me question this whole PR:
I don't think that sharing the axis object is an option. I think there may need to be a special provision in autoscaling that sets the widest possible bounds for the entire chain of twinned axes. Inheriting autoscaling is definitely a necessary part of this, but autoscaling itself may need to be redefined to special-case axes with twins. I think that fixing that issue that way will also address the concerns about my PR and autoscaling that @efiring brought up. |
I think that's what the current implementation does, actually - I am not sure how this is accomplished under the hood, though. Per discussion above I realized that sharing the At this point this PR only sets the autoscale setting equal for the twinned axis instances and doesn't touch anything else. I think it's safe to merge without messing with your PR, what do you think? |
I think that you are safe to merge. I am working on some autoscaling tests right now. I am pretty sure there will be no conflict but it will be interesting to see how our PRs play together. |
#6860 wants a backport. |
Fixes #6860
Creating
twinx()
ortwiny()
should inherit the autoscaling behavior from the parent Axes instance, but only for the shared axis. Previously,twinx()
would autoscale on the x-axis regardless of the setting on the original Axes, meaning that plotting on the twin would look incorrect with the (visible) original x-axis.