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

Closed
wants to merge 1 commit into
from

Conversation

Projects
None yet
7 participants
Member

dmcdougall commented Sep 26, 2012

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.

Member

dmcdougall commented Sep 26, 2012

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

Owner

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.

Member

dmcdougall commented Sep 26, 2012

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?

Owner

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.

Member

dmcdougall commented Sep 26, 2012

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.

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 referenced this pull request May 29, 2013

Closed

ability to unshare axis #318

Member

dmcdougall commented Jun 22, 2013

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?

Member

dmcdougall commented Jun 29, 2013

@mdboom Would you mind playing with this today?

(sprinty mcsprintington)

Owner

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.

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...

Owner

tacaswell commented Jul 16, 2013

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.

Owner

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?

Member

dmcdougall commented Aug 18, 2013

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.

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

Member

dmcdougall commented Jul 31, 2014

@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.

Member

WeatherGod commented Jul 31, 2014

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

Owner

tacaswell commented Dec 15, 2015

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

@tacaswell tacaswell closed this Dec 15, 2015

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.

@lkjell lkjell referenced this pull request Dec 4, 2017

Open

Share and unshare axes after creation. #9923

0 of 6 tasks complete
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment