How to reduce the gap between a pcolormesh and a colorbar in matplotlib?
Question:
I have a dataset that I want to plot as 4 panels (each a pcolormesh with its associated colorbar). This is the code I’m using to do this, with some mocked up data
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import gridspec
xs = np.linspace(0.1, 0.2, 100)
ys = np.linspace(0, 2*np.pi*0.1, 400)
x_mesh, y_mesh = np.meshgrid(xs, ys)
# mocked up data arrays
A = np.full_like(x_mesh, 1.0)
B = np.full_like(x_mesh, 1.0)
C = np.full_like(x_mesh, 1.0)
D = np.full_like(x_mesh, 1.0)
fig = plt.figure()
gs = gridspec.GridSpec(nrows = 2, ncols = 4, height_ratios = (0.5, 0.5), width_ratios = (0.45, 0.05, 0.45, 0.05))
ax0 = fig.add_subplot(gs[0,0])
ax0_cbar = fig.add_subplot(gs[0,1])
ax1 = fig.add_subplot(gs[0,2])
ax1_cbar = fig.add_subplot(gs[0,3])
ax2 = fig.add_subplot(gs[1,0])
ax2_cbar = fig.add_subplot(gs[1,1])
ax3 = fig.add_subplot(gs[1,2])
ax3_cbar = fig.add_subplot(gs[1,3])
a = ax0.pcolormesh(x_mesh/1.0e-2, y_mesh/1.0e-2, A,
shading = 'auto')
cb1 = plt.colorbar(a, cax=ax0_cbar)
cb1.set_label(r"A")
b = ax1.pcolormesh(x_mesh/1.0e-2, y_mesh/1.0e-2, B,
shading = 'auto')
cb1 = plt.colorbar(b, cax=ax1_cbar)
cb1.set_label(r"B")
c = ax2.pcolormesh(x_mesh/1.0e-2, y_mesh/1.0e-2, C,
shading = 'auto')
cb1 = plt.colorbar(c, cax=ax2_cbar)
cb1.set_label(r"C")
d = ax3.pcolormesh(x_mesh/1.0e-2, y_mesh/1.0e-2, D,
shading = 'auto')
cb1 = plt.colorbar(d, cax=ax3_cbar)
cb1.set_label(r"D")
ax0.xaxis.set_ticklabels([])
ax1.xaxis.set_ticklabels([])
fig.tight_layout()
But when I actually do this, I find that there are really large gaps between the pcolormesh and the colorbars that are really unappealing (picture attached). How can I reduce these? I though I would be able to do it with fig.tight_layout()
and width_ratios
in gridspec
Answers:
You don’t require new axes elements for your colorbars, simply use the ax keyword argument to specify the colorbars for each subplot. The matplotlib documentation shows that using ax will produce a colorbar axis stolen from the parent axes ax (https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.colorbar.html). The documentation should be your first port of call, always!
Here I have written a working version of your code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import gridspec
xs = np.linspace(0.1, 0.2, 100)
ys = np.linspace(0, 2*np.pi*0.1, 400)
x_mesh, y_mesh = np.meshgrid(xs, ys)
# mocked up data arrays
A = np.full_like(x_mesh, 1.0)
B = np.full_like(x_mesh, 1.0)
C = np.full_like(x_mesh, 1.0)
D = np.full_like(x_mesh, 1.0)
fig = plt.figure(figsize=(6,4))
gs = gridspec.GridSpec(nrows = 2, ncols = 2, height_ratios = (0.5, 0.5), width_ratios = (0.5, 0.5))
ax0 = fig.add_subplot(gs[0,0])
ax1 = fig.add_subplot(gs[0,1])
ax2 = fig.add_subplot(gs[1,0])
ax3 = fig.add_subplot(gs[1,1])
a = ax0.pcolormesh(x_mesh/1.0e-2, y_mesh/1.0e-2, A,
shading = 'auto')
cb1 = plt.colorbar(a, ax=ax0)
cb1.set_label(r"A")
b = ax1.pcolormesh(x_mesh/1.0e-2, y_mesh/1.0e-2, B,
shading = 'auto')
cb1 = plt.colorbar(b, ax=ax1)
cb1.set_label(r"B")
c = ax2.pcolormesh(x_mesh/1.0e-2, y_mesh/1.0e-2, C,
shading = 'auto')
cb1 = plt.colorbar(c, ax=ax2)
cb1.set_label(r"C")
d = ax3.pcolormesh(x_mesh/1.0e-2, y_mesh/1.0e-2, D,
shading = 'auto')
cb1 = plt.colorbar(d, ax=ax3)
cb1.set_label(r"D")
ax0.xaxis.set_ticklabels([])
ax1.xaxis.set_ticklabels([])
fig.tight_layout()
plt.show()
Happy coding
The approach above is correct. It can break down if you have equal aspect axes, for which you can now use layout='compressed'
for simple cases to remove white space:
fig, axs = plt.subplots(2, 2, layout='compressed', figsize=(6, 3))
for ax in axs.flat:
pc = ax.pcolormesh(np.random.randn(10, 10))
ax.set_aspect(1)
fig.colorbar(pc, ax=ax)
plt.show()
See also: https://matplotlib.org/stable/gallery/subplots_axes_and_figures/colorbar_placement.html
I have a dataset that I want to plot as 4 panels (each a pcolormesh with its associated colorbar). This is the code I’m using to do this, with some mocked up data
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import gridspec
xs = np.linspace(0.1, 0.2, 100)
ys = np.linspace(0, 2*np.pi*0.1, 400)
x_mesh, y_mesh = np.meshgrid(xs, ys)
# mocked up data arrays
A = np.full_like(x_mesh, 1.0)
B = np.full_like(x_mesh, 1.0)
C = np.full_like(x_mesh, 1.0)
D = np.full_like(x_mesh, 1.0)
fig = plt.figure()
gs = gridspec.GridSpec(nrows = 2, ncols = 4, height_ratios = (0.5, 0.5), width_ratios = (0.45, 0.05, 0.45, 0.05))
ax0 = fig.add_subplot(gs[0,0])
ax0_cbar = fig.add_subplot(gs[0,1])
ax1 = fig.add_subplot(gs[0,2])
ax1_cbar = fig.add_subplot(gs[0,3])
ax2 = fig.add_subplot(gs[1,0])
ax2_cbar = fig.add_subplot(gs[1,1])
ax3 = fig.add_subplot(gs[1,2])
ax3_cbar = fig.add_subplot(gs[1,3])
a = ax0.pcolormesh(x_mesh/1.0e-2, y_mesh/1.0e-2, A,
shading = 'auto')
cb1 = plt.colorbar(a, cax=ax0_cbar)
cb1.set_label(r"A")
b = ax1.pcolormesh(x_mesh/1.0e-2, y_mesh/1.0e-2, B,
shading = 'auto')
cb1 = plt.colorbar(b, cax=ax1_cbar)
cb1.set_label(r"B")
c = ax2.pcolormesh(x_mesh/1.0e-2, y_mesh/1.0e-2, C,
shading = 'auto')
cb1 = plt.colorbar(c, cax=ax2_cbar)
cb1.set_label(r"C")
d = ax3.pcolormesh(x_mesh/1.0e-2, y_mesh/1.0e-2, D,
shading = 'auto')
cb1 = plt.colorbar(d, cax=ax3_cbar)
cb1.set_label(r"D")
ax0.xaxis.set_ticklabels([])
ax1.xaxis.set_ticklabels([])
fig.tight_layout()
But when I actually do this, I find that there are really large gaps between the pcolormesh and the colorbars that are really unappealing (picture attached). How can I reduce these? I though I would be able to do it with fig.tight_layout()
and width_ratios
in gridspec
You don’t require new axes elements for your colorbars, simply use the ax keyword argument to specify the colorbars for each subplot. The matplotlib documentation shows that using ax will produce a colorbar axis stolen from the parent axes ax (https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.colorbar.html). The documentation should be your first port of call, always!
Here I have written a working version of your code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import gridspec
xs = np.linspace(0.1, 0.2, 100)
ys = np.linspace(0, 2*np.pi*0.1, 400)
x_mesh, y_mesh = np.meshgrid(xs, ys)
# mocked up data arrays
A = np.full_like(x_mesh, 1.0)
B = np.full_like(x_mesh, 1.0)
C = np.full_like(x_mesh, 1.0)
D = np.full_like(x_mesh, 1.0)
fig = plt.figure(figsize=(6,4))
gs = gridspec.GridSpec(nrows = 2, ncols = 2, height_ratios = (0.5, 0.5), width_ratios = (0.5, 0.5))
ax0 = fig.add_subplot(gs[0,0])
ax1 = fig.add_subplot(gs[0,1])
ax2 = fig.add_subplot(gs[1,0])
ax3 = fig.add_subplot(gs[1,1])
a = ax0.pcolormesh(x_mesh/1.0e-2, y_mesh/1.0e-2, A,
shading = 'auto')
cb1 = plt.colorbar(a, ax=ax0)
cb1.set_label(r"A")
b = ax1.pcolormesh(x_mesh/1.0e-2, y_mesh/1.0e-2, B,
shading = 'auto')
cb1 = plt.colorbar(b, ax=ax1)
cb1.set_label(r"B")
c = ax2.pcolormesh(x_mesh/1.0e-2, y_mesh/1.0e-2, C,
shading = 'auto')
cb1 = plt.colorbar(c, ax=ax2)
cb1.set_label(r"C")
d = ax3.pcolormesh(x_mesh/1.0e-2, y_mesh/1.0e-2, D,
shading = 'auto')
cb1 = plt.colorbar(d, ax=ax3)
cb1.set_label(r"D")
ax0.xaxis.set_ticklabels([])
ax1.xaxis.set_ticklabels([])
fig.tight_layout()
plt.show()
Happy coding
The approach above is correct. It can break down if you have equal aspect axes, for which you can now use layout='compressed'
for simple cases to remove white space:
fig, axs = plt.subplots(2, 2, layout='compressed', figsize=(6, 3))
for ax in axs.flat:
pc = ax.pcolormesh(np.random.randn(10, 10))
ax.set_aspect(1)
fig.colorbar(pc, ax=ax)
plt.show()
See also: https://matplotlib.org/stable/gallery/subplots_axes_and_figures/colorbar_placement.html