0

An image is worth a thousand words : https://www.harrisgeospatial.com/docs/html/images/colorbars.png

I want to obtain the same color bar than the one on the right with matplotlib. Default behavior use the same color for "upper"/"lower" and adjacent cell...

Thank you for your help!

Here is the code I have:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as colors

N = 100
X, Y = np.mgrid[-3:3:complex(0, N), -2:2:complex(0, N)]
Z1 = np.exp(-X**2 - Y**2)
Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
Z = (Z1 - Z2) * 2

fig, ax = plt.subplots(1, 1, figsize=(8, 8))

# even bounds gives a contour-like effect
bounds = np.linspace(-1, 1, 10)
norm = colors.BoundaryNorm(boundaries=bounds, ncolors=256)
pcm = ax.pcolormesh(X, Y, Z,
                    norm=norm,
                    cmap='RdBu_r')
fig.colorbar(pcm, ax=ax, extend='both', orientation='vertical')
A. Bot
  • 102
  • 12
  • 1
    Perhaps you can insert your code – Toady Jan 25 '19 at 00:21
  • Done! Thank you for your answer – A. Bot Jan 25 '19 at 01:07
  • 1
    For example in [this example](https://matplotlib.org/gallery/images_contours_and_fields/image_masked.html) there is a colorbar with upper and lower extensions. – ImportanceOfBeingErnest Jan 25 '19 at 01:16
  • Hello, thank you for your answer. Yep I know the extensions, I have included them in the piece of code I have published. Actually I want to have them drawn in different colors than the adjacent bins and in a color consistent with the base color map – A. Bot Jan 25 '19 at 01:27
  • Is that what you are looking for? `pcm = ax.pcolormesh(X, Y, Z, norm=norm, cmap='gnuplot', vmin=-1, vmax=1)`. The color maps are there: https://matplotlib.org/users/colormaps.html – Toady Jan 25 '19 at 11:58
  • I suppose the link to the example wasn't clear enough, hence see the answer below. – ImportanceOfBeingErnest Jan 25 '19 at 17:29

2 Answers2

1

In order to have the "over"/"under"-color of a colormap take the first/last color of that map but still be different from the last color inside the colormapped range you can get one more color from a colormap than you have boundaries in the BoundaryNorm and use the first and last color as the respective colors for the "over"/"under"-color.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors

N = 100
X, Y = np.mgrid[-3:3:complex(0, N), -2:2:complex(0, N)]
Z1 = np.exp(-X**2 - Y**2)
Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
Z = (Z1 - Z2) * 2

fig, ax = plt.subplots(1, 1, figsize=(8, 8))

# even bounds gives a contour-like effect
bounds = np.linspace(-1, 1, 11)
# get one more color than bounds from colormap
colors = plt.get_cmap('RdBu_r')(np.linspace(0,1,len(bounds)+1))
# create colormap without the outmost colors
cmap = mcolors.ListedColormap(colors[1:-1])
# set upper/lower color
cmap.set_over(colors[-1])
cmap.set_under(colors[0])
# create norm from bounds
norm = mcolors.BoundaryNorm(boundaries=bounds, ncolors=len(bounds)-1)
pcm = ax.pcolormesh(X, Y, Z, norm=norm, cmap=cmap)
fig.colorbar(pcm, ax=ax, extend='both', orientation='vertical')

plt.show()

enter image description here

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • This is exactly what I was looking for! Thank you! – A. Bot Jan 25 '19 at 20:05
  • I still do not understand why this is not default behaviour ... It is far nicer this way don't you agree? – A. Bot Jan 25 '19 at 20:14
  • By pure logic, the last value *of* a colormap cannot be outside of the colormap, because then it would not be the last value *of* the colormap, right? – ImportanceOfBeingErnest Jan 25 '19 at 20:26
  • By pure logic if you you ask for a display of upper and lower values (extend='both') you want to be actually able to distinguish upper and lower values from the adjacent ones! And by pure logic, the first and last values of the color maps should be used for upper and lower values. I maintain, even by pure logic this should be default behaviour ;) – A. Bot Jan 29 '19 at 02:14
  • If you ask for `extend='both'`, you will need to define which colors you you want to use. If you want to use the first and last color of the colormap for those, they necessarily cannot be the first and last color of the colormap anymore - hence you need to define a new colormap which does not contain those outside colors. Similar to a book, if you do not want the last page of a book to be part of a book, you need to remove it. As soon as you do that it is not the last page of the book any more. In any case I can assure you that what you ask will not ever be the default in matplotlib. – ImportanceOfBeingErnest Jan 29 '19 at 02:43
0

As suggested in my comment you can change the color map with

pcm = ax.pcolormesh(X, Y, Z, norm=norm, cmap='rainbow_r')

That gives:

enter image description here

You can define your own color map as shown here: Create own colormap using matplotlib and plot color scale

Toady
  • 357
  • 2
  • 13
  • The problem with this is that we can't distinguish between the values above and below -1 for example... that is what I wanted. ImportanceOfBeingErnest found a solution! – A. Bot Jan 25 '19 at 20:08