I have aligned a color bar to the x-axis of a horizontal box plot.
As you can see in the figure at the bottom, there is a blank area along the x-axis.
So I want to trim the x-axis together with the color bar, which was normalized by vmin
and vmax
Any idea how to accomplish that?
Thanks a lot!
test_data = [np.random.normal(mean, 1, 10) for mean in range(400, 500, 10)]
### set sns style
sns.set_style('white')
### set fig, ax
fig = plt.figure(figsize=(4,2))
ax1 = fig.add_axes([0.10,0.10,1.2,2])
### Define color bar
# choose color map
cmap = plt.get_cmap('nipy_spectral')
# normalize min and max
norm = mpl.colors.Normalize(vmin=380,vmax=780)
# assign cmap and norm
sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm)
sm.set_array([])
### Plot color bar (change "pad" to adjust distance of color bar to x-axis)
plt.colorbar(sm, ticks=np.linspace(380,780,11), location="bottom", pad=0)
### Plot boxplot
bplot = ax1.boxplot(test_data, vert=False, patch_artist=True)
### Set axis
ax1.set_yticklabels(["a","b","c","d","e","f","g","h","i","j"])
ax1.get_xaxis().set_visible(False)
ax1.set_xlim(380,780)
plt.show()
The easiest way to place the colorbar exactly aligned with the main ax, is to also create its ax as cax = fig.add_axes([...])
using the same x and width as the main ax and adapting the y and height. Then the colorbar can be created as plt.colorbar(sm, ticks=np.linspace(380, 780, 11), orientation='horizontal', cax=cax)
.
As you want to reduce part of the colorbar, it might be easier to draw it via imshow()
using an extent to align it with the x-axis.
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import matplotlib as mpl
test_data = [np.random.normal(mean, 1, 10) for mean in range(400, 500, 10)]
sns.set_style('white')
fig = plt.figure(figsize=(10, 3))
ax1 = fig.add_axes([0.10, 0.17, 0.88, 0.80]) # x0, y0, width, height in figure coordinates
cax = fig.add_axes([0.10, 0.12, 0.88, 0.05], sharex=ax1) # use same x0 and width
### Define color bar
cmap = plt.get_cmap('nipy_spectral')
norm = mpl.colors.Normalize(vmin=380, vmax=780)
xmin, xmax = 380, 500
cax.imshow(np.linspace(xmin, xmax, 100).reshape(1, -1), extent=[xmin, xmax, 0, 1], cmap=cmap, norm=norm, aspect='auto')
cax.set_yticks([])
bplot = ax1.boxplot(test_data, vert=False, patch_artist=True)
ax1.set_yticklabels(["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"])
ax1.set_xlim(xmin, xmax)
ax1.tick_params(axis='x', labelbottom=False)
cax.set_xticks(np.arange(xmin, xmax + 1, 20))
plt.show()
imshow()
can also be used to color the boxplot rectangles in a similar way. The example below uses adapted example data to better illustrate the concept. Note that imshow
resets the xlims and ylims, so they need to be saved first and reset afterwards.
test_data = [np.random.uniform(mean - 20, mean + 20, 10) for mean in range(400, 500, 10)]
sns.set_style('white')
fig = plt.figure(figsize=(10, 3))
ax1 = fig.add_axes([0.10, 0.17, 0.88, 0.80]) # x0, y0, width, height in figure coordinates
cax = fig.add_axes([0.10, 0.12, 0.88, 0.05], sharex=ax1) # use same x0 and width
### Define color bar
cmap = plt.get_cmap('nipy_spectral')
norm = mpl.colors.Normalize(vmin=380, vmax=780)
xmin, xmax = 380, 510
cax.imshow(np.linspace(xmin, xmax, 200).reshape(1, -1), extent=[xmin, xmax, 0, 1], cmap=cmap, norm=norm, aspect='auto')
cax.set_yticks([])
bplot = ax1.boxplot(test_data, vert=False, patch_artist=True, medianprops={'color': 'white'})
ax1.set_yticklabels(["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"])
ax1.tick_params(axis='x', labelbottom=False)
cax.set_xticks(np.arange(xmin, xmax + 1, 20))
ymin, ymax = ax1.get_ylim()
for art in ax1.artists:
art.set_facecolor('none') # make transparent
x0, y0, x1, y1 = art.get_path().get_extents().extents
ax1.imshow(np.linspace(x0, x1, 100).reshape(1, -1), extent=[x0, x1, y0, y1],
cmap=cmap, norm=norm, aspect='auto', zorder=0)
ax1.set_xlim(xmin, xmax)
ax1.set_ylim(ymin, ymax)