Skip to content

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

Merged
merged 6 commits into from
Aug 25, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ per-file-ignores =
tutorials/advanced/transforms_tutorial.py: E402, E501
tutorials/colors/colormaps.py: E501
tutorials/colors/colors.py: E402
tutorials/colors/colormap-manipulation.py: E402
tutorials/intermediate/artists.py: E402, E501
tutorials/intermediate/constrainedlayout_guide.py: E402, E501
tutorials/intermediate/gridspec.py: E402, E501
Expand Down Expand Up @@ -114,6 +115,7 @@ per-file-ignores =
examples/color/color_demo.py: E402
examples/color/colorbar_basics.py: E402
examples/color/colormap_reference.py: E402
examples/color/custom_cmap.py: E402
examples/color/named_colors.py: E402
examples/event_handling/data_browser.py: E501
examples/event_handling/path_editor.py: E501
Expand All @@ -129,7 +131,6 @@ per-file-ignores =
examples/images_contours_and_fields/contourf_demo.py: E402, E501
examples/images_contours_and_fields/contourf_hatching.py: E402
examples/images_contours_and_fields/contourf_log.py: E402
examples/images_contours_and_fields/custom_cmap.py: E402
examples/images_contours_and_fields/demo_bboximage.py: E402
examples/images_contours_and_fields/image_clip_path.py: E402
examples/images_contours_and_fields/image_demo.py: E402
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
Creating a colormap from a list of colors
=========================================

For more detail on creating and manipulating colormaps see
:doc:`/tutorials/colors/colormap-manipulation`.

Creating a :doc:`colormap </tutorials/colors/colormaps>`
from a list of colors can be done with the
:meth:`~.colors.LinearSegmentedColormap.from_list` method of
Expand Down
17 changes: 15 additions & 2 deletions lib/matplotlib/cm.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
"""
Builtin colormaps, colormap handling utilities, and the `ScalarMappable` mixin.

See :doc:`/gallery/color/colormap_reference` for a list of builtin colormaps.
See :doc:`/tutorials/colors/colormaps` for an in-depth discussion of colormaps.
.. seealso::

:doc:`/gallery/color/colormap_reference` for a list of builtin
colormaps.

:doc:`/tutorials/colors/colormap-manipulation` for examples of how to
make colormaps and

:doc:`/tutorials/colors/colormaps` an in-depth discussion of
choosing colormaps.

:doc:`/tutorials/colors/colormapnorms` for more details about data
normalization


"""

import functools
Expand Down
27 changes: 19 additions & 8 deletions lib/matplotlib/colors.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,25 @@

This module includes functions and classes for color specification
conversions, and for mapping numbers to colors in a 1-D array of colors called
a colormap. Colormapping typically involves two steps: a data array is first
mapped onto the range 0-1 using an instance of :class:`Normalize` or of a
subclass; then this number in the 0-1 range is mapped to a color using an
instance of a subclass of :class:`Colormap`. Two are provided here:
:class:`LinearSegmentedColormap`, which is used to generate all the built-in
colormap instances, but is also useful for making custom colormaps, and
:class:`ListedColormap`, which is used for generating a custom colormap from a
list of color specifications.
a colormap.

Mapping data onto colors using a colormap typically involves two steps:
a data array is first mapped onto the range 0-1 using a subclass of
:class:`Normalize`, then this number is mapped to a color using
a subclass of :class:`Colormap`. Two are provided here:
:class:`LinearSegmentedColormap`, which uses piecewise-linear interpolation
to define colormaps, and :class:`ListedColormap`, which makes a colormap
from a list of colors.

.. seealso::

:doc:`/tutorials/colors/colormap-manipulation` for examples of how to
make colormaps and

:doc:`/tutorials/colors/colormaps` for a list of built-in colormaps.

:doc:`/tutorials/colors/colormapnorms` for more details about data
normalization

The module also provides functions for checking whether an object can be
interpreted as a color (:func:`is_color_like`), for converting such an object
Expand Down
209 changes: 209 additions & 0 deletions tutorials/colors/colormap-manipulation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
"""
********************************
Creating Colormaps in Matplotlib
********************************

Creating and manipulating colormaps in Matplotlib is straight-forward
using the class `.ListedColormap` and a Nx4 numpy array of values
between 0 and 1 to represent the RGBA values of the colormap. There
is also a `.LinearSegmentedColormap` class that allows colormaps to be
specified with far fewer anchor points defining segments, and linearly
interpolating between the anchor points.

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 listed colormaps
# =========================
#
# 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 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])

##############################################################################
# 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])

##############################################################################
# Creating linear segmented colormaps
# ===================================
#
# `.LinearSegmentedColormap` class specifies colormaps using anchor points
# between which RGB(A) values are interpolated.
#
# The format to specify these colormaps allows discontinuities at the anchor
# points. Each anchor point is specified as a row in a matrix of the
# form ``[x[i] yleft[i] yright[i]]``, where ``x[i]`` is the anchor, and
# ``yleft[i]`` and ``yright[i]`` are the values of the color on either
# side of the anchor point.
#
# If there are no discontinuities, then ``yleft[i]=yright[i]``:

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)

#############################################################################
# In order to make a discontinuity at an anchor point, the third column is
# different than the second. The matrix for each of "red", "green", "blue",
# and optionally "alpha" is set up as::
#
# cdict['red'] = [...
# [x[i] yleft[i] yright[i]],
# [x[i+1] yleft[i+1] yright[i+1]],
# ...]
#
# and for values passed to the colormap between ``x[i]`` and ``x[i+1]``,
# the interpolation is between ``yright[i]`` and ``yleft[i+1]``.
#
# In the example below there is a discontiuity in red at 0.5. The
# interpolation between 0 and 0.5 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 because red[0, 1] is the value to the
# left of 0, and red[2, 2] is the value to the right of 1.0.

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