How to get the correct yticks of a colorbar in matplotlib without whitespace in the colorbar?
This is my code, note that the colors of the colorbar are misaligned if I apply .set_ticks() using the (formatted) values I got through get.ticks(), these values (as printed in the output) seem incorrect as the minimum shown is 15 but my minimum input value is 17.15116279.
import geopandas as gpd # version 0.11.0
import matplotlib.pyplot as plt # version 3.5.2
import matplotlib.colors as clr
from matplotlib import colorbar
from matplotlib.colors import Normalize # tbv colorbar
from matplotlib import cm
import matplotlib.ticker as mtick
cmap = clr.LinearSegmentedColormap.from_list('custom blue', ["#fce19c", "#c4ddee"], N=400)
world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
world = world[(world.pop_est>0) & (world.name!="Antarctica")]
vals = [22.36958444, 29.21348315, 30.74534161, 37.42331288, 20.,
19.31407942, 26.08695652, 26.36165577, 25.0, 17.79279279,
17.15116279, 19.60784314]
world = world[:len(vals)]
world['gdp_per_cap'] = vals
fig, ax = plt.subplots(1, 1)
ax = world.plot(column='gdp_per_cap', ax=ax, legend=False, cmap=cmapgeelblauw)
from mpl_toolkits.axes_grid1.axes_divider import make_axes_locatable
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.1)
vmin = world['gdp_per_cap'].min()
vmax = world['gdp_per_cap'].max()
norm = Normalize(vmin=vmin, vmax=vmax)
n_cmap = cm.ScalarMappable(norm=norm, cmap=cmap)
n_cmap.set_array([])
cbar = fig.colorbar(n_cmap, cax=cax)
print(cax==cbar.ax) # True
vals = cbar.ax.get_yticks()
print(vals)
cbar.ax.yaxis.set_ticks(vals)
cbar.ax.set_yticklabels(['{:,.0%}'.format(x/100) for x in vals])
plt.show()
Note that the colorbar remains correct is if
cbar.ax.yaxis.set_ticks(vals)
is not applied. But in that case I get the warning "UserWarning: FixedFormatter should only be used together with FixedLocator".
Also note: to avoid the issue I could apply a format this way:
cax_format = mtick.PercentFormatter(decimals=2)
cbar = fig.colorbar(n_cmap, cax=cax, format=cax_format)
And if I add the line
fig.draw_without_rendering()
# followed by vals = cbar.ax.get_yticks()
as suggested by Stef in the comments then the values are different (but still incorrect from my point of view) and the colorbar gets a 2nd white area due to this:
This is what is looks like if I do not set the ticks: This is what I am after but the warning made me set the ticks and realise that something may be wrong.
Based on the 2nd comment by Stef: "note that not necessarily all ticks are within the view limits, i.e. this first and last one may not actually be displayed. Manually setting ticks, on the other hand, expands the view limits to the ticks range given. If these are outside vmin / vmax it will cause the white gap you see."
Indeed, if I manually adjust the values as follows:
fig.draw_without_rendering()
vals = cbar.ax.get_yticks()
print(vals)
vals = [vmin] vals[1:-1].tolist() [vmax]
print(vals)
cbar.ax.yaxis.set_ticks(vals)
vals = ['{:,.0%}'.format(x/100) for x in vals]
vals = [''] vals[1:-1] ['']
print(vals)
cbar.ax.set_yticklabels(vals)
plt.show()
Then you get:
CodePudding user response:
By manually setting the ticks and tick labels, you create a fixed locator and a corresponding function formatter. Using a fixed locator is seldom the optimal solution due to the possible pitfalls outlined in the comments.
If you just want to add a %
sign and/or change the number of decimals, you can use a string formatter which is implicitely created when you pass a formatting string to