Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork7.9k
Open
Labels
Description
Documentation Link
https://matplotlib.org/stable/gallery/images_contours_and_fields/pcolormesh_levels.html
Problem
[The following can possibly also apply to pcolor or similar graphics ]
pcolormesh
is very useful when you need tolook precisely at the values of a 2D data field (rather than usingcontour
andcontourf
and wondering how the contours are computed):
- If you want to pinpoint thelocations of specific values, you need to use only a few specific colors, using
ListedColormap
. - And if you want to look at thelocations of only one value (e.g. the location of a data mask), you need to plot only the values inone interval andcompletely ignore the other values
Thepcolormesh example page unfortunately does not cover the use cases mentioned above:
- no example using
ListedColormap
- no mention that thedata will be plotted even if it lies outside the requested levels, using the first and last specified colors! This is so misleading that it's almost more a bug than a feature! This is probably obvious for the matplotlib colormaps and norm gurus, but not for most people. I end up having to read again all the color documentation each time I come back to matplotlib
- no mention that you have to explicitlyspecify a transparent (
alpha=0
) color usingset_bad
,set_over
andset_under
forremoving what should not be plotted. Maybe there is a cleaner (more obvious) way to do that. I will be glad to learn that (from an improved documentation) - no mention that a workaround is to use
numpy.ma
tomask the values that should not be plotted. But this may be tricky for people who are not used to dealing with cleanly masked/missing values
Suggested improvement
The following code and output image, based on theMaking levels using Norms example tentatively shows what is missing (in my opinion) in the documentation, as described above
It can hopefully be used to improve/extend the documentation
#!/usr/bin/env python# pcolormesh test/example adapted from# https://matplotlib.org/stable/gallery/images_contours_and_fields/pcolormesh_levels.html#making-levels-using-normsimport matplotlib.pyplot as pltfrom matplotlib.colors import ListedColormap, BoundaryNormfrom matplotlib.ticker import MaxNLocatorimport numpy as np# make these smaller to increase the resolutiondx, dy = 0.05, 0.05# generate 2 2d grids for the x & y boundsy, x = np.mgrid[slice(1, 5 + dy, dy), slice(1, 5 + dx, dx)]z = np.sin(x)**10 + np.cos(10 + y*x) * np.cos(x)# x and y are bounds, so z should be the value *inside* those bounds.# Therefore, remove the last value from the z array.z = z[:-1, :-1]# pick the desired colormap, sensible levels, and define a normalization# instance which takes data values and translates those into levels.cmap_0 = plt.get_cmap('PiYG')levels_0 = MaxNLocator(nbins=15).tick_values(z.min(), z.max())norm_0 = BoundaryNorm(levels_0, ncolors=cmap_0.N, clip=True)fig, (ax_0, ax_1, ax_2, ax_3, ax_4, ax_5) = plt.subplots(nrows=6)fig.set_size_inches(5, 12)im_0 = ax_0.pcolormesh(x, y, z, cmap=cmap_0, norm=norm_0)fig.colorbar(im_0, ax=ax_0)ax_0.set_title('pcolormesh with levels')ax_1.set_title('3 intervals/colors covering the\nfull data range ([%.3f, %.3f])' % (z.min(), z.max()))cmap_1 = ListedColormap(['pink', 'lavender', 'green'])levels_1 = [-1.1, -0.3, 0.3, 1.1]norm_1 = BoundaryNorm(levels_1, cmap_1.N)im_1 = ax_1.pcolormesh(x, y, z, cmap=cmap_1, norm=norm_1, zorder=20)fig.colorbar(im_1, ax=ax_1)ax_2.set_title('Top of data range is above the color scale\n...but set_over color is NOT specified')cmap_2 = ListedColormap(['pink', 'lavender'])levels_2 = [-1.1, -0.3, 0.3]norm_2 = BoundaryNorm(levels_2, cmap_2.N)im_2 = ax_2.pcolormesh(x, y, z, cmap=cmap_2, norm=norm_2, zorder=20)fig.colorbar(im_2, ax=ax_2)ax_3.set_title('BLUE color specified for\ndata ABOVE the color scale')cmap_3 = ListedColormap(['pink', 'lavender'])cmap_3.set_over('blue')levels_3 = [-1.1, -0.3, 0.3]norm_3 = BoundaryNorm(levels_3, cmap_3.N)im_3 = ax_3.pcolormesh(x, y, z, cmap=cmap_3, norm=norm_3, zorder=20)text_3 = ax_3.text(0.05, 0.3, 'Test text\nin the BACKGROUND', fontsize='xx-large', horizontalalignment='left', verticalalignment='center', transform=ax_3.transAxes, zorder=10)fig.colorbar(im_3, ax=ax_3)ax_4.set_title('Color above the scale is now\nfully TRANSPARENT (alpha=0)')cmap_4 = ListedColormap(['pink', 'lavender'])cmap_4.set_over('black', alpha=0)levels_4 = [-1.1, -0.3, 0.3]norm_4 = BoundaryNorm(levels_4, cmap_4.N)im_4 = ax_4.pcolormesh(x, y, z, cmap=cmap_4, norm=norm_4, zorder=20)text_4 = ax_4.text(0.05, 0.3, 'Test text\nin the BACKGROUND', fontsize='xx-large', horizontalalignment='left', verticalalignment='center', transform=ax_4.transAxes, zorder=10)fig.colorbar(im_4, ax=ax_4)ax_5.set_title('Color above the scale is NOT TRANSPARENT\n...but the data above 0. is MASKED')# MASK the values we do not want, instead of plotting them with a# fully transparent colorzm = np.ma.masked_greater(z, 0.)cmap_5 = ListedColormap(['pink', 'lavender'])# For testing purpose, we explicitly specify a fully opaque color# (but opaque is the default anyway)cmap_5.set_over('black', alpha=1)levels_5 = [-1.1, -0.3, 0.3]norm_5 = BoundaryNorm(levels_5, cmap_5.N)im_5 = ax_5.pcolormesh(x, y, zm, cmap=cmap_5, norm=norm_5, zorder=20)text_5 = ax_5.text(0.05, 0.3, 'Test text\nin the BACKGROUND', fontsize='xx-large', horizontalalignment='left', verticalalignment='center', transform=ax_5.transAxes, zorder=10)fig.colorbar(im_5, ax=ax_5)# adjust spacing between subplots so `ax1` title and `ax0` tick labels# don't overlapfig.tight_layout()fig.savefig('test_pcolormesh.png')plt.show()
Matplotlib Version
3.3.4
Matplotlib documentation version
3.4.3