Use of extend in a pcolormesh plot with discrete c

2019-05-15 16:42发布

问题:

I'm trying to create a pcolormesh plot with a discrete colorbar. The output should fulfil these criteria:

  1. The first level should be white
  2. The data should be cut off at some level
  3. Data above the cut off should have a separate colour (namely the last colour of the colormap)

I am almost there but the 'extend' keyword does not behave the way I would expect it to (the colour in the "max-arrow" is the same as for the last level - see example). How do I set values above 'vmax' to a separate colour (i.e., the last colour of whatever colormap I use)

import numpy as np
import xarray as xr
import matplotlib as mpl
import matplotlib.pyplot as plt

ds = xr.Dataset(
    coords={'lon': np.arange(-180, 180, 10),
            'lat': np.arange(-85, 90, 10)},
    data_vars={'data': (('lat', 'lon'), np.random.rand(18, 36))})

cmap = plt.cm.get_cmap('Reds')
cmap.set_under('w')
# cmap.set_over()  # do something here?
levels = np.arange(0, .7, .1)
ds.data.plot.pcolormesh(
    cmap=cmap,
    vmin=levels[1],
    # vmax=levels[-1],
    extend='max',
    norm = mpl.colors.BoundaryNorm(levels, ncolors=cmap.N, clip=False)
)

I'm using xarray but the behaviour is the same for plt.pcolormesh:

p = plt.pcolormesh(
    np.arange(-180, 180, 10),
    np.arange(-85, 90, 10),
    np.random.rand(18, 36),
    cmap=cmap,
    vmin=levels[1],
    # vmax=levels[-1],
    norm = mpl.colors.BoundaryNorm(levels, ncolors=cmap.N, clip=False)
)
plt.colorbar(p, extend='max') 

回答1:

Indeed, if you set cmap.set_over("blue") you would see blue as the color of the values exceeding the maximum value.

However, if you want to use the last color of the colormap as that color for set_over you need to make a colormap, which stops at the second last color. To that end, the following rationale may be used. If we aim at 6 different colors from a colormap plus the color for overshooting values, we take 7 colors from that colormap, replace the first with white color and use the first 6 colors as the colors for the boundary interval. The last colors is then used as the color for overshooting values.

import numpy as np; np.random.seed(1)
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.colors

lon,lat = np.meshgrid(np.arange(-180, 180, 10), np.arange(-85, 90, 10))
data = np.sort(np.random.rand(18, 36),axis=1)

# create 7 boundaries between 0 and 0.6, to have 6 intervals
boundaries = np.arange(0, .7, .1)
# create list of 7(!) colors from colormap
cmap_reds = plt.cm.get_cmap('Reds',len(boundaries))
colors = list(cmap_reds(np.arange(len(boundaries))))
#replace first color with white
colors[0] = "white"
cmap = matplotlib.colors.ListedColormap(colors[:-1], "")
# set over-color to last color of list 
cmap.set_over(colors[-1])

cm = plt.pcolormesh(lon,lat,data,
    cmap=cmap,
    norm = mpl.colors.BoundaryNorm(boundaries, ncolors=len(boundaries)-1, clip=False)
)
plt.colorbar(cm, extend="max")
plt.show()