Skip to content

FIX: better handle margins on ax.bar #7471

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

tacaswell
Copy link
Member

Cancel margin on the proper sides depending on if the bar is positive,
negative, or mixed.

This mostly works, some pathological issues with multiple bar plots

import matplotlib.pyplot as plt
plt.ion()

fig, [[ax1, ax2], [ax3, ax4]] = plt.subplots(2, 2, tight_layout=True)

ax1.set_title('all positive')
ax1.bar([1, 2, 3], [1, 3, 5])

ax2.set_title('all negative')
ax2.bar([1, 2, 3], [-1, -3, -5])

ax3.set_title('mixed')
ax3.bar([1, 2, 3], [1, -3, 5])

ax4.set_title('bars with conflicting constraints')
ax4.bar([1, 2, 3], [1, 3, 5])
ax4.bar([1, 2, 3], [-1, -3, -5])

so

Cancel margin on the proper sides depending on if the bar is positive,
negative, or mixed.
@tacaswell tacaswell added this to the 2.0 (style change major release) milestone Nov 16, 2016
@story645
Copy link
Member

story645 commented Nov 16, 2016

Hmm, I think the self.use_*_margin should be an attribute of the class so that it persists, but I dunno
what logic to go with. If the default is set to False then maybe:

 if all(h =< 0 for h in height):
     self.use_bottom_margin = True
 elif all (h>=0 for h in height):
     self.use_top_margin = True
 else:
     self.use_bottom_margin = True
     self.use_top_margin = True

@tacaswell
Copy link
Member Author

The margin information is persisted through the ARtist.margin property (+ getter/setter) and *_margin property (+ getter/setter), I was just being very verbose with the local variables here so I could understand it while writing it 😉

@story645
Copy link
Member

OK, but my point is that if it persists and you use exclusionary logic, that should fix the multiple bars weirdness while still hold for the other cases. (because there should never actually be a false/false)

@tacaswell
Copy link
Member Author

I do not follow 😞 . I think the problem is that the bars don't know about each other so one asks for the bottom to be sans-margin and the other for the top to be sans-margin.

Still not sure what the best approach here is. One thought is to make the 'margin' values to me much more expressive (ex {'definitely no margin', 'prefer to not have a margin', 'meh', 'prefer to have a margin', 'definitely a margin'}) but then you end up with very complex resolution rules which seems like a less than great idea once I typed it out.

@anntzer suggested that we should only consider artists that are on the edge (ex, maybe by computing the bounding box for everything but one artist and find the artists the change the bbox size and ask them what to do about margins). That might be expensive (the artists probably need to do a better job at caching their data and (maybe) screen size (which would also help with hit-testing?) the Axes could also keep track of what artists push the bounding box out when it is update). Now that I type this out it seems like a pretty reasonable idea and a modification to what is currently there

Sorry for very rambly post.

@story645
Copy link
Member

Hmm, I think we're talking past each other a bit then. I'm thinking that since the margins are an axis property, that solves the boxes unaware problem. Like they start off being turned off (False) and then turned on as required by the graphs (the sample if statements I threw out). Essentially, bars can only turn on margins, they don't have the power to turn them off.

But @anntzer's solution might be cleaner.

@tacaswell
Copy link
Member Author

See the logic in https://github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/axes/_base.py#L2182 which allows artists to 'cancel' the default margins.

@anntzer
Copy link
Contributor

anntzer commented Nov 17, 2016

I think that the "conflicting case" that @tacaswell noted indicates that the margin cancellation system is clearly not designed to handle the case of multiple artists. I think I have a better idea. Let's drop the margins cancellation system, and instead let each artist maintain two lists of "sticky" values (one for the x axis and one for the y axis). For example, a vertical bar plot would register a y-sticky value at y=0; an image would register two x-sticky and two y-sticky values (at -0.5 and nx-0.5, and -0.5 and ny-0.5).

During auto-scaling, first compute the union of the bounding boxes, as is done right now. Then, if an edge of the union is at a "sticky" value, don't apply the margins to that edge.

Note that this allows handling bar plots (including multiple of them) without any special checking of positive vs negative values.

@tacaswell
Copy link
Member Author

@anntzer I like that idea (if that is what you suggested before, sorry for not understanding), it is much more elegant than what @mdboom and I came up with.

Have a slight concern about float equality, might have to do 'if sticky value is "close to" edge'

Can you take a crack at implementing it? All of the margins property / cancellation related stuff can be ripped out with no deprecation (it has only been released in betas).

@anntzer
Copy link
Contributor

anntzer commented Nov 17, 2016

No, that was not what I thought of before. I think I had proposed two other strategies before:

  • Keep the margins cancellation system, but only cancel a margins if it is cancelled by an artist that is on that edge (what you wrote when first describing my proposal). Note that you don't need to recompute the bbox union without the artist, you can just iterate over artists and check, for each edge where they want to cancel the margin, whether they are touching that edge).
    But this would still require special casing bar plots depending on the signs of the values within.
  • Completely drop the margins system, and "hack it" through by calling margins(0) within imshow() and bar() until we can come up with a better solution (note that this would require being able to specify the left and right margins separately, which is probably very easy to implement).
    Not so elegant, but probably the fastest solution.

I will try implementing my proposal.

@anntzer anntzer mentioned this pull request Nov 17, 2016
2 tasks
@anntzer
Copy link
Contributor

anntzer commented Nov 17, 2016

As a side note, setting margins["bottom"] = False for the "wiggle" and "weighted_wiggle" stackplots seem wrong regardless of the other issues raised here.

@efiring
Copy link
Member

efiring commented Nov 25, 2016

I'm going to close this on the assumption it is superseded by #7476, which will be merged eventually, I hope.

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.

5 participants