Colormaps

HoloViews supports a wide range of colormaps, each of which allow you to translate numerical data values into visible colors in a plot. Here we will review all the colormaps provided for HoloViews and discuss when and how to use them.

The Styling_Plots user guide discusses how to specify any of the colormaps discussed here, using the cmap style option:

In [1]:
import numpy as np
import holoviews as hv
hv.extension('matplotlib')

ls  = np.linspace(0, 10, 400)
x,y = np.meshgrid(ls, ls)
img = hv.Image(np.sin(x)*np.cos(y)+0.1*np.random.rand(400,400), 
               bounds=(-20,-20,20,20)).opts(colorbar=True, xaxis=None, yaxis=None)

hv.Layout([img.relabel(c).opts(cmap=c) for c in ['gray','PiYG','flag','Set1']])
Out[1]:

As you can see, the colormap you choose can dramatically change how your data appears. A well-chosen colormap can help guide the user to notice the features of the data you want to highlight, while a poorly chosen colormap can completely obscure the data and lead to erroneous conclusions. E.g. the low levels of noise present in this data are very difficult to see in A and B, but they completely dominate the plot in C and are visible only at specific (presumably arbitrary) value levels that correspond to color transitions in D. Thus it is important to choose colormaps very carefully!

Note that the cmap style option used above is applied by the underlying plotting library, not by HoloViews itself. In the above example, Matplotlib uses it as the colormap constructs the image, whereas a Bokeh version of the same plot would provide the colormap to the Bokeh JavaScript code running in the local web browser, which allows the user to control the colormap dynamically in some cases.

Colormaps can also be used with the Datashader shade() operation, in which the provided cmap is applied by Datashader to create an image before passing the image to the plotting library, which enables additional Datashader features but disables client-side features like colorbars and dynamic colormapping on display.

Available colormaps

As outlined in Styling_Plots, you can easily make your own custom colormaps, but it's quite difficult to ensure that a custom map is designed well, so it's generally best to choose an existing, well-tested colormap. Here we will show the many different types of colormaps available, discussing each category and how to use that type of map. The ones shown here are those that are available by name, if the corresponding provider has been installed. E.g. those labelled "(bokeh)" will only be available if Bokeh is installed.

Most of these colormaps will work best on either a light or a dark background, but not both. To faithfully and intuitively represent monotonically increasing values, you will generally want a colormap where the lowest values are similar in tone to the page background, and higher values become more perceptually salient compared to the page background. To let you match the colormap to the page, the maps listed below have a variant suffixed with _r (not shown), which is the same map but with the reverse order.

In [2]:
from math import ceil
from holoviews.plotting.util import process_cmap

colormaps = hv.plotting.list_cmaps()
spacing = np.linspace(0, 1, 64)[np.newaxis]
opt_kwargs = dict(aspect=6, xaxis=None, yaxis=None, sublabel_format='')

def filter_cmaps(category):
    return hv.plotting.util.list_cmaps(records=True,category=category,reverse=False)

def cmap_examples(category,cols=4):
    cms = filter_cmaps(category)
    n = len(cms)*1.0
    c=ceil(n/cols) if n>cols else cols
    bars = [hv.Image(spacing, ydensity=1, label="{0} ({1})".format(r.name,r.provider))\
            .opts(cmap=process_cmap(r.name,provider=r.provider), **opt_kwargs)
           for r in cms]
    return hv.Layout(bars).opts(vspace=0.1, hspace=0.1, transpose=(n>cols)).cols(c)

Perceptually uniform sequential colormaps

Useful for the typical case of having increasing numeric values that you want to distinguish without bias for any specific value. The colormaps in this category are designed to represent similar distances in value space (e.g. a numerical difference from 0.2 to 0.4, or one from 0.4 to 0.6, with similar differences in what we perceive visually).

For detailed discussions of this important issue, see Kovesi, van der Walt & Smith, and Karpov, who each argue for different color spaces and criteria for evaluating colormaps and thus develop different types of colormaps. Despite the disagreements over important details, all of the maps here will be significantly more uniform than an arbitrary map designed without perceptual criteria, such as those in "Other Sequential" below, and thus these colormaps represent good default choices in most cases.

When choosing one of these, be sure to consider whether you wish your page background to be distinguishable from a color in the colormap. If your data covers the entire plot, then using the background color is fine, but if you need the background color to show through (e.g. to show missing values), then you should avoid maps that include black (fire, magma, inferno, gray, k*) on a black page or white (fire,gray) on a white page.

In [3]:
cmap_examples('Uniform Sequential')
Out[3]:

Diverging colormaps

Useful to highlight differences from a neutral central value, which is typically chosen to match the page background (e.g. white or yellow when using a white page, or black when using a black page).

Most of the diverging maps listed here were not developed to match a definition of perceptual uniformity, but those coming from colorcet were and should thus be preferred over the rest (which can be obtained by specifying Uniform Diverging here).

Some of these colormaps include both red and green, making them ambiguous for people with the most common types of colorblindness, and should thus be avoided where possible.

In [4]:
cmap_examples('Diverging')
Out[4]:

Rainbow colormaps

Rainbow-like colormaps convey value primarily through color rather than luminance. They result in eye-catching plots, but because rainbow colors form a continuous, cyclic spectrum, they can be ambiguous about which values are higher than the others. Most of them are also highly perceptually non-uniform, with pronounced banding that makes some values easily distinguished from their neighbors, and other wide ranges of values nearly indistinguishable (e.g. the greenish colors in the gist_rainbow and jet colormaps).

If you do want a rainbow colormap, please consider using one of the three perceptually uniform versions (category Uniform Rainbow) included here:

  • colorwheel (colorcet): for cyclic values like angles and longitudes that wrap around to the same value at the end of the range (notice that the lowest and highest colors are both blue)
  • rainbow (colorcet): for monotonically and uniformly increasing values (skips purple region to avoid ordering ambiguity)
  • isolum (colorcet): for monotonically and uniformly increasing values, but only uses hue changes, with a constant lightness. Nearly all the other maps are dominated by changes in lightness, which is much more perceptually salient than strict changes in hue as in this map. Useful as a counterpart and illustration of the role of lightness.

Of course, rainbow colormaps have the disadvantage that they are inherently unsuitable for most colorblind viewers, because they require viewers to distinguish between red and green to determine value.

In [5]:
cmap_examples('Rainbow')
Out[5]:

Categorical colormaps

Primarily useful as color cycles rather than colormaps, i.e. as a list of discrete color values, not a continuous range of colors. Will produce discrete banding when used on continuous values, like in a geographic contour plot, but if that effect is desired it's probably better to use color_levels with a sequential colormap to be able to control how many levels there are and give them a natural ordering.

Most of these color sets are constructed by hand, with a relatively small number of distinct colors. If you want a larger number of colors, the glasbey_ categorical maps from Colorcet are generated using a systematic procedure based on sampling a perceptual space for widely separated colors, which allows large numbers of categories to be distinguished from each other.

The glasbey_hv colors have the useful property that they share the same first 12 colors as the default HoloViews color cycle, which means that if you want the same colors as usual but with more available when needed, you can switch the HoloViews default using hv.Cycle.default_cycles['default_colors']=colorcet.glasbey_hv.

In [6]:
cmap_examples('Categorical')
Out[6]:

Mono Sequential colormaps

Monotonically increasing values that serve the same purpose as Uniform Sequential (above), but are not specifically constructed to be perceptually uniform. Useful when you want to fit into a particular visual theme or color scheme, or when you want to color entire plots differently from other entire plots (e.g. to provide a visual "traffic light" indicator for the entire plot, making some plots stand out relative to others). If you just need a single colormap, try to select a Uniform Sequential map instead of these.

In [7]:
cmap_examples('Mono Sequential')
Out[7]:

Other Sequential colormaps

Other sequential colormaps are included, but are not meant for general use. Some of these have a very high degree of perceptual non-uniformity, making them highly misleading. E.g. the hot (matplotlib) colormap includes pronounced banding (with sharp perceptual discontinuities and long stretches of indistinguishable colors); consider using the perceptually uniform fire (colorcet) map instead. Others like gray largely duplicate maps in the other categories above, and so can cause confusion. The Uniform Sequential maps, or if necessary Mono Sequential, are generally good alternatives to these.

In [8]:
cmap_examples('Other Sequential')
Out[8]:

Miscellaneous colormaps

There are a variety of other colormaps not fitting into the categories above, mostly of limited usefuless. Exceptions include the flag and prism (matplotlib) colormaps that could be useful for highlighting local changes in value (details), with no information about global changes in value (due to the repeating colors).

In [9]:
cmap_examples('Miscellaneous')
Out[9]:

See the Styling_Plots user guide for how these colormaps can be used to control how your data is plotted.

Querying and filtering the list of colormaps

For most purposes, you can just pick one of the colormaps above for a given plot. However, HoloViews is very often used to build applications and dashboards, many of which include a "colormap" selection widget. Because there are so many colormaps available, most of which are inappropriate for any specific plot, it's useful to be able to pull up a list of all the colormaps that are suitable for the specific type of plot used in the app.

To allow such filtering, HoloViews stores the following information about each named colormap, matched by substring:

  • name: string name for the colormap
  • category: Type of map by intended use or purpose ('[Uniform|Mono|Other ]Sequential', '[Uniform ]Diverging', '[Uniform ]Rainbow', '[Uniform ]Categorical', or 'Miscellaneous')
  • provider: package offering the colormap ('matplotlib', 'bokeh', or 'colorcet')
  • source: original source or creator of the colormaps ('cet', 'colorbrewer', 'd3', 'bids','misc')
  • bg: base/background color expected for the map ('light','dark','medium','any')
  • reverse: whether the colormap name includes _r indicating that it is a reverse of a base map (True, False)

The hv.plotting.list_cmaps() function used above can select a subset of the available colormaps by filtering based on the above values:

list_cmaps(provider, records, name, category, source, bg, reverse)

The examples below should make it clear how to use this function.

In [10]:
from holoviews.plotting import list_cmaps
def format_list(l):
    print(' '.join(sorted([k for k in l])))

All named colormaps provided by colorcet, reversed:

In [11]:
format_list(list_cmaps(provider='colorcet',reverse=True))
bgy_r bgyw_r bjy_r bkr_r bky_r blues_r bmw_r bmy_r bwy_r colorwheel_r coolwarm_r cwr_r dimgray_r fire_r glasbey_cool_r glasbey_dark_r glasbey_hv_r glasbey_light_r glasbey_r glasbey_warm_r gray_r gwv_r isolum_r kb_r kbc_r kg_r kgy_r kr_r rainbow_r

All non-reversed colormaps provided by matplotlib and originating from d3 that have 20 in their names:

In [12]:
format_list(list_cmaps(name='20', source='d3', provider='matplotlib', reverse=False))
tab20 tab20b tab20c twilight twilight_shifted

Colormaps provided by Bokeh that are suitable for dark-colored (e.g. black) backgrounds:

In [13]:
format_list(list_cmaps(category='Other Sequential', bg='dark'))
afmhot binary_r bone gist_gray gist_heat gist_yarg_r gray hot pink

Notice how some of these have _r, because those two natively start with light values and must be reversed to be suitable on a dark background. In this case the results for bg='light' are the complementary set of colormaps:

In [14]:
format_list(list_cmaps(category='Other Sequential', bg='light'))
afmhot_r binary bone_r gist_gray_r gist_heat_r gist_yarg gray_r hot_r pink_r

However, Diverging colormaps do not change their background color when reversed, and so requesting a light or dark background gives different maps altogether (depending on their central color):

In [15]:
format_list(list_cmaps(category='Diverging', bg='dark'))
bkr bky bwy_r coolwarm_r cwr_r gwv_r
In [16]:
format_list(list_cmaps(category='Diverging', bg='light'))
BrBG BrBG_r PRGn PRGn_r PiYG PiYG_r PuOr PuOr_r RdBu RdBu_r RdGy RdGy_r RdYlBu RdYlBu_r RdYlGn RdYlGn_r Spectral Spectral_r bkr_r bky_r bwr bwr_r bwy coolwarm coolwarm_r cwr gwv seismic seismic_r

Matches are done by substring, so all sequential colormaps suitable for dark or any backgrounds can be obtained with:

In [17]:
format_list(list_cmaps(category='Sequential', bg='a'))
Blues_r BuGn_r BuPu_r GnBu_r Greens_r Greys_r Inferno Magma OrRd_r Oranges_r Plasma PuBuGn_r PuBu_r PuRd_r Purples_r RdPu_r Reds_r Viridis Wistia Wistia_r YlGnBu_r YlGn_r YlOrBr_r YlOrRd_r afmhot autumn autumn_r bgy bgyw binary_r blues blues_r bmw bmy bone cividis cool cool_r copper copper_r dimgray fire gist_gray gist_heat gist_yarg_r gray hot inferno kb kb_r kbc kg kg_r kgy kr kr_r magma pink plasma spring spring_r summer summer_r viridis winter winter_r

In the examples above, list_cmaps is returning just the colormap name, but if you want to work with the filter information yourself to do more complex queries, you can ask that it return the full records as namedtuples instead:

In [18]:
list_cmaps(category="Uniform Sequential", provider='bokeh', bg='light', records=True)
Out[18]:
[CMapInfo(name='Inferno_r', provider='bokeh', category='Uniform Sequential', source='bids', bg='light'),
 CMapInfo(name='Magma_r', provider='bokeh', category='Uniform Sequential', source='bids', bg='light'),
 CMapInfo(name='Plasma_r', provider='bokeh', category='Uniform Sequential', source='bids', bg='light'),
 CMapInfo(name='Viridis_r', provider='bokeh', category='Uniform Sequential', source='bids', bg='light')]

In addition to populating GUI widgets, another way to use this filtering is to systematically evaluate how your plot will look with a variety of different colormaps of the same type:

In [19]:
hv.Layout([img.relabel(c).opts(cmap=c, colorbar=False, sublabel_format='') 
           for c in list_cmaps(category='Diverging', bg='light', reverse=False)][:14])\
    .opts(vspace=0.1, hspace=0.1).cols(7)
Out[19]:

You could also consider filtering on the actual values in the colormap, perhaps to ensure that the specific background color you are using is not present in the colormap. For this you can use the hv.plotting.util.process_cmap function to look up the actual colormap values by name and provider.