Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Share and unshare axes after creation.#9923

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Closed
lkjell wants to merge7 commits intomatplotlib:masterfromlkjell:sharexy
Closed

Share and unshare axes after creation.#9923

lkjell wants to merge7 commits intomatplotlib:masterfromlkjell:sharexy

Conversation

lkjell
Copy link
Contributor

@lkjelllkjell commentedDec 4, 2017
edited
Loading

PR Summary

Implementing share and unshare axes after creation ref PR#1312.

Currently sharing axes has a parent/masteraxes. The class attribute is_sharex,_sharey and_sharez.
In addition there is aGrouper object which store whichaxes share their axes. Because of technical limitation, it is suggested that sharing should be transitive and there is no masteraxes. That is if the masteraxes is unshared the otheraxes in the group will still share their axes. The parent/master attribute are kept for backward compatibility of the behaviour of cla().

Reverting back to WeakSet implementation tofix#4827 too.

Test code

import matplotlib.pyplot as pltimport numpy as npt = np.arange(0.01, 5.0, 0.01)s1 = np.sin(2 * np.pi * t)s2 = np.exp(-t)s3 = np.sin(4 * np.pi * t)ax1 = plt.subplot(311)plt.plot(t, s1)ax2 = plt.subplot(312)plt.plot(t, s2)ax3 = plt.subplot(313)plt.plot(t, s3)ax1.share_x_axes(ax2)ax1.share_y_axes(ax2)# Share both axes.ax3.share_axes(ax1)plt.xlim(0.01, 5.0)ax3.unshare_y_axes()ax2.unshare_x_axes()plt.show()

PR Checklist

  • Has Pytest style unit tests
  • Code is PEP 8 compliant
  • New features are documented, with examples if plot related
  • Documentation is sphinx and numpydoc compliant
  • Added an entry to doc/users/next_whats_new/ if major new feature (follow instructions in README.rst there)
  • Documented in doc/api/api_changes.rst if API changed in a backward-incompatible way

@lkjelllkjell changed the titleSharexyShare and unshare axes after creation.Dec 4, 2017
@@ -496,22 +494,18 @@ def __init__(self, fig, rect,
self.set_aspect('auto')
self._adjustable = 'box'
self.set_anchor('C')
self._sharex = sharex
self._sharey = sharey
self._shared_x_axes = WeakSet()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

self._shared_x_axes = WeakSet([self]) (throughout the PR)


# keep original intention
axes._adjustable = 'datalim'
self._adjustable = 'datalim'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

What happens if one calls set_adjustable() on an axes which is shared with others? Should that be prevented, or somehow propagated?

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

It is a preliminary pr. Discussion is needed. See there are some build error related to this set_adjustable()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

This is a messy question partly addressed by, or at least related to#8752, which needs work to handle twinx, twiny properly and to remove some parts that are no longer needed because of#8626.

@lkjell
Copy link
ContributorAuthor

Funny, forgot there where a 3d plot where you can share the z axis as well...

@lkjell
Copy link
ContributorAuthor

should

def test_inverted_cla():    # Github PR #5450. Setting autoscale should reset    # axes to be non-inverted.    # plotting an image, then 1d graph, axis is now down    fig = plt.figure(0)    ax = fig.gca()    # 1. test that a new axis is not inverted per default    assert not(ax.xaxis_inverted())    assert not(ax.yaxis_inverted())    img = np.random.random((100, 100))    ax.imshow(img)    # 2. test that a image axis is inverted    assert not(ax.xaxis_inverted())    assert ax.yaxis_inverted()    # 3. test that clearing and plotting a line, axes are    # not inverted    ax.cla()    x = np.linspace(0, 2*np.pi, 100)    ax.plot(x, np.cos(x))    assert not(ax.xaxis_inverted())    assert not(ax.yaxis_inverted())    # 4. autoscaling should not bring back axes to normal    ax.cla()    ax.imshow(img)    plt.autoscale()    assert not(ax.xaxis_inverted())    assert ax.yaxis_inverted()    # 5. two shared axes. Clearing the master axis should bring axes in shared    # axes back to normal    ax0 = plt.subplot(211)    ax1 = plt.subplot(212, sharey=ax0)    ax0.imshow(img)    ax1.plot(x, np.cos(x))    ax0.cla()
  assert not(ax1.yaxis_inverted())

still be valid?

@lkjell
Copy link
ContributorAuthor

Think I have everything working now. However, WeakSet is not pickable. Any suggestion on this? Or perhaps just use the old grouper?

@lkjell
Copy link
ContributorAuthor

Ok reverting to use Grouper. But now grouper will remove the item from other set as well. Pickle is possible now. Hopefully everything is ok now.

@dstansbydstansby added this to thev2.2 milestoneDec 5, 2017
@lkjell
Copy link
ContributorAuthor

Was not aware of#4827. Guess it is reverting to WeakSet implementation and hackgetstate andsetstate

plt.xlim(0.01, 5.0)

ax3.unshare_y_axes()
ax2.unshare_x_axes()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I didn't try to run this, but its hard to imagine this actually demos anything in a non-interactive session.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

You do not create share as usual by setting a share parent axes. Sharing is set after the creation of the axes.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I think demos should be readily readable by users since they are one of the primary ways we document Matplotlib. I find this demo is very cryptic. Why are you un-sharing? Just to show that these functions exist? What effect does this have on the example? The description of the demo should state the usual way of sharing, and this alternate way.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

hm... no comment.

@efiring
Copy link
Member

When you unshare you are taking an Axes out of the set, but it looks to me like more is needed. When the Axes was created as shared, itsxaxis.major etc. attributes are made as references to the attributes in the master. This is done incla. Don't you need to also break those connections, perhaps executingcla for the newly orphaned axis? And similarly, don't you need to make those connections in your new method for sharing?

I haven't looked at this in great detail so I might be missing a lot, but I'm uneasy about this major change in the sharing machinery.

@lkjell
Copy link
ContributorAuthor

There was some behaviour with cla() that I left for backward compatible. That is the parent axes attribute I believe you are referring too. The old shared axes reference will get zero out when that axes is unshared. However, the parent reference will not get propagated to new shared axes. During cla the those attributed are cleared or copy over depending on the parent axes attribute.

Sharing axes is a transitive behavior thus the code will only link the axes together and you need to specify yourself which datalim you want to use. Unlink is therefore similar.

@efiring
Copy link
Member

What do you mean by "transitive behavior"? I saw that terminology in#4827 and I didn't understand it there either.

@lkjell
Copy link
ContributorAuthor

It is a mathematical term. Say A share axes with B, and B share axes with C. Transitive property means that A share axes with C also.

Say you now unlink B and C. With parent axes approach A and C should no longer share but A and B still share.

But if you think about it. Because sharing is transitive. And you did not spesify to unlink A and C. Unlink B and C will also unlink B with A, but A and C still keep the link.

@efiring
Copy link
Member

I see the attraction of using a WeakSet and having what looks like a relationship among equals instead of the master/slave paradigm, but the heart of sharing is not the grouping itself, it is the question of what is actually shared and where it lives. With the master/slave system, the slaves have references to attributes in the master, replacing their own attributes. This is the critical part. Having connect/disconnect functions doesn't make much sense to me without a re-thinking of the handling of these attributes--or some other scheme for switching between shared and single-owner attributes.

Are you assuming that someone using the unshare function will then either call cla on the newly isolated Axes, or manually recreate the missing attributes?

What use case is motivating your work on this?

Looking farther, it might be good to have fine-grained control over sharing, allowing sharing of viewlims but independent locators and formatters, for example. I think that this would require some changes to the way locators and formatters work.

@lkjell
Copy link
ContributorAuthor

The old behaviour is not removed. If you share axes as the old way on axes creation you will get the behaviour you describe. In the old way you could still share the axes after creation, but that feature is sort of hidden. However, there were no way to unshare axes.

I am not quite sure what other attributes the slaves keep reference to. My understanding is that the shared axes kept a reference to the parent axes instance. Upon axes creation cla() is called. This will look if there is a reference to a parent axes. If there is then attributes arecopy over. There are no other links than that.

If you are right that the slave keep reference on master's attribute and not a copy then I have to check on that. A simple test is to share the axes as the old way. Unshare. Then change the attribute you mean that are shared in the master axes and check if those changes in the other axes as well.

@lkjell
Copy link
ContributorAuthor

I think I understand what you mean. Sosharex.xaxis.major andsharex.xaxis.minor are reference only.
Perhaps instead of reference a copy should be made.

        if sharex is not None:            # major and minor are class instances with            # locator and formatter attributes            self.xaxis.major = sharex.xaxis.major            self.xaxis.minor = sharex.xaxis.minor            x0, x1 = sharex.get_xlim()            self.set_xlim(x0, x1, emit=False, auto=None)            self.xaxis._scale = mscale.scale_factory(                    sharex.xaxis.get_scale(), self.xaxis)

@lkjell
Copy link
ContributorAuthor

However, I think the best solution without altering too much. When you unshare. You just make a copy of the attributes.

My work is to compare some plots. Sometimes you want to share the datalim, but you also want the option to disable and look at a different location.

@lkjell
Copy link
ContributorAuthor

I see that doing a deep copy ofAxis.major andAxis.minor is not that simple. Therefore I revert the last commit. This just make unshare incompatible with the old behaviour. Doing share axes after creation is compatible with unshare, assuming the sharing group does not have a parent axes.

@efiring
Copy link
Member

I think you are finding is that the present implementation of sharing is more complicated than it appears on the surface. Several mpl components are coupled in not-so-obvious ways. It might be that for your use case, short of a major overhaul of mpl, sharing is not the best solution. Instead, could you use event notification to connect datalims when desired?

@jklymakjklymak modified the milestones:v2.2,v3.0Jan 9, 2018
@jklymak
Copy link
Member

jklymak commentedJan 9, 2018
edited
Loading

@efring (and@lkjell) As we discussed on the phone call, it does seem that all the different ways sharing gets used could use a rethink.

I think@lkjell 's use case is something other interactive users would like, and therefore it would be nice to support. I think this interactive use of "sharing" where the x/y-limits are kept the same so zooms are carried out across axes could/should be disambiguated from the other meaning of sharing: "I want this x-axis tolook like this other x-axis" which is a layout problem rather than a communication problem during interactive use.

As discussed, the two don't need as much care when the aspect ratio is kept atauto, but as soon as aspect ratios are involved, the two aspects of sharing become complicated.

So, I support the idea of this PR, but I think it might be more of a feature request. I've bumped to 3.0 because I don't think this will/should go in until after 2.2 does and we have some time to think this through. If other disagree and would like to see it in sooner, by all means switch back!

@efiring
Copy link
Member

efiring commentedJan 9, 2018 via email

On 2018/01/09 7:25 AM, Jody Klymak wrote: I've bumped to 3.0 because I don't think this will/should go in until after 2.2 does and we have some time to think this through.
Agreed, this is much too big a topic for 2.2.

@lkjell
Copy link
ContributorAuthor

When 3.0 is released? With all respect the previous PR was 5 years ago. And I am not eager to see this goes to dust. The new sharing mechanism is not compatible with the old. But you could still use it exclusively. Or to make it more distinct a rename to share_datalim is more appropriate?

@jklymak
Copy link
Member

2.2 is three weeks. 3.0 is meant to be July. I think July is enough time to sort this out, but I don't think three weeks is.

Lets step back and make sure this is all done properly for 3.0 (where we will also have some more leeway to change things.)

@lkjell
Copy link
ContributorAuthor

Discussion on when you merge can take another time.

I do want to highlight with current way of sharing axis (datalim) with valid API. Of course there is no way to unlink and you still have the problem withAxis.major andAxis.minor that the new axes you share with is different from parent axes. Now with this it is possible say A->B and C->D.
And now you join B and C together. This is possible with current implementation of sharing.
you just need to access_shared_x_axes attribute fromget_shared_x_axes(). The share group would now have two parent axes.

Therefore I suggest that:

  1. Make it possible to do a deep copy ofTicker() instance.
  2. All axes keep their own reference ofmajor andminor.
  3. No more parent axes.cla() will default to the other shared axes attributes.
  4. Share axes means to share datalim.
  5. Share will only link axes together. Updating datalim must be called explicitly. The same with the other attributes.
import numpy as npimport matplotlib.pyplot as pltt = np.arange(0.01, 5.0, 0.01)s1 = np.sin(2*np.pi*t)s2 = np.exp(-t)s3 = np.sin(4*np.pi*t)ax1 = plt.subplot(311)plt.plot(t,s1)plt.setp(ax1.get_xticklabels(), fontsize=6)## share x onlyax2 = plt.subplot(312, sharex=ax1)plt.plot(t, s2)# make these tick labels invisibleplt.setp( ax2.get_xticklabels(), visible=False)ax3 = plt.subplot(313)plt.plot(t, s3)plt.xlim(0.01,5.0)# Share x and y axisax2.get_shared_x_axes().join(ax1, ax3)ax2.get_shared_y_axes().join(ax1, ax3)print(ax1.xaxis.major == ax3.xaxis.major)print(ax1.xaxis.major == ax2.xaxis.major)

@jklymak
Copy link
Member

Not fully parsing the above yet, but just for completeness, what are you considering the "default" in the above?

@lkjell
Copy link
ContributorAuthor

I would consider 5 as not to break old behaviour. But currently in the PR 4 is the default.

@lkjell
Copy link
ContributorAuthor

lkjell commentedMar 10, 2018
edited
Loading

For testing use commit837723f for behaviour 4, and commitd31d4fa for behaviour 5.
Latest commit has some suggested behaviour forsymmetric=True andtransitive=True.
Comment them out to test them. They have their strength and weaknesses. See test code in original post.

@jklymak
Copy link
Member

I'll cheerfully admit I'm not getting this PR. I understand what you mean bytransitive andsymmetricbut I'm not understanding why you'd want either of them. If I change A's limits and its shared with B, I expect B's limits to change. If C is shared with B, I expect its limits to change as well. Transitive and symmetric are what I'd always expect, and I don't see the use case for not making shared axes have these properties.

As for "reverse", I have no idea what thats trying to do? Link the self axes with the list of axes, but only in one direction?

I thought I'd try to understand it, but the example above returns the following, which as far as I can tell has no shared y axes? So I'm confused. Not at all clear why it fixes the seaboard issue.

test

@lkjell
Copy link
ContributorAuthor

@jklymak sharing will link only. Propagation of viewlimit must be done by user. Transitive and symmetric is by default on. Reverse would do the opposite, i.eax1.share_x_axes(ax2) =ax2.share_x_axes(ax2, reverse=True).

The example just mimic behaviour, you still need to make twins and share with the correct axes.

@lkjell
Copy link
ContributorAuthor

@jklymak pehaps you are right. I am thinking too complicated. Just use from commit
804bd66

@ImportanceOfBeingErnest
Copy link
Member

I do have problems understanding what this PR is doing and especially which problem it is trying to solve.

Currently if you want to share axes you'd use thejoin method of theGrouper. If you want to unshare axes, you'd use theremove method of theGrouper

import matplotlib.pyplot as pltfig, (ax, ax2) = plt.subplots(ncols=2)ax.plot([0,1],[0,1])ax2.plot([1,2],[1,2])#share axesax.get_shared_y_axes().join(ax,ax2)ax.autoscale()#unshare axesax.get_shared_y_axes().remove(ax2)ax.autoscale()ax2.autoscale()plt.show()

Also joining operation with the currentGrouperare transitive.

import matplotlib.pyplot as pltfig, (ax, ax2, ax3) = plt.subplots(ncols=3)ax.plot([0,1],[0,1])ax2.plot([1,2],[1,2])ax3.plot([2,3],[2,3])# join ax - ax2ax.get_shared_y_axes().join(ax,ax2)# join ax2 - ax3ax2.get_shared_y_axes().join(ax2,ax3)# transitivity is givenprint(ax3.get_shared_y_axes().joined(ax,ax3))ax.autoscale()plt.show()

So, I think it would be good if there was a problem shown that is currently hard to come by, which this PR would solve.

@lkjell
Copy link
ContributorAuthor

@ImportanceOfBeingErnest the discussion is long. The join and remove from Grouper I believe is not intended to be used like that.

The PR fixes pickling, share of formatter and locator, unset parent axes, and a clean API for doing sharing and unsharing after axes creation.

PS. the rebase is not finished.

@ImportanceOfBeingErnest
Copy link
Member

ImportanceOfBeingErnest commentedJun 5, 2018
edited
Loading

Precisely because this is very long, could you add example cases for the issues this solves?

If the Grouper methods are not to be used like I did, it would be good to know why.

@lkjell
Copy link
ContributorAuthor

Sigh... Some of the problems listed above in the comments will also be a problem with current implementation.

@lkjell
Copy link
ContributorAuthor

Small example that shows formatter and locator is left as crufts.

import matplotlib.pyplot as pltax1 = plt.subplot(1,2,1)ax2 = plt.subplot(1,2,2, sharex=ax1)# ax1.invert_xaxis() #pick one to see different effectax2.invert_xaxis()ax2.get_shared_x_axes().remove(ax2)ax2.cla()ax1.cla()plt.show()

Sharexy use Weakref instead of cbook.GrouperAdd a share/unshare function to share/unshare both x,y,z axisremove() unshare axes successfully.Revert to Grouper. But now grouper remove will also remove from other sets.unshare will also remove parent/master axes.unshare also remove parent axes in the orphan axes.Adding unshare and share tests.Add what is new.Adding unshare axis demo.Revert "Revert to Grouper. But now grouper remove will also remove from other sets."Converting Weakset to list and back during pickle.Adding pickle test.Update tests to use Weakset backend.Add example of how to share 3D plot.Add an API breakage message.change twinx, twiny to use the new share api.Adding an is sharing axes method.Fix overline in example too short.Use the new is_sharing_[x,y]_axes when appropriateupdate tests to use is sharing axes methodsSimplify share and unsharing code to one.remove crufts.Change quotation marks.Update descriptions.Update docs. Unshare axes if related.Sharing will implicit set adjustable to datalim.Copy major and minor when unshare axes.If unshare a parent axes its children will copy a new major minor.
Delete api change file in old api folder.
get shared return a shallow copy.
pep8 is a girl
@jklymakjklymak modified the milestones:v3.0,needs sortingJul 9, 2018
@anntzeranntzer mentioned this pull requestSep 18, 2019
6 tasks
@mwaskom
Copy link

Does#15287 close this?

@anntzer
Copy link
Contributor

No, it doesn't allow unsharing (I guess mostly because we still can't decide on the exact semantics of that).

mwaskom reacted with thumbs up emoji

@jklymak
Copy link
Member

#15287 does some of this. The rest is probably a bit of an edge use case. Thanks for the suggestion@lkjell !

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@efiringefiringefiring left review comments

@anntzeranntzeranntzer left review comments

@jklymakjklymakjklymak left review comments

Assignees
No one assigned
Labels
None yet
Projects
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

Pickled Figure Loses sharedx Properties
10 participants
@lkjell@efiring@jklymak@dopplershift@stonebig@ImportanceOfBeingErnest@mwaskom@anntzer@story645@dstansby

[8]ページ先頭

©2009-2025 Movatter.jp