Set Matplotlib colorbar size to match graph

PythonImageMatplotlib

Python Problem Overview


I cannot get the colorbar on imshow graphs like this one to be the same height as the graph, short of using Photoshop after the fact. How do I get the heights to match?Example of the colorbar size mismatch

Python Solutions


Solution 1 - Python

This combination (and values near to these) seems to "magically" work for me to keep the colorbar scaled to the plot, no matter what size the display.

plt.colorbar(im,fraction=0.046, pad=0.04)

It also does not require sharing the axis which can get the plot out of square.

Solution 2 - Python

You can do this easily with a matplotlib AxisDivider.

The example from the linked page also works without using subplots:

import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
import numpy as np
    
plt.figure()
ax = plt.gca()
im = ax.imshow(np.arange(100).reshape((10,10)))
    
# create an axes on the right side of ax. The width of cax will be 5%
# of ax and the padding between cax and ax will be fixed at 0.05 inch.
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.05)
   
plt.colorbar(im, cax=cax)

enter image description here

Solution 3 - Python

I appreciate all the answers above. However, like some answers and comments pointed out, the axes_grid1 module cannot address GeoAxes, whereas adjusting fraction, pad, shrink, and other similar parameters cannot necessarily give the very precise order, which really bothers me. I believe that giving the colorbar its own axes might be a better solution to address all the issues that have been mentioned.

Code
import matplotlib.pyplot as plt
import numpy as np

fig=plt.figure()
ax = plt.axes()
im = ax.imshow(np.arange(100).reshape((10,10)))

# Create an axes for colorbar. The position of the axes is calculated based on the position of ax.
# You can change 0.01 to adjust the distance between the main image and the colorbar.
# You can change 0.02 to adjust the width of the colorbar.
# This practice is universal for both subplots and GeoAxes.

cax = fig.add_axes([ax.get_position().x1+0.01,ax.get_position().y0,0.02,ax.get_position().height])
plt.colorbar(im, cax=cax) # Similar to fig.colorbar(im, cax = cax)
Result

enter image description here

Later on, I find matplotlib.pyplot.colorbar official documentation also gives ax option, which are existing axes that will provide room for the colorbar. Therefore, it is useful for multiple subplots, see following.

Code
fig, ax = plt.subplots(2,1,figsize=(12,8)) # Caution, figsize will also influence positions.
im1 = ax[0].imshow(np.arange(100).reshape((10,10)), vmin = -100, vmax =100)
im2 = ax[1].imshow(np.arange(-100,0).reshape((10,10)), vmin = -100, vmax =100)
fig.colorbar(im1, ax=ax)
Result

enter image description here

Again, you can also achieve similar effects by specifying cax, a more accurate way from my perspective.

Code
fig, ax = plt.subplots(2,1,figsize=(12,8))
im1 = ax[0].imshow(np.arange(100).reshape((10,10)), vmin = -100, vmax =100)
im2 = ax[1].imshow(np.arange(-100,0).reshape((10,10)), vmin = -100, vmax =100)
cax = fig.add_axes([ax[1].get_position().x1-0.25,ax[1].get_position().y0,0.02,ax[0].get_position().y1-ax[1].get_position().y0])
fig.colorbar(im1, cax=cax)
Result

enter image description here

Solution 4 - Python

@bogatron already gave the answer suggested by the matplotlib docs, which produces the right height, but it introduces a different problem. Now the width of the colorbar (as well as the space between colorbar and plot) changes with the width of the plot. In other words, the aspect ratio of the colorbar is not fixed anymore.

To get both the right height and a given aspect ratio, you have to dig a bit deeper into the mysterious axes_grid1 module.

import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable, axes_size
import numpy as np

aspect = 20
pad_fraction = 0.5

ax = plt.gca()
im = ax.imshow(np.arange(200).reshape((20, 10)))
divider = make_axes_locatable(ax)
width = axes_size.AxesY(ax, aspect=1./aspect)
pad = axes_size.Fraction(pad_fraction, width)
cax = divider.append_axes("right", size=width, pad=pad)
plt.colorbar(im, cax=cax)

Note that this specifies the width of the colorbar w.r.t. the height of the plot (in contrast to the width of the figure, as it was before).

The spacing between colorbar and plot can now be specified as a fraction of the width of the colorbar, which is IMHO a much more meaningful number than a fraction of the figure width.

image plot with colorbar

UPDATE:

I created an IPython notebook on the topic, where I packed the above code into an easily re-usable function:

import matplotlib.pyplot as plt
from mpl_toolkits import axes_grid1

def add_colorbar(im, aspect=20, pad_fraction=0.5, **kwargs):
    """Add a vertical color bar to an image plot."""
    divider = axes_grid1.make_axes_locatable(im.axes)
    width = axes_grid1.axes_size.AxesY(im.axes, aspect=1./aspect)
    pad = axes_grid1.axes_size.Fraction(pad_fraction, width)
    current_ax = plt.gca()
    cax = divider.append_axes("right", size=width, pad=pad)
    plt.sca(current_ax)
    return im.axes.figure.colorbar(im, cax=cax, **kwargs)

It can be used like this:

im = plt.imshow(np.arange(200).reshape((20, 10)))
add_colorbar(im)

Solution 5 - Python

All the above solutions are good, but I like @Steve's and @bejota's the best as they do not involve fancy calls and are universal.

By universal I mean that works with any type of axes including GeoAxes. For example, it you have projected axes for mapping:

projection = cartopy.crs.UTM(zone='17N')
ax = plt.axes(projection=projection)
im = ax.imshow(np.arange(200).reshape((20, 10)))

a call to

cax = divider.append_axes("right", size=width, pad=pad)

will fail with: KeyException: map_projection

Therefore, the only universal way of dealing colorbar size with all types of axes is:

ax.colorbar(im, fraction=0.046, pad=0.04)

Work with fraction from 0.035 to 0.046 to get your best size. However, the values for the fraction and paddig will need to be adjusted to get the best fit for your plot and will differ depending if the orientation of the colorbar is in vertical position or horizontal.

Solution 6 - Python

When you create the colorbar try using the fraction and/or shrink parameters.

From the documents:

> fraction 0.15; fraction of original axes to use for colorbar

> shrink 1.0; fraction by which to shrink the colorbar

Solution 7 - Python

An alternative is

shrink=0.7, aspect=20*0.7

shrink scales the height and width, but the aspect argument restores the original width. Default aspect ratio is 20. The 0.7 is empirically determined.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionElliotView Question on Stackoverflow
Solution 1 - PythonskytakerView Answer on Stackoverflow
Solution 2 - PythonbogatronView Answer on Stackoverflow
Solution 3 - PythonFei YaoView Answer on Stackoverflow
Solution 4 - PythonMatthiasView Answer on Stackoverflow
Solution 5 - PythonBogdanView Answer on Stackoverflow
Solution 6 - PythonSteve BarnesView Answer on Stackoverflow
Solution 7 - PythonPeter CorkeView Answer on Stackoverflow