-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
DOC: colormap-manipulation tutorial #11905
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
daa9c59
6d2a7bc
f58f5f3
d6a5179
c6544d8
e9cda7f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
""" | ||
******************************** | ||
Creating Colormaps in Matplotlib | ||
******************************** | ||
|
||
Matplotlib colormaps are implimented as a class, which makes them quite | ||
flexible, but opaque to users as to how to create and/or | ||
manipulate them. This opacity is not helped in the library by the fact that | ||
the named colormaps are accessed via `.matplotlib.cm.get_cmap` module, whereas | ||
the colormap class itself is defined in `.matplotlib.colors.Colormap`! | ||
|
||
Fortunately, the way to create colormaps is quite straight forward, by creating | ||
an instance of class `.ListedColormap` using a Nx4 numpy array of values | ||
between 0 and 1 to represent the RGBA values of the colormap. | ||
|
||
Getting colormaps and accessing their values | ||
============================================ | ||
|
||
First, getting a named colormap, most of which are listed in | ||
:doc:`/tutorials/colors/colormaps` requires the use of | ||
`.matplotlib.cm.get_cmap`, which returns a | ||
:class:`.matplotlib.colors.ListedColormap` object. The second argument gives | ||
the size of the list of colors used to define the colormap, and below we | ||
use a modest value of 12 so there are not a lot of values to look at. | ||
""" | ||
|
||
import numpy as np | ||
import matplotlib as mpl | ||
import matplotlib.pyplot as plt | ||
from matplotlib import cm | ||
from matplotlib.colors import ListedColormap, LinearSegmentedColormap | ||
from collections import OrderedDict | ||
|
||
viridis = cm.get_cmap('viridis', 12) | ||
print(viridis) | ||
|
||
############################################################################## | ||
# The object ``viridis`` is a callable, that when passed a float between | ||
# 0 and 1 returns an RGBA value from the colormap: | ||
|
||
print(viridis(0.56)) | ||
|
||
############################################################################## | ||
# The list of colors that comprise the colormap can be directly accessed using | ||
# the ``colors`` property, | ||
# or it can be acccessed indirectly by calling ``viridis`` with an array | ||
# of values matching the length of the colormap. Note that the returned list | ||
# is in the form of an RGBA Nx4 array, where N is the length of the colormap. | ||
|
||
print('viridis.colors', viridis.colors) | ||
print('viridis(range(12))', viridis(range(12))) | ||
print('viridis(np.linspace(0, 1, 12))', viridis(np.linspace(0, 1, 12))) | ||
|
||
############################################################################## | ||
# The colormap is a lookup table, so "oversampling" the colormap returns | ||
# nearest-neighbor interpolation (note the repeated colors in the list below) | ||
|
||
print('viridis(np.linspace(0, 1, 15))', viridis(np.linspace(0, 1, 15))) | ||
|
||
############################################################################## | ||
# Creating a new ListedColormap | ||
# ============================= | ||
# | ||
# This is essential the inverse operation of the above where we supply a | ||
# Nx4 numpy array with all values between 0 and 1, | ||
# to `.ListedColormap` to make a new colormap. This means that | ||
# any numpy operations that we can do on a Nx4 array make carpentry of | ||
# new colormaps from existing colormaps quite straight forward. | ||
# | ||
# Suppose we want to make the first 25 entries of a 256-length "viridis" | ||
# colormap pink for some reason: | ||
|
||
viridis = cm.get_cmap('viridis', 256) | ||
newcolors = viridis(np.linspace(0, 1, 256)) | ||
pink = np.array([248/256, 24/256, 148/256, 1]) | ||
newcolors[:25, :] = pink | ||
newcmp = ListedColormap(newcolors) | ||
|
||
|
||
def plot_examples(cms): | ||
""" | ||
helper function to plot two colormaps | ||
""" | ||
np.random.seed(19680801) | ||
data = np.random.randn(30, 30) | ||
|
||
fig, axs = plt.subplots(1, 2, figsize=(6, 3), constrained_layout=True) | ||
for [ax, cmap] in zip(axs, cms): | ||
psm = ax.pcolormesh(data, cmap=cmap, rasterized=True, vmin=-4, vmax=4) | ||
fig.colorbar(psm, ax=ax) | ||
plt.show() | ||
|
||
plot_examples([viridis, newcmp]) | ||
|
||
############################################################################## | ||
# We can easily reduce the range of a colormap; here we choose the middle | ||
# 0.5 of the colormap. | ||
|
||
viridis = cm.get_cmap('viridis', 256) | ||
newcmp = ListedColormap(viridis(np.linspace(0.25, 0.75, 256))) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will this have over-sampling issues? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ooops, yes it will. Not really noticeable at 256 resolution, but would certainly be a problem for small colormaps. Changed to # We can easily reduce the dynamic range of a colormap; here we choose the middle
# 0.5 of the colormap. However, we need to interpolate from a larger
# colormap, otherwise the new colormap will have repeated values.
viridisBig = cm.get_cmap('viridis', 512)
newcmp = ListedColormap(viridisBig(np.linspace(0.25, 0.75, 256)))
plot_examples([viridis, newcmp]) |
||
plot_examples([viridis, newcmp]) | ||
|
||
############################################################################## | ||
# and we can easily concatenate two colormaps: | ||
|
||
top = cm.get_cmap('Oranges_r', 128) | ||
bottom = cm.get_cmap('Blues', 128) | ||
|
||
newcolors = np.vstack((top(np.linspace(0, 1, 128)), | ||
bottom(np.linspace(0, 1, 128)))) | ||
newcmp = ListedColormap(newcolors, name='OrangeBlue') | ||
plot_examples([viridis, newcmp]) | ||
|
||
############################################################################## | ||
# Of course we need not start from a named colormap, we just need to create | ||
# the Nx4 array to pass to `.ListedColormap`. Here we create a | ||
# brown colormap that goes to white.... | ||
|
||
N = 256 | ||
vals = np.ones((N, 4)) | ||
vals[:, 0] = np.linspace(90/256, 1, N) | ||
vals[:, 1] = np.linspace(39/256, 1, N) | ||
vals[:, 2] = np.linspace(41/256, 1, N) | ||
newcmp = ListedColormap(vals) | ||
plot_examples([viridis, newcmp]) | ||
|
||
############################################################################## | ||
# LinearSegmented colormaps | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Retitled |
||
# ========================= | ||
# | ||
# `.LinearSegmentedColormap` have an alternate way to specify colormaps that | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Alternative wording:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. rewritten... |
||
# specify anchor points for linear ramps for each of RGB, and optionally, alpha | ||
# (RGBA). | ||
# | ||
# The format to specify these colormaps is a bit complicated to allow | ||
# discontinuities at the anchor points. First, with no discontinuities: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the format needs a description here, or at least a "See LinearSegmentedColormap for a description of the format." There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. new version describes the format. |
||
|
||
cdict = {'red': [[0.0, 0.0, 0.0], | ||
[0.5, 1.0, 1.0], | ||
[1.0, 1.0, 1.0]], | ||
'green': [[0.0, 0.0, 0.0], | ||
[0.25, 0.0, 0.0], | ||
[0.75, 1.0, 1.0], | ||
[1.0, 1.0, 1.0]], | ||
'blue': [[0.0, 0.0, 0.0], | ||
[0.5, 0.0, 0.0], | ||
[1.0, 1.0, 1.0]]} | ||
|
||
|
||
def plot_linearmap(cdict): | ||
newcmp = LinearSegmentedColormap('testCmap', segmentdata=cdict, N=256) | ||
rgba = newcmp(np.linspace(0, 1, 256)) | ||
fig, ax = plt.subplots(figsize=(4, 3), constrained_layout=True) | ||
col = ['r', 'g', 'b'] | ||
for xx in [0.25, 0.5, 0.75]: | ||
ax.axvline(xx, color='0.7', linestyle='--') | ||
for i in range(3): | ||
ax.plot(np.arange(256)/256, rgba[:, i], color=col[i]) | ||
ax.set_xlabel('index') | ||
ax.set_ylabel('RGB') | ||
plt.show() | ||
|
||
plot_linearmap(cdict) | ||
|
||
############################################################################# | ||
# However, consider the case where the third column is different than the | ||
# second. The linear interpolation between red[i, 0] and red[i+1, 0] is | ||
# from red[i, 2] to red[i+1, 1]. This format allows us to have | ||
# discontinuities in the colormap at the anchor points; in this case | ||
# between 0 and 0.5, the linear interpolation goes from 0.3 to 1, and | ||
# between 0.5 and 1 it goes from 0.9 to 1. Note that red[0, 1], and red[2, 2] | ||
# are both superfluous to the interpolation, which happens between the last | ||
# element of the first anchor and the first element of the second anchor. | ||
|
||
cdict['red'] = [[0.0, 0.0, 0.3], | ||
[0.5, 1.0, 0.9], | ||
[1.0, 1.0, 1.0]] | ||
plot_linearmap(cdict) | ||
|
||
|
||
############################################################################# | ||
# | ||
# ------------ | ||
# | ||
# References | ||
# """""""""" | ||
# | ||
# The use of the following functions, methods, classes and modules is shown | ||
# in this example: | ||
|
||
import matplotlib | ||
matplotlib.axes.Axes.pcolormesh | ||
matplotlib.figure.Figure.colorbar | ||
matplotlib.colors | ||
matplotlib.colors.LinearSegmentedColormap | ||
matplotlib.colors.ListedColormap | ||
matplotlib.cm | ||
matplotlib.cm.get_cmap |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't indent bullet points. RestructuredText interprets this as a blockquote. This leads to over-indented bullet points in the HTML.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No problem - shoudl this be a
seealso::
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, you could make this a
seealso::
.If you switch to
seealso::
the following is just for information: Make sure to write bullet lists exactly like this:I know this is a bit picky, but that's how restructered text wants it.