Skip to content

Commit daa9c59

Browse files
committed
DOC: colormap-manipulation tutorial
1 parent 1c50036 commit daa9c59

File tree

1 file changed

+169
-0
lines changed

1 file changed

+169
-0
lines changed
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
"""
2+
*************************************************
3+
Manipulating and Creating Colormaps in Matplotlib
4+
*************************************************
5+
6+
Matplotlib colormaps are implimented as a class, which makes them quite
7+
flexible, but ocasionally opaque to users as to how to create and/or
8+
manipulate them. This opacity is not helped in the library by the fact that
9+
the named colormaps are accessed via `.matplotlib.cm.get_cmap` module, whereas
10+
the colormap class itself is defined in `.matplotlin.colors.Colormap`!
11+
12+
Getting colormaps and accessing their values
13+
============================================
14+
15+
First, getting a named colormap, most of which are listed in
16+
:doc:`/tutorials/colors/colormaps` requires the use of
17+
`.matplotlib.cm.get_cmap`, which returns a
18+
:class:`.matplotlib.colors.ListedColormap` object. The second argument gives
19+
the size of the list of colors used to define the colormap.
20+
"""
21+
22+
import numpy as np
23+
import matplotlib as mpl
24+
import matplotlib.pyplot as plt
25+
from matplotlib import cm
26+
from matplotlib.colors import ListedColormap, LinearSegmentedColormap
27+
from collections import OrderedDict
28+
29+
viridis = cm.get_cmap('viridis', 12)
30+
print(viridis)
31+
32+
##############################################################################
33+
# This list of colors can be directly accessed using the ``colors`` property,
34+
# or it can be indirectly acccessed by calling the object. Note that the list
35+
# is of the form of an RGBA Nx4 array, where N is the length of the colormap.
36+
37+
print('viridis.colors', viridis.colors)
38+
print('viridis(range(12))', viridis(range(12)))
39+
print('viridis(np.linspace(0, 1, 12))', viridis(np.linspace(0, 1, 12)))
40+
41+
##############################################################################
42+
# The colormap is a lookup table, so "oversampling" the colormap returns
43+
# nearest-neighbor interpolation (note the repeated colors in the list below)
44+
45+
print('viridis(np.linspace(0, 1, 15))', viridis(np.linspace(0, 1, 15)))
46+
47+
##############################################################################
48+
# Creating a new ListedColormap: Colormap carpentry
49+
# =================================================
50+
#
51+
# This is essential the inverse operation of the above where we supply a
52+
# Nx4 numpy array with all values between 0 and 1,
53+
# to `.ListedColormap` to make a new colormap. This means that
54+
# any numpy operations that we can do on a Nx4 array make carpentry of
55+
# new colormaps from existing colormaps quite straight forward.
56+
#
57+
# Suppose we want to make the first 25 entries of a 256-length "viridis"
58+
# colormap pink for some reason:
59+
60+
viridis = cm.get_cmap('viridis', 256)
61+
newcolors = viridis(np.linspace(0, 1, 256))
62+
pink = np.array([248/256, 24/256, 148/256, 1])
63+
newcolors[:25, :] = pink
64+
newcmp = ListedColormap(newcolors)
65+
66+
def plot_examples(cms):
67+
np.random.seed(19680801)
68+
data = np.random.randn(30,30)
69+
70+
fig, axs = plt.subplots(1, 2, figsize=(6, 3), constrained_layout=True)
71+
for [ax, cm] in zip(axs, cms):
72+
psm = ax.pcolormesh(data, cmap=cm, rasterized=True, vmin=-4, vmax=4)
73+
fig.colorbar(psm, ax=ax)
74+
plt.show()
75+
76+
plot_examples([viridis, newcmp])
77+
78+
##############################################################################
79+
# We can easily reduce the range of a colormap; here we choose the middle
80+
# 0.5 of the colormap.
81+
82+
viridis = cm.get_cmap('viridis', 256)
83+
newcmp = ListedColormap(viridis(np.linspace(0.25, 0.75, 256)))
84+
plot_examples([viridis, newcmp])
85+
86+
##############################################################################
87+
# and we can easily paste together two colormaps:
88+
89+
top = cm.get_cmap('Oranges_r', 128)
90+
bottom = cm.get_cmap('Blues', 128)
91+
92+
newcolors = np.vstack((top(np.linspace(0, 1, 128)),
93+
bottom(np.linspace(0, 1, 128))))
94+
newcmp = ListedColormap(newcolors, name='OrangeBlue')
95+
plot_examples([viridis, newcmp])
96+
97+
##############################################################################
98+
# LinearSegmented colormaps
99+
# =========================
100+
#
101+
# LinearSegmented colormaps are an alternate way to specify colormaps that
102+
# specify anchor points for linear ramps for each of RGB and optionally, alpha
103+
# (RGBA).
104+
#
105+
# The format to specify these colormaps is a bit complicated to allow
106+
# discontinuities at the anchor points. First, with no discontinuities:
107+
108+
cdict = {'red': [[0.0, 0.0, 0.0],
109+
[0.5, 1.0, 1.0],
110+
[1.0, 1.0, 1.0]],
111+
'green': [[0.0, 0.0, 0.0],
112+
[0.25, 0.0, 0.0],
113+
[0.75, 1.0, 1.0],
114+
[1.0, 1.0, 1.0]],
115+
'blue': [[0.0, 0.0, 0.0],
116+
[0.5, 0.0, 0.0],
117+
[1.0, 1.0, 1.0]]}
118+
119+
def plot_linearmap(cdict):
120+
newcmp = LinearSegmentedColormap('testCmap', segmentdata=cdict, N=256)
121+
rgba = newcmp(np.linspace(0, 1, 256))
122+
fig, ax = plt.subplots(figsize=(4, 3), constrained_layout=True)
123+
col = ['r', 'g', 'b']
124+
for xx in [0.25, 0.5, 0.75]:
125+
ax.axvline(xx, color='0.7', linestyle='--')
126+
for i in range(3):
127+
ax.plot(np.arange(256)/256, rgba[:, i], color=col[i])
128+
ax.set_xlabel('index')
129+
ax.set_ylabel('RGB')
130+
plt.show()
131+
132+
plot_linearmap(cdict)
133+
134+
#############################################################################
135+
# However, consider the case where the third column is different than the
136+
# second. The linear interpolation between red[i, 0] and red[i+1, 0] is
137+
# from red[i, 2] to red[i+1, 1]. This format allows us to have
138+
# discontinuities in the colormap at the anchor points; in this case
139+
# between 0 and 0.5, the linear interpolation goes from 0.3 to 1, and
140+
# between 0.5 and 1 it goes from 0.9 to 1. Note that red[0, 1], and red[2, 2]
141+
# are both superfluous to the interpolation, which happens between the last
142+
# element of the first anchor and the first element of the second anchor.
143+
144+
cdict['red'] = [[0.0, 0.0, 0.3],
145+
[0.5, 1.0, 0.9],
146+
[1.0, 1.0, 1.0]]
147+
plot_linearmap(cdict)
148+
149+
150+
#############################################################################
151+
#
152+
# ------------
153+
#
154+
# References
155+
# """"""""""
156+
#
157+
# The use of the following functions, methods, classes and modules is shown
158+
# in this example:
159+
160+
import matplotlib
161+
matplotlib.axes.Axes.pcolormesh
162+
matplotlib.figure.Figure.colorbar
163+
matplotlib.colors
164+
matplotlib.colors.LinearSegmentedColormap
165+
matplotlib.colors.ListedColormap
166+
matplotlib.cm
167+
matplotlib.cm.ScalarMappable.get_cmap
168+
matplotlib.pyplot.register_cmap
169+
matplotlib.cm.register_cmap

0 commit comments

Comments
 (0)