Skip to content

Add ability to unshare a pair of shared [xy] axes #1312

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

Closed
wants to merge 1 commit into from

Conversation

dmcdougall
Copy link
Member

Example:

import matplotlib.pyplot as plt

# Let's say you shared a pair of x axes
fig = plt.figure()
ax1 = fig.add_subplot(2, 1, 1)
ax2 = fig.add_subplot(2, 1, 2, sharex=ax1)
ax1.plot(range(3))
ax2.plot(range(10))

# You can then unshare the x axes shared between ax1 and ax2
ax1.unshare_x_axes(ax2)

# Alternatively, you can call
ax2.unshare_x_axes(ax1)

# They are equivalent

I've tried this simple case out, and it seems to work quite nicely. I had to add functionality to remove weak references from cbook.Grouper() and that change is included in this pull request.

@dmcdougall
Copy link
Member Author

I should also say that this is my attempt at resolving #318.

@mdboom
Copy link
Member

mdboom commented Sep 26, 2012

These seems to be too zealous in its disconnection. Consider this (based on shared_axis_demo.py):

from pylab import *

t = arange(0.01, 5.0, 0.01)
s1 = sin(2*pi*t)
s2 = exp(-t)
s3 = sin(4*pi*t)
ax1 = subplot(311)
plot(t,s1)
setp( ax1.get_xticklabels(), fontsize=6)

## share x only
ax2 = subplot(312, sharex=ax1)
plot(t, s2)
# make these tick labels invisible
setp( ax2.get_xticklabels(), visible=False)

# share x and y
ax3 = subplot(313,  sharex=ax1, sharey=ax1)
plot(t, s3)
xlim(0.01,5.0)

ax3.unshare_x_axes(ax2)
show()

The unshare ends up disconnecting all three of the shared x axes, not just the pair mentioned. (There is another more minor bug illustrated by this too -- that setting adjustable back to "box" should only happen if the axes is not shared in both x and y).

Admittedly, this is a problem with the Grouper 's design. The shared axes are logically an undirected graph. Grouper builds up disjointed subsets of that graph, but it doesn't actually retain the graph itself, so it's not possible to delete a single edge like this.

Think of it this way: suppose you have the graph where A is connected to B and B is connected to C:

A -- B -- C

The grouper builds a single set "ABC" but throws away the source edges entirely. So then if you later want to remove the edge BC, it doesn't know whether A was connected to B or C in the first place, so it can't know that the resulting subset should be "AB".

So it looks like we may want to reimplement Grouper as an actual undirected graph -- or implement sharing in terms of one. The latter is how the original implementation of axis sharing was implemented, and it was difficult to get right. The other requirement of however this is done is that it must not keep alive axes on its own (implying weak references), but that the connections must survive the axes being deleted, since connections are transitive.

Sorry to have sent you on a wild goose chase about adding remove to the Grouper: I should have realized how tricky that would be.

@dmcdougall
Copy link
Member Author

I see, the Grouper does not preserve transitivity. That makes sense. I did wonder about how the three-way share would work.

I'd still like to implement this feature, but I think -- as you mentioned -- it's necessary to re-implement either Grouper or the sharing mechanism.

As an initial idea, how about each Axes just keeps a list of other Axes objects that it shares with (just think about x-axis sharing for the moment). This would preserve directional information.

Using your A -- B -- C diagram above, we have:

The _shared_x_axes variable of A would change from a Grouper to a simple list, [B].
The _shared_x_axes variable of B would change from a Grouper to a simple list, [A, C].
The _shared_x_axes variable of C would change from a Grouper to a simple list, [B].

Then a call to A.unshare_x_axes(B) would result in:

The _shared_x_axes variable of A would change from a Grouper to a simple list, [].
The _shared_x_axes variable of B would change from a Grouper to a simple list, [C].
The _shared_x_axes variable of C would change from a Grouper to a simple list, [B].

The resulting schematic would be:

A B -- C

Thoughts?

@mdboom
Copy link
Member

mdboom commented Sep 26, 2012

That seems reasonable. One would then need a function to do depth search on the graph to find all of a shared axes siblings when then axes are updated -- but that should probably work fine.

One care that must be taken is when sharing axes between figures -- in this approach, a shared axes has the potential to keep another figure alive and non-deletable. The current approach uses weak references to avoid that.

@dmcdougall
Copy link
Member Author

Fair, and a depth-first search is not going to be slow here. How many axes does one typically share? Maybe, at worst, 10?

I think the weak reference approach should still be fine. The only thing that has changed is the ability to see the sharing relationships between axes.

Edit: grammar.

@pelson
Copy link
Member

pelson commented May 14, 2013

What is the status of this PR? Does it need some work before it can be merged? If so, I suggest we close this until it is ready for consideration.

Cheers,

@efiring efiring mentioned this pull request May 29, 2013
@dmcdougall
Copy link
Member Author

This appears to no longer be overzealous in the unsharing. I'm not sure this implementation is the best. I'm also unsure of the consequences on storing a list of Axes objects, rather than weakrefs to the Axes objects. I'm sure that can easily be changed though.

Is someone willing to play with this and give some feedback?

@dmcdougall
Copy link
Member Author

@mdboom Would you mind playing with this today?

(sprinty mcsprintington)

@mdboom
Copy link
Member

mdboom commented Jun 29, 2013

@dmcdougall, @ivanov and I just had a meat-space discussion about this. We came down to this: should unsharing work as an inverse operation to sharing, or should it work to orphan a single node?

Imagine this: you start with:

A -- B -- C     D -- E -- F

then you join C and D

A -- B -- C -- D -- E -- F

with the implementation in this PR, it is possible to undo that operation with C.unshare(D).

However, one could conceive of this:

A -- B -- C

In this case, saying A.unshare(B) could be surprising because the link between A and C would also be lost. This suggests that the interface should be simply A.unshare() which would completely orphan the node.

Perhaps we need to get back to the original use case that motivated this to decide the best way forward.

@pelson
Copy link
Member

pelson commented Jul 15, 2013

Perhaps we need to get back to the original use case that motivated this to decide the best way forward.

Definitely. Personally I've never seen a need for this functionality, but my mind is open to a sensible use case...

@tacaswell
Copy link
Member

I think one use case is building applications which have mpl embedded and wanting the ability to add/remove overlays where twin[x,y] is the right way to do it.

@mdboom
Copy link
Member

mdboom commented Jul 16, 2013

@tacaswell : I suspect in that case, my first example above is the appropriate one, and this PR implements it.

@dmcdougall: Do you want to have the honours of rebasing this and adding a test or two?

@dmcdougall
Copy link
Member Author

There's a slight problem with this implementation -- sharing is now no longer transitive. Transitive sharing is the behaviour of the current implementation, using cbook.Grouper(). I'm going to need to think about the best way to do this.

Regarding the unsharing vs. orphaning discussion, there's no reason we can't have both.

@a6073758
Copy link

any news on this issue? Is there in the meantime some way of linking existing axes or unsharing?

@dmcdougall
Copy link
Member Author

@a6073758 Thanks for expressing interest in this. Is this feature something you need? If so, would you mind saying a few words about a use-case you have in mind for it? I'd like to better understand how it will be used.

A decision needs to be made regarding what 'unsharing' should actually do. And it should be done so that the current sharing behaviour remains unchanged. The interface @mdboom suggested seems very reasonable, with A.unshare() orphaning the A axes from all others.

@WeatherGod
Copy link
Member

I'd like to probe this unsharing concept a bit. I could imagine it might
make sense in an interactive situation where one might want to "pin" one of
the subplots for a while. Once an axes is unshared, should it be possible
to "reshare" the axes? If so, whose limits do we adopt? While this might be
obvious in a 2x2 grid situation, what about a 2x1?

On Thu, Jul 31, 2014 at 6:29 AM, Damon McDougall notifications@github.com
wrote:

@a6073758 https://github.com/a6073758 Thanks for expressing interest in
this. Is this feature something you need? If so, would you mind saying a
few words about a use-case you have in mind for it? I'd like to better
understand how it will be used.

A decision needs to be made regarding what 'unsharing' should actually do.
And it should be done so that the current sharing behaviour remains
unchanged. The interface @mdboom https://github.com/mdboom suggested
seems very reasonable, with A.unshare() orphaning the A axes from all
others.


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

@tacaswell tacaswell modified the milestones: proposed next point release, next point release Feb 19, 2015
@tacaswell tacaswell modified the milestones: next point release, proposed next point release Jun 18, 2015
@tacaswell tacaswell mentioned this pull request Dec 15, 2015
@tacaswell
Copy link
Member

Closing as it looks like moving away from Grouper is problematic and this has not been touched in 1.5 yrs.

@lkjell
Copy link
Contributor

lkjell commented Dec 4, 2017

Guess no progress on this. Since sharing is symmetric and transitive in nature. An unlink operation should simple orphan the node.

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

Successfully merging this pull request may close these issues.

7 participants