{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## Using colormaps" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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.\n", "\n", "The [Styling_Plots](Styling_Plots.ipynb) user guide discusses how to specify any of the colormaps discussed here, using the `cmap` style option:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import holoviews as hv\n", "hv.extension('matplotlib')\n", "\n", "ls = np.linspace(0, 10, 400)\n", "x,y = np.meshgrid(ls, ls)\n", "img = hv.Image(np.sin(x)*np.cos(y)+0.1*np.random.rand(400,400), \n", " bounds=(-20,-20,20,20)).opts(colorbar=True, xaxis=None, yaxis=None)\n", "\n", "hv.Layout([img.relabel(c).opts(cmap=c) for c in ['gray','PiYG','flag','Set1']])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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!\n", "\n", "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.\n", "\n", "Colormaps can also be used with the [Datashader `shade()` operation](15-Large_Data.ipynb), 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.\n", "\n", "## Available colormaps\n", "\n", "As outlined in [Styling_Plots](Styling_Plots.ipynb), 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.\n", "\n", "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." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from math import ceil\n", "from holoviews.plotting.util import process_cmap\n", "\n", "colormaps = hv.plotting.list_cmaps()\n", "spacing = np.linspace(0, 1, 64)[np.newaxis]\n", "opt_kwargs = dict(aspect=6, xaxis=None, yaxis=None, sublabel_format='')\n", "\n", "def filter_cmaps(category):\n", " return hv.plotting.util.list_cmaps(records=True,category=category,reverse=False)\n", "\n", "def cmap_examples(category,cols=4):\n", " cms = filter_cmaps(category)\n", " n = len(cms)*1.0\n", " c=ceil(n/cols) if n>cols else cols\n", " bars = [hv.Image(spacing, ydensity=1, label=\"{0} ({1})\".format(r.name,r.provider))\\\n", " .opts(cmap=process_cmap(r.name,provider=r.provider), **opt_kwargs)\n", " for r in cms]\n", " return hv.Layout(bars).opts(vspace=0.1, hspace=0.1, transpose=(n>cols)).cols(c)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Perceptually uniform sequential colormaps\n", "\n", "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). \n", "\n", "For detailed discussions of this important issue, see\n", "[Kovesi,](https://arxiv.org/abs/1509.03700)\n", "[van der Walt & Smith,](https://bids.github.io/colormap) and \n", "[Karpov,](http://inversed.ru/Blog_2.htm) 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.\n", "\n", "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." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "cmap_examples('Uniform Sequential')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Diverging colormaps\n", "\n", "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). \n", "\n", "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).\n", "\n", "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." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "cmap_examples('Diverging')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Rainbow colormaps\n", "\n", "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). \n", "\n", "If you do want a rainbow colormap, please consider using one of the three perceptually uniform versions (category `Uniform Rainbow`) included here:\n", "\n", "- `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)\n", "- `rainbow` (colorcet): for monotonically and uniformly increasing values (skips purple region to avoid ordering ambiguity)\n", "- `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.\n", "\n", "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." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "cmap_examples('Rainbow')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Categorical colormaps\n", "\n", "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.\n", "\n", "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. \n", "\n", "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`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "cmap_examples('Categorical')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Mono Sequential colormaps\n", "\n", "Monotonically increasing values that serve the same purpose as [Uniform Sequential](#Perceptually-uniform-sequential-colormaps) (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." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "cmap_examples('Mono Sequential')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Other Sequential colormaps\n", "\n", "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." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "cmap_examples('Other Sequential')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Miscellaneous colormaps\n", "\n", "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)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "cmap_examples('Miscellaneous')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "See the [Styling_Plots](Styling_Plots.ipynb) user guide for how these colormaps can be used to control how your data is plotted.\n", "\n", "## Querying and filtering the list of colormaps\n", "\n", "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.\n", "\n", "To allow such filtering, HoloViews stores the following information about each named colormap, matched by substring:\n", "\n", "- **name**: string name for the colormap\n", "- **category**: Type of map by intended use or purpose ('[Uniform|Mono|Other ]Sequential', '[Uniform ]Diverging', '[Uniform ]Rainbow', '[Uniform ]Categorical', or 'Miscellaneous')\n", "- **provider**: package offering the colormap ('matplotlib', 'bokeh', or 'colorcet')\n", "- **source**: original source or creator of the colormaps ('cet', 'colorbrewer', 'd3', 'bids','misc')\n", "- **bg**: base/background color expected for the map ('light','dark','medium','any')\n", "- **reverse**: whether the colormap name includes `_r` indicating that it is a reverse of a base map (True, False)\n", "\n", "The `hv.plotting.list_cmaps()` function used above can select a subset of the available colormaps by filtering based on the above values:\n", "\n", "```\n", "list_cmaps(provider, records, name, category, source, bg, reverse)\n", "```\n", "\n", "The examples below should make it clear how to use this function." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from holoviews.plotting import list_cmaps\n", "def format_list(l):\n", " print(' '.join(sorted([k for k in l])))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "All named colormaps provided by `colorcet`, reversed:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "format_list(list_cmaps(provider='colorcet',reverse=True))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "All non-reversed colormaps provided by `matplotlib` and originating from `d3` that have `20` in their names:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "format_list(list_cmaps(name='20', source='d3', provider='matplotlib', reverse=False))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Colormaps provided by Bokeh that are suitable for dark-colored (e.g. black) backgrounds:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "format_list(list_cmaps(category='Other Sequential', bg='dark'))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "format_list(list_cmaps(category='Other Sequential', bg='light'))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "format_list(list_cmaps(category='Diverging', bg='dark'))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "format_list(list_cmaps(category='Diverging', bg='light'))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Matches are done by substring, so all sequential colormaps suitable for `dark` or `any` backgrounds can be obtained with:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "format_list(list_cmaps(category='Sequential', bg='a'))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "list_cmaps(category=\"Uniform Sequential\", provider='bokeh', bg='light', records=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "hv.Layout([img.relabel(c).opts(cmap=c, colorbar=False, sublabel_format='') \n", " for c in list_cmaps(category='Diverging', bg='light', reverse=False)][:14])\\\n", " .opts(vspace=0.1, hspace=0.1).cols(7)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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." ] } ], "metadata": { "language_info": { "name": "python", "pygments_lexer": "ipython3" } }, "nbformat": 4, "nbformat_minor": 2 }