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

Appearance settings

Colorbar grid position #18575

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

Merged
merged 18 commits into from
Oct 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion 3 examples/color/colorbar_basics.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@
fig.colorbar(pos, ax=ax1)

# repeat everything above for the negative data
# you can specify location, anchor and shrink the colorbar
neg = ax2.imshow(Zneg, cmap='Reds_r', interpolation='none')
fig.colorbar(neg, ax=ax2)
fig.colorbar(neg, ax=ax2, location='right', anchor=(0, 0.3), shrink=0.7)

# Plot both positive and negative values between +/- 1.2
pos_neg_clipped = ax3.imshow(Z, cmap='RdBu', vmin=-1.2, vmax=1.2,
Expand Down
70 changes: 43 additions & 27 deletions 70 lib/matplotlib/colorbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -1509,35 +1509,51 @@ def make_axes_gridspec(parent, *, location=None, orientation=None,
kw['orientation'] = loc_settings['orientation']
location = kw['ticklocation'] = loc_settings['location']

pad = loc_settings["pad"]
anchor = kw.pop('anchor', loc_settings['anchor'])
panchor = kw.pop('panchor', loc_settings['panchor'])
pad = kw.pop('pad', loc_settings["pad"])
wh_space = 2 * pad / (1 - pad)

# for shrinking
pad_s = (1 - shrink) * 0.5
wh_ratios = [pad_s, shrink, pad_s]

if location == "left":
gs = parent.get_subplotspec().subgridspec(
1, 2, wspace=wh_space, width_ratios=[fraction, 1-fraction-pad])
ss_main = gs[1]
ss_cb = gs[0].subgridspec(3, 1, hspace=0, height_ratios=wh_ratios)[1]
elif location == "right":
gs = parent.get_subplotspec().subgridspec(
1, 2, wspace=wh_space, width_ratios=[1-fraction-pad, fraction])
ss_main = gs[0]
ss_cb = gs[1].subgridspec(3, 1, hspace=0, height_ratios=wh_ratios)[1]
elif location == "top":
gs = parent.get_subplotspec().subgridspec(
2, 1, hspace=wh_space, height_ratios=[fraction, 1-fraction-pad])
ss_main = gs[1]
ss_cb = gs[0].subgridspec(1, 3, wspace=0, width_ratios=wh_ratios)[1]
aspect = 1 / aspect
else: # "bottom"
gs = parent.get_subplotspec().subgridspec(
2, 1, hspace=wh_space, height_ratios=[1-fraction-pad, fraction])
ss_main = gs[0]
ss_cb = gs[1].subgridspec(1, 3, wspace=0, width_ratios=wh_ratios)[1]
aspect = 1 / aspect
if location in ('left', 'right'):
# for shrinking
height_ratios = [
(1-anchor[1])*(1-shrink), shrink, anchor[1]*(1-shrink)]

if location == 'left':
gs = parent.get_subplotspec().subgridspec(
1, 2, wspace=wh_space,
width_ratios=[fraction, 1-fraction-pad])
ss_main = gs[1]
ss_cb = gs[0].subgridspec(
3, 1, hspace=0, height_ratios=height_ratios)[1]
else:
gs = parent.get_subplotspec().subgridspec(
1, 2, wspace=wh_space,
width_ratios=[1-fraction-pad, fraction])
ss_main = gs[0]
ss_cb = gs[1].subgridspec(
3, 1, hspace=0, height_ratios=height_ratios)[1]
else:
# for shrinking
width_ratios = [
anchor[0]*(1-shrink), shrink, (1-anchor[0])*(1-shrink)]

if location == 'bottom':
gs = parent.get_subplotspec().subgridspec(
2, 1, hspace=wh_space,
height_ratios=[1-fraction-pad, fraction])
ss_main = gs[0]
ss_cb = gs[1].subgridspec(
1, 3, wspace=0, width_ratios=width_ratios)[1]
aspect = 1 / aspect
else:
gs = parent.get_subplotspec().subgridspec(
2, 1, hspace=wh_space,
height_ratios=[fraction, 1-fraction-pad])
ss_main = gs[1]
ss_cb = gs[0].subgridspec(
1, 3, wspace=0, width_ratios=width_ratios)[1]
aspect = 1 / aspect

parent.set_subplotspec(ss_main)
parent.set_anchor(loc_settings["panchor"])
Expand Down
82 changes: 82 additions & 0 deletions 82 lib/matplotlib/tests/test_colorbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -622,3 +622,85 @@ def test_colorbar_int(clim):
im = ax.imshow([[*map(np.int16, clim)]])
fig.colorbar(im)
assert (im.norm.vmin, im.norm.vmax) == clim


def test_anchored_cbar_position_using_specgrid():
data = np.arange(1200).reshape(30, 40)
levels = [0, 200, 400, 600, 800, 1000, 1200]
shrink = 0.5
anchor_y = 0.3
# right
fig, ax = plt.subplots()
cs = ax.contourf(data, levels=levels)
cbar = plt.colorbar(
cs, ax=ax, use_gridspec=True,
location='right', anchor=(1, anchor_y), shrink=shrink)

# the bottom left corner of one ax is (x0, y0)
# the top right corner of one ax is (x1, y1)
# p0: the vertical / horizontal position of anchor
x0, y0, x1, y1 = ax.get_position().extents
cx0, cy0, cx1, cy1 = cbar.ax.get_position().extents
p0 = (y1 - y0) * anchor_y + y0

np.testing.assert_allclose(
[cy1, cy0],
[y1 * shrink + (1 - shrink) * p0, p0 * (1 - shrink) + y0 * shrink])
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure I understand why the anchor point p0 should be affected by shrink.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

p0 is not affected by shrink, cy1 is the top of colorbar axes, which can be calculated using p0 and shrink
cy1 = y1 * shrink + (1 - shrink) * p0
while p0 and shrink do not affect each other

Copy link
Member

Choose a reason for hiding this comment

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

I'm not saying they affect each other; I'm saying that shrink has an effect on p0 for cy0/cy1, and I don't know why. If p0 is an anchor, then it should not get shrunk.

Copy link
Member

Choose a reason for hiding this comment

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

I looked at this again, and anchor is the anchor point of the colorbar, while I was thinking it was the anchor on the parent Axes, but that's defined by panchor. So this is why I was confused by this calculation.

However, that means that p0 doesn't seem to be calculated right, as it is calculating an anchor position in the parent Axes space (using y0/y1), but using anchor_y, which was used for the colorbar anchor position.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In the test function, I will take location=right as an example

demo

the x0 y0 x1 y1 for the plot axes is [0.125, 0.11, 0.745, 0.88] (rounded to 2 digits) , blue region
the cx0 cy0 cx1 cy1 for cbar is [0.78, 0.22, 0.80, 0.61] , orange region

the p0 is the absolute y postion (red dot) of colorbar anchor against the plot, while anchor-y is a relative colorbar postion against the parent axes,
thus p0-y0 = (y1-y0) * anchor_y.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I assumed the y value for colorbar anchor is the relative postion against the plot, not the absolute y position. Correct me if I am wrong.

Copy link
Member

Choose a reason for hiding this comment

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

@ShawnChen1996 I think what would be convincing would be an example that shows clearly this version does close to the same as the version with use_gridspec=False, just to make sure the semantics are the same for anchor and panchor. For completeness it would be good if the were tested at more than one anchor along all four sides.

I know below we suggested not making an image test, but it would be helpful to see what the image looks like, even if it doesn't make it into the test. I guess in situations like this, I actually lean towards adding an image test since its hard to tell by looking at the numbers if things are working. But at least for review, it would be super helpful to see the image.

If the kwargs are being used the same as the old meaning of the kwargs, then we still have a documentation issue, but that shouldn't block this PR.


# left
fig, ax = plt.subplots()
cs = ax.contourf(data, levels=levels)
cbar = plt.colorbar(
cs, ax=ax, use_gridspec=True,
location='left', anchor=(1, anchor_y), shrink=shrink)

# the bottom left corner of one ax is (x0, y0)
# the top right corner of one ax is (x1, y1)
# p0: the vertical / horizontal position of anchor
x0, y0, x1, y1 = ax.get_position().extents
cx0, cy0, cx1, cy1 = cbar.ax.get_position().extents
p0 = (y1 - y0) * anchor_y + y0

np.testing.assert_allclose(
[cy1, cy0],
[y1 * shrink + (1 - shrink) * p0, p0 * (1 - shrink) + y0 * shrink])

# top
shawnchenx6 marked this conversation as resolved.
Show resolved Hide resolved
shrink = 0.5
anchor_x = 0.3
fig, ax = plt.subplots()
cs = ax.contourf(data, levels=levels)
cbar = plt.colorbar(
cs, ax=ax, use_gridspec=True,
location='top', anchor=(anchor_x, 1), shrink=shrink)

# the bottom left corner of one ax is (x0, y0)
# the top right corner of one ax is (x1, y1)
# p0: the vertical / horizontal position of anchor
x0, y0, x1, y1 = ax.get_position().extents
cx0, cy0, cx1, cy1 = cbar.ax.get_position().extents
p0 = (x1 - x0) * anchor_x + x0

np.testing.assert_allclose(
[cx1, cx0],
[x1 * shrink + (1 - shrink) * p0, p0 * (1 - shrink) + x0 * shrink])

# bottom
shrink = 0.5
anchor_x = 0.3
fig, ax = plt.subplots()
cs = ax.contourf(data, levels=levels)
cbar = plt.colorbar(
cs, ax=ax, use_gridspec=True,
location='bottom', anchor=(anchor_x, 1), shrink=shrink)

# the bottom left corner of one ax is (x0, y0)
# the top right corner of one ax is (x1, y1)
# p0: the vertical / horizontal position of anchor
x0, y0, x1, y1 = ax.get_position().extents
cx0, cy0, cx1, cy1 = cbar.ax.get_position().extents
p0 = (x1 - x0) * anchor_x + x0

np.testing.assert_allclose(
[cx1, cx0],
[x1 * shrink + (1 - shrink) * p0, p0 * (1 - shrink) + x0 * shrink])
Morty Proxy This is a proxified and sanitized view of the page, visit original site.