|
| 1 | +""" |
| 2 | +******************************** |
| 3 | +Creating Colormaps in Matplotlib |
| 4 | +******************************** |
| 5 | +
|
| 6 | +Creating and manipulating colormaps in Matplotlib is straight-forward |
| 7 | +using the class `.ListedColormap` and a Nx4 numpy array of values |
| 8 | +between 0 and 1 to represent the RGBA values of the colormap. There |
| 9 | +is also a `.LinearSegmentedColormap` class that allows colormaps to be |
| 10 | +specified with far fewer anchor points defining segments, and linearly |
| 11 | +interpolating between the anchor points. |
| 12 | +
|
| 13 | +Getting colormaps and accessing their values |
| 14 | +============================================ |
| 15 | +
|
| 16 | +First, getting a named colormap, most of which are listed in |
| 17 | +:doc:`/tutorials/colors/colormaps` requires the use of |
| 18 | +`.matplotlib.cm.get_cmap`, which returns a |
| 19 | +:class:`.matplotlib.colors.ListedColormap` object. The second argument gives |
| 20 | +the size of the list of colors used to define the colormap, and below we |
| 21 | +use a modest value of 12 so there are not a lot of values to look at. |
| 22 | +""" |
| 23 | + |
| 24 | +import numpy as np |
| 25 | +import matplotlib as mpl |
| 26 | +import matplotlib.pyplot as plt |
| 27 | +from matplotlib import cm |
| 28 | +from matplotlib.colors import ListedColormap, LinearSegmentedColormap |
| 29 | +from collections import OrderedDict |
| 30 | + |
| 31 | +viridis = cm.get_cmap('viridis', 12) |
| 32 | +print(viridis) |
| 33 | + |
| 34 | +############################################################################## |
| 35 | +# The object ``viridis`` is a callable, that when passed a float between |
| 36 | +# 0 and 1 returns an RGBA value from the colormap: |
| 37 | + |
| 38 | +print(viridis(0.56)) |
| 39 | + |
| 40 | +############################################################################## |
| 41 | +# The list of colors that comprise the colormap can be directly accessed using |
| 42 | +# the ``colors`` property, |
| 43 | +# or it can be acccessed indirectly by calling ``viridis`` with an array |
| 44 | +# of values matching the length of the colormap. Note that the returned list |
| 45 | +# is in the form of an RGBA Nx4 array, where N is the length of the colormap. |
| 46 | + |
| 47 | +print('viridis.colors', viridis.colors) |
| 48 | +print('viridis(range(12))', viridis(range(12))) |
| 49 | +print('viridis(np.linspace(0, 1, 12))', viridis(np.linspace(0, 1, 12))) |
| 50 | + |
| 51 | +############################################################################## |
| 52 | +# The colormap is a lookup table, so "oversampling" the colormap returns |
| 53 | +# nearest-neighbor interpolation (note the repeated colors in the list below) |
| 54 | + |
| 55 | +print('viridis(np.linspace(0, 1, 15))', viridis(np.linspace(0, 1, 15))) |
| 56 | + |
| 57 | +############################################################################## |
| 58 | +# Creating listed colormaps |
| 59 | +# ========================= |
| 60 | +# |
| 61 | +# This is essential the inverse operation of the above where we supply a |
| 62 | +# Nx4 numpy array with all values between 0 and 1, |
| 63 | +# to `.ListedColormap` to make a new colormap. This means that |
| 64 | +# any numpy operations that we can do on a Nx4 array make carpentry of |
| 65 | +# new colormaps from existing colormaps quite straight forward. |
| 66 | +# |
| 67 | +# Suppose we want to make the first 25 entries of a 256-length "viridis" |
| 68 | +# colormap pink for some reason: |
| 69 | + |
| 70 | +viridis = cm.get_cmap('viridis', 256) |
| 71 | +newcolors = viridis(np.linspace(0, 1, 256)) |
| 72 | +pink = np.array([248/256, 24/256, 148/256, 1]) |
| 73 | +newcolors[:25, :] = pink |
| 74 | +newcmp = ListedColormap(newcolors) |
| 75 | + |
| 76 | + |
| 77 | +def plot_examples(cms): |
| 78 | + """ |
| 79 | + helper function to plot two colormaps |
| 80 | + """ |
| 81 | + np.random.seed(19680801) |
| 82 | + data = np.random.randn(30, 30) |
| 83 | + |
| 84 | + fig, axs = plt.subplots(1, 2, figsize=(6, 3), constrained_layout=True) |
| 85 | + for [ax, cmap] in zip(axs, cms): |
| 86 | + psm = ax.pcolormesh(data, cmap=cmap, rasterized=True, vmin=-4, vmax=4) |
| 87 | + fig.colorbar(psm, ax=ax) |
| 88 | + plt.show() |
| 89 | + |
| 90 | +plot_examples([viridis, newcmp]) |
| 91 | + |
| 92 | +############################################################################## |
| 93 | +# We can easily reduce the dynamic range of a colormap; here we choose the |
| 94 | +# middle 0.5 of the colormap. However, we need to interpolate from a larger |
| 95 | +# colormap, otherwise the new colormap will have repeated values. |
| 96 | + |
| 97 | +viridisBig = cm.get_cmap('viridis', 512) |
| 98 | +newcmp = ListedColormap(viridisBig(np.linspace(0.25, 0.75, 256))) |
| 99 | +plot_examples([viridis, newcmp]) |
| 100 | + |
| 101 | +############################################################################## |
| 102 | +# and we can easily concatenate two colormaps: |
| 103 | + |
| 104 | +top = cm.get_cmap('Oranges_r', 128) |
| 105 | +bottom = cm.get_cmap('Blues', 128) |
| 106 | + |
| 107 | +newcolors = np.vstack((top(np.linspace(0, 1, 128)), |
| 108 | + bottom(np.linspace(0, 1, 128)))) |
| 109 | +newcmp = ListedColormap(newcolors, name='OrangeBlue') |
| 110 | +plot_examples([viridis, newcmp]) |
| 111 | + |
| 112 | +############################################################################## |
| 113 | +# Of course we need not start from a named colormap, we just need to create |
| 114 | +# the Nx4 array to pass to `.ListedColormap`. Here we create a |
| 115 | +# brown colormap that goes to white.... |
| 116 | + |
| 117 | +N = 256 |
| 118 | +vals = np.ones((N, 4)) |
| 119 | +vals[:, 0] = np.linspace(90/256, 1, N) |
| 120 | +vals[:, 1] = np.linspace(39/256, 1, N) |
| 121 | +vals[:, 2] = np.linspace(41/256, 1, N) |
| 122 | +newcmp = ListedColormap(vals) |
| 123 | +plot_examples([viridis, newcmp]) |
| 124 | + |
| 125 | +############################################################################## |
| 126 | +# Creating linear segmented colormaps |
| 127 | +# =================================== |
| 128 | +# |
| 129 | +# `.LinearSegmentedColormap` class specifies colormaps using anchor points |
| 130 | +# between which RGB(A) values are interpolated. |
| 131 | +# |
| 132 | +# The format to specify these colormaps allows discontinuities at the anchor |
| 133 | +# points. Each anchor point is specified as a row in a matrix of the |
| 134 | +# form ``[x[i] yleft[i] yright[i]]``, where ``x[i]`` is the anchor, and |
| 135 | +# ``yleft[i]`` and ``yright[i]`` are the values of the color on either |
| 136 | +# side of the anchor point. |
| 137 | +# |
| 138 | +# If there are no discontinuities, then ``yleft[i]=yright[i]``: |
| 139 | + |
| 140 | +cdict = {'red': [[0.0, 0.0, 0.0], |
| 141 | + [0.5, 1.0, 1.0], |
| 142 | + [1.0, 1.0, 1.0]], |
| 143 | + 'green': [[0.0, 0.0, 0.0], |
| 144 | + [0.25, 0.0, 0.0], |
| 145 | + [0.75, 1.0, 1.0], |
| 146 | + [1.0, 1.0, 1.0]], |
| 147 | + 'blue': [[0.0, 0.0, 0.0], |
| 148 | + [0.5, 0.0, 0.0], |
| 149 | + [1.0, 1.0, 1.0]]} |
| 150 | + |
| 151 | + |
| 152 | +def plot_linearmap(cdict): |
| 153 | + newcmp = LinearSegmentedColormap('testCmap', segmentdata=cdict, N=256) |
| 154 | + rgba = newcmp(np.linspace(0, 1, 256)) |
| 155 | + fig, ax = plt.subplots(figsize=(4, 3), constrained_layout=True) |
| 156 | + col = ['r', 'g', 'b'] |
| 157 | + for xx in [0.25, 0.5, 0.75]: |
| 158 | + ax.axvline(xx, color='0.7', linestyle='--') |
| 159 | + for i in range(3): |
| 160 | + ax.plot(np.arange(256)/256, rgba[:, i], color=col[i]) |
| 161 | + ax.set_xlabel('index') |
| 162 | + ax.set_ylabel('RGB') |
| 163 | + plt.show() |
| 164 | + |
| 165 | +plot_linearmap(cdict) |
| 166 | + |
| 167 | +############################################################################# |
| 168 | +# In order to make a discontinuity at an anchor point, the third column is |
| 169 | +# different than the second. The matrix for each of "red", "green", "blue", |
| 170 | +# and optionally "alpha" is set up as:: |
| 171 | +# |
| 172 | +# cdict['red'] = [... |
| 173 | +# [x[i] yleft[i] yright[i]], |
| 174 | +# [x[i+1] yleft[i+1] yright[i+1]], |
| 175 | +# ...] |
| 176 | +# |
| 177 | +# and for values passed to the colormap between ``x[i]`` and ``x[i+1]``, |
| 178 | +# the interpolation is between ``yright[i]`` and ``yleft[i+1]``. |
| 179 | +# |
| 180 | +# In the example below there is a discontiuity in red at 0.5. The |
| 181 | +# interpolation between 0 and 0.5 goes from 0.3 to 1, and between 0.5 and 1 |
| 182 | +# it goes from 0.9 to 1. Note that red[0, 1], and red[2, 2] are both |
| 183 | +# superfluous to the interpolation because red[0, 1] is the value to the |
| 184 | +# left of 0, and red[2, 2] is the value to the right of 1.0. |
| 185 | + |
| 186 | +cdict['red'] = [[0.0, 0.0, 0.3], |
| 187 | + [0.5, 1.0, 0.9], |
| 188 | + [1.0, 1.0, 1.0]] |
| 189 | +plot_linearmap(cdict) |
| 190 | + |
| 191 | + |
| 192 | +############################################################################# |
| 193 | +# |
| 194 | +# ------------ |
| 195 | +# |
| 196 | +# References |
| 197 | +# """""""""" |
| 198 | +# |
| 199 | +# The use of the following functions, methods, classes and modules is shown |
| 200 | +# in this example: |
| 201 | + |
| 202 | +import matplotlib |
| 203 | +matplotlib.axes.Axes.pcolormesh |
| 204 | +matplotlib.figure.Figure.colorbar |
| 205 | +matplotlib.colors |
| 206 | +matplotlib.colors.LinearSegmentedColormap |
| 207 | +matplotlib.colors.ListedColormap |
| 208 | +matplotlib.cm |
| 209 | +matplotlib.cm.get_cmap |
0 commit comments