Colormaps

In contrast to Color cycles, colormaps are palettes constructed by sampling some continuous function between two end colors. Colormaps are generally used to encode data values on a pseudo-third dimension. They are are implemented with the LinearSegmentedColormap and PerceptuallyUniformColormap classes, which are subclassed from LinearSegmentedColormap in matplotlib (see Making new colormaps).

ProPlot adds several features to help you use colormaps effectively in your figures. This section documents the new registered colormaps, explains how to make and modify colormaps, and shows how to apply them to your plots.

Included colormaps

On import, ProPlot registers a few sample PerceptuallyUniformColormap colormaps (see Perceptually uniform colormaps) plus a ton of other colormaps from other online data viz projects. Use show_cmaps to generate a table of registered maps, as shown below. The figure is broken down into the following sections:

  • “User” colormaps, i.e. colormaps saved to your ~/.proplot/cmaps folder. A great way to save colormaps to this folder is using the Colormap function. See Making new colormaps for details.

  • Matplotlib and seaborn original colormaps.

  • ProPlot colormaps belonging to the PerceptuallyUniformColormap class. See the Perceptually uniform colormaps section.

  • Miscellaneous diverging colormaps.

  • cmOcean colormaps, originally intended for oceanographic visualizations but useful for all scientific fields.

  • ColorBrewer colormaps, included with matplotlib by default.

  • Colormaps from the SciVisColor online interactive tool. There are so many of these maps because they are intended to be merged with one another – suitable for complex datasets with complex statistical distributions.

ProPlot removes some default matplotlib colormaps with erratic color transitions. Note that colormap and color cycle identification is now flexible: names are case-insensitive (e.g. 'Viridis', 'viridis', and 'ViRiDiS' are equivalent) and can be specified in their “reversed” form (e.g. 'BuRd' is equivalent to 'RdBu_r'). See CmapDict for more info.

[1]:
import proplot as plot
f = plot.show_cmaps()
_images/colormaps_4_0.svg

Perceptually uniform colormaps

ProPlot’s custom colormaps are instances of the new PerceptuallyUniformColormap class (see Included colormaps for a table). PerceptuallyUniformColormap objects generate colors by interpolating between coordinates in any of three possible “perceptually uniform” colorspaces:

  • HCL: A purely perceptually uniform colorspace, where colors are broken down into “hue” (color, range 0-360), “chroma” (saturation, range 0-100), and “luminance” (brightness, range 0-100).

  • HPLuv: Hue and luminance are identical to HCL, but 100 saturation is scaled to be the minimum maximum saturation across all hues for a given luminance. HPLuv is more appropriate for multi-hue colormaps.

  • HSLuv: Hue and luminance are identical to HCL, but 100 saturation is scaled to be the maximum possible saturation for a given hue and luminance. HSLuv is more appropriate for single-hue colormaps – saturation banding can occur when crossing hue thresholds in this space.

HCL is the only “purely” perceptually uniform colorspace. But interpolating between coordinates in this space can result in “impossible” colors – colors that, when translated from HCL back into RGB, result in RGB channels greater than 1. HSLuv and HPLuv help resolve this issue by respectively scaling and clipping the highest-saturation colors across different hues and luminances. See this page for more info.

To plot arbitrary cross-sections of these colorspaces, use show_colorspaces. The blacked out regions represent “impossible” colors.

[2]:
import proplot as plot
f = plot.show_colorspaces(axwidth=1.6, luminance=50)
_images/colormaps_7_0.svg
[3]:
import proplot as plot
f = plot.show_colorspaces(axwidth=1.6, saturation=60)
_images/colormaps_8_0.svg
[4]:
import proplot as plot
f = plot.show_colorspaces(axwidth=1.6, hue=0)
_images/colormaps_9_0.svg

To see how any colormap varies with respect to each channel, use the show_channels function. Below, we do this for the matplotlib 'magma', seaborn 'rocket', and proplot 'Fire' and 'Dusk' colormaps. The first two are nicely-designed LinearSegmentedColormaps, and the last one is a PerceptuallyUniformColormap. They are all roughly linear across the hue and luminance channels, but not the chroma channel (top row). 'Fire' is linear in the HSL scaling of the chroma channel (bottom left), while 'Dusk' is linear in the HPL scaling of the chroma channel (bottom right).

[5]:
import proplot as plot
f = plot.show_channels('magma', 'rocket', 'fire', 'dusk', axwidth=1.2, minhue=-180, maxsat=300, rgb=False)
_images/colormaps_11_0.svg

Making new colormaps

You can make new colormaps with Colormap. Every command that accepts a cmap argument (see cmap_changer) is passed to Colormap, and Colormap keyword args can be specified with cmap_kw. To save your colormap and use it everytime ProPlot is imported, simply use save=True. To save the colormap data to an arbitrary file, use the save method. See Colormap and cmap_changer for details.

To build monochromatic PerceptuallyUniformColormaps from arbitrary colors, just pass a color name, hex string, or RGB tuple to Colormap. The colormap colors will vary from the specified color to some shade near white (controlled by the fade keyword arg). The default is to fade to pure white. The first plot shows several of these maps merged into one, and the second is just one map.

[6]:
import proplot as plot
import numpy as np
f, axs = plot.subplots(ncols=2, axwidth=2, aspect=1, bottom=0.1)
state = np.random.RandomState(51423)
data = state.rand(50,50).cumsum(axis=1)
cmap1 = plot.Colormap('brick red_r', 'denim_r', 'warm gray_r', fade=90, name='tricolor')
m = axs[0].contourf(data, cmap=cmap1, levels=12)
m = axs[1].contourf(data, cmap='ocean blue', cmap_kw={'name':'ocean blue'})
cmap2 = m.cmap
axs.format(xticks='none', yticks='none', suptitle='Monochromatic PerceptuallyUniformColormaps')
for ax,title in zip(axs, ['Three monochromatic cmaps', 'One monochromatic cmap']):
    ax.format(title=title)
f = plot.show_channels(cmap1, cmap2, axwidth=1.2, rgb=False)
_images/colormaps_14_0.svg
_images/colormaps_14_1.svg

To build PerceptuallyUniformColormaps with arbitrary channel transitions, you have two options.

  • Pass a dictionary to Colormap. This calls the from_hsl static method, which interpolates between the listed colors in a perceptually uniform colorspace.

  • Pass a list of colors to Colormap. This calls the from_list static method, which draws lines between channel values specified by the keyword args hue, saturation, and luminance. The values can be numbers, color strings, or lists thereof. Numbers indicate the channel value. For color strings, the channel value is inferred from the specified color. You can end any color string with '+N' or '-N' to offset the channel value by the number N, as shown below.

[7]:
import proplot as plot
import numpy as np
f, axs = plot.subplots(ncols=3, span=False, axwidth=2, aspect=1.5)
axs.format(suptitle='Building your own PerceptuallyUniformColormaps')
# Colormaps from channel values
state = np.random.RandomState(51423)
data = state.rand(10,15)
ax = axs[0]
cmap1 = plot.Colormap({'hue':['red-90', 'red+90'], 'saturation':[50, 70, 30], 'luminance':[20, 100]}, name='Matter', space='hcl')
m = ax.pcolormesh(data, cmap=cmap1)
ax.format(title='From channel values')
ax = axs[1]
cmap2 = plot.Colormap({'hue':['red', 'red-720'], 'saturation':[80,20], 'luminance':[20, 100]}, name='cubehelix', space='hpl')
m = ax.pcolormesh(data, cmap=cmap2)
ax.format(xlabel='x axis', ylabel='y axis', title='From channel values')
# Colormap from lists
ax = axs[2]
m = ax.pcolormesh(data, cmap=('maroon', 'goldenrod'), cmap_kw={'name':'reddish'})
cmap3 = m.cmap
ax.format(title='From list of colors')
# Colormap breakdowns
f = plot.show_channels(cmap1, cmap2, cmap3, minhue=-180, axwidth=1.2, rgb=False)
_images/colormaps_16_0.svg
_images/colormaps_16_1.svg

Merging colormaps

Colormap also lets you merge arbitrary colormaps and modify existing colormaps. To merge colormaps, simply pass multiple arguments to the Colormap constructor. This makes it easy to create complex SciVisColor style colormaps, which may be desirable for complex datasets with funky statistical distributions. The below reconstructs the colormap from this example.

[8]:
import proplot as plot
import numpy as np
f, axs = plot.subplots(ncols=2, axwidth=2, span=False)
state = np.random.RandomState(51423)
data = state.rand(100,100).cumsum(axis=1)
# Save colormap as "test1.json"
cmap = plot.Colormap('Green1_r', 'Orange5', 'Blue1_r', 'Blue6', name='test1', save=True)
m = axs[0].contourf(data, cmap=cmap, levels=100)
f.colorbar(m, loc='b', col=1, locator='none')
# Save colormap as "test2.json"
cmap = plot.Colormap('Green1_r', 'Orange5', 'Blue1_r', 'Blue6', ratios=(1,3,5,10), name='test2', save=True)
m = axs[1].contourf(data, cmap=cmap, levels=100)
f.colorbar(m, loc='b', col=2, locator='none')
axs.format(xlabel='xlabel', ylabel='ylabel', suptitle='Merging existing colormaps')
for ax,title in zip(axs, ['Evenly spaced', 'Matching SciVisColor example']):
    ax.format(title=title)
Saved colormap to '/home/docs/.proplot/cmaps/test1.json'.
Saved colormap to '/home/docs/.proplot/cmaps/test2.json'.
_images/colormaps_19_1.svg

Modifying colormaps

To modify a diverging colormap by cutting out some central colors, pass the cut keyword arg to Colormap. This is great when you want to have a sharper cutoff between negative and positive values. To cut out colors from the left or right of a colormap, pass the left and right keyword args to Colormap. To rotate a cyclic colormap, pass the shift argument to Colormap (see LinearSegmentedColormap) – ProPlot ensures the colors at the ends of these maps are distinct, so that levels don’t blur together.

[9]:
import proplot as plot
import numpy as np
f, axs = plot.subplots([[1,1,2,2,3,3],[0,4,4,5,5,0]], axwidth=1.5)
state = np.random.RandomState(51423)
data = state.rand(50,50).cumsum(axis=0) - 50
# Cutting central colors
for ax,cut in zip(axs[:3],(0, 0.1, 0.2)):
    m = ax.contourf(data, cmap='Div', cmap_kw={'cut':cut}, levels=13)
    ax.format(xlabel='xlabel', ylabel='ylabel', title=f'cut = {cut}',
              suptitle='Slicing existing colormaps')
    ax.colorbar(m, loc='b', locator='null')
# Cutting left and right
for ax,cut in zip(axs[3:],(0.2,0.8)):
    if cut<0.5:
        title, cmap, cmap_kw = f'left={cut}', 'grays', {'left':cut}
    else:
        title, cmap, cmap_kw = f'right={cut}', 'grays', {'right':cut}
    ax.contourf(data, cmap=cmap, cmap_kw=cmap_kw, colorbar='b', colorbar_kw={'locator':'null'})
    ax.format(xlabel='xlabel', ylabel='ylabel', title=title)
# Rotating cyclic
f, axs = plot.subplots(ncols=3, axwidth=1.5)
data = (state.rand(50,50)-0.48).cumsum(axis=1).cumsum(axis=0) - 50
for ax,shift in zip(axs,(0, 90, 180)):
    m = ax.contourf(data, cmap='twilight', cmap_kw={'shift':shift}, levels=12)
    ax.format(xlabel='x axis', ylabel='y axis', title=f'shift = {shift}',
              suptitle='Rotating cyclic colormaps')
    ax.colorbar(m, loc='b', locator='null')
_images/colormaps_22_0.svg
_images/colormaps_22_1.svg

You can also change the “gamma” of any PerceptuallyUniformColormap map on-the-fly. The gamma controls how the luminance and saturation channels vary between segments of the colormap. A gamma larger than 1 emphasizes high luminance, low saturation colors, and a gamma smaller than 1 emphasizes low luminance, high saturation colors. See PerceptuallyUniformColormap for details.

[10]:
import proplot as plot
import numpy as np
name = 'boreal'
# Illustrations
f, axs = plot.subplots(ncols=3, axwidth=1.5, aspect=1)
state = np.random.RandomState(51423)
data = state.rand(10,10).cumsum(axis=1)
cmaps = []
for ax,gamma in zip(axs,(0.7, 1.0, 1.4)):
    cmap = plot.Colormap(name, name=f'g{gamma}', gamma=gamma)
    cmaps.append(cmap)
    m = ax.pcolormesh(data, cmap=cmap, levels=10, extend='both')
    ax.colorbar(m, loc='r', locator='none')
    ax.format(title=f'gamma = {gamma}', xlabel='x axis', ylabel='y axis', suptitle='Modifying existing PerceptuallyUniformColormaps')
# Breakdowns
f = plot.show_channels(*cmaps, axwidth=1.2, rgb=False)
_images/colormaps_24_0.svg
_images/colormaps_24_1.svg

Downloading colormaps

There are plenty of online interactive tools for generating perceptually uniform colormaps, including HCLWizard, Chroma.js, SciVisColor, and HCL picker.

To add colormaps downloaded from any of these sources, save the colormap data to a file in your ~/.proplot/cmaps folder, then call register_cmaps. The file should be named name.ext, where name will be the registered colormap name. See register_cmaps for valid file extensions.