{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n.. redirect-from:: /tutorials/colors/colormap-manipulation\n\n\n# Creating Colormaps in Matplotlib\n\nMatplotlib has a number of built-in colormaps accessible via\n`.matplotlib.colormaps`. There are also external libraries like\npalettable_ that have many extra colormaps.\n\n\nHowever, we may also want to create or manipulate our own colormaps.\nThis can be done using the class `.ListedColormap` or\n`.LinearSegmentedColormap`.\nBoth colormap classes map values between 0 and 1 to colors. There are however\ndifferences, as explained below.\n\nBefore manually creating or manipulating colormaps, let us first see how we\ncan obtain colormaps and their colors from existing colormap classes.\n\n## Getting colormaps and accessing their values\n\nFirst, getting a named colormap, most of which are listed in\n`colormaps`, may be done using `.matplotlib.colormaps`,\nwhich returns a colormap object. The length of the list of colors used\ninternally to define the colormap can be adjusted via `.Colormap.resampled`.\nBelow we use a modest value of 8 so there are not a lot of values to look at.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import matplotlib.pyplot as plt\nimport numpy as np\n\nimport matplotlib as mpl\nfrom matplotlib.colors import LinearSegmentedColormap, ListedColormap\n\nviridis = mpl.colormaps['viridis'].resampled(8)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The object ``viridis`` is a callable, that when passed a float between\n0 and 1 returns an RGBA value from the colormap:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "print(viridis(0.56))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### ListedColormap\n\n`.ListedColormap`\\s store their color values in a ``.colors`` attribute.\nThe list of colors that comprise the colormap can be directly accessed using\nthe ``colors`` property,\nor it can be accessed indirectly by calling ``viridis`` with an array of\nvalues matching the length of the colormap. Note that the returned list is\nin the form of an RGBA (N, 4) array, where N is the length of the colormap.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "print('viridis.colors', viridis.colors)\nprint('viridis(range(8))', viridis(range(8)))\nprint('viridis(np.linspace(0, 1, 8))', viridis(np.linspace(0, 1, 8)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The colormap is a lookup table, so \"oversampling\" the colormap returns\nnearest-neighbor interpolation (note the repeated colors in the list below)\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "print('viridis(np.linspace(0, 1, 12))', viridis(np.linspace(0, 1, 12)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### LinearSegmentedColormap\n`.LinearSegmentedColormap`\\s do not have a ``.colors`` attribute.\nHowever, one may still call the colormap with an integer array, or with a\nfloat array between 0 and 1.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "copper = mpl.colormaps['copper'].resampled(8)\n\nprint('copper(range(8))', copper(range(8)))\nprint('copper(np.linspace(0, 1, 8))', copper(np.linspace(0, 1, 8)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Creating listed colormaps\n\nCreating a colormap is essentially the inverse operation of the above where\nwe supply a list or array of color specifications to `.ListedColormap` to\nmake a new colormap.\n\nBefore continuing with the tutorial, let us define a helper function that\ntakes one of more colormaps as input, creates some random data and applies\nthe colormap(s) to an image plot of that dataset.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def plot_examples(colormaps):\n \"\"\"\n Helper function to plot data with associated colormap.\n \"\"\"\n np.random.seed(19680801)\n data = np.random.randn(30, 30)\n n = len(colormaps)\n fig, axs = plt.subplots(1, n, figsize=(n * 2 + 2, 3),\n layout='constrained', squeeze=False)\n for [ax, cmap] in zip(axs.flat, colormaps):\n psm = ax.pcolormesh(data, cmap=cmap, rasterized=True, vmin=-4, vmax=4)\n fig.colorbar(psm, ax=ax)\n plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the simplest case we might type in a list of color names to create a\ncolormap from those.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "cmap = ListedColormap([\"darkorange\", \"gold\", \"lawngreen\", \"lightseagreen\"])\nplot_examples([cmap])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In fact, that list may contain any valid\n`Matplotlib color specification `.\nParticularly useful for creating custom colormaps are (N, 4)-shaped arrays.\nBecause with the variety of numpy operations that we can do on a such an\narray, carpentry of new colormaps from existing colormaps become quite\nstraight forward.\n\nFor example, suppose we want to make the first 25 entries of a 256-length\n\"viridis\" colormap pink for some reason:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "viridis = mpl.colormaps['viridis'].resampled(256)\nnewcolors = viridis(np.linspace(0, 1, 256))\npink = np.array([248/256, 24/256, 148/256, 1])\nnewcolors[:25, :] = pink\nnewcmp = ListedColormap(newcolors)\n\nplot_examples([viridis, newcmp])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can reduce the dynamic range of a colormap; here we choose the\nmiddle half of the colormap. Note, however, that because viridis is a\nlisted colormap, we will end up with 128 discrete values instead of the 256\nvalues that were in the original colormap. This method does not interpolate\nin color-space to add new colors.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "viridis_big = mpl.colormaps['viridis']\nnewcmp = ListedColormap(viridis_big(np.linspace(0.25, 0.75, 128)))\nplot_examples([viridis, newcmp])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and we can easily concatenate two colormaps:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "top = mpl.colormaps['Oranges_r'].resampled(128)\nbottom = mpl.colormaps['Blues'].resampled(128)\n\nnewcolors = np.vstack((top(np.linspace(0, 1, 128)),\n bottom(np.linspace(0, 1, 128))))\nnewcmp = ListedColormap(newcolors, name='OrangeBlue')\nplot_examples([viridis, newcmp])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Of course we need not start from a named colormap, we just need to create\nthe (N, 4) array to pass to `.ListedColormap`. Here we create a colormap that\ngoes from brown (RGB: 90, 40, 40) to white (RGB: 255, 255, 255).\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "N = 256\nvals = np.ones((N, 4))\nvals[:, 0] = np.linspace(90/256, 1, N)\nvals[:, 1] = np.linspace(40/256, 1, N)\nvals[:, 2] = np.linspace(40/256, 1, N)\nnewcmp = ListedColormap(vals)\nplot_examples([viridis, newcmp])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Creating linear segmented colormaps\n\nThe `.LinearSegmentedColormap` class specifies colormaps using anchor points\nbetween which RGB(A) values are interpolated.\n\nThe format to specify these colormaps allows discontinuities at the anchor\npoints. Each anchor point is specified as a row in a matrix of the\nform ``[x[i] yleft[i] yright[i]]``, where ``x[i]`` is the anchor, and\n``yleft[i]`` and ``yright[i]`` are the values of the color on either\nside of the anchor point.\n\nIf there are no discontinuities, then ``yleft[i] == yright[i]``:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "cdict = {'red': [[0.0, 0.0, 0.0],\n [0.5, 1.0, 1.0],\n [1.0, 1.0, 1.0]],\n 'green': [[0.0, 0.0, 0.0],\n [0.25, 0.0, 0.0],\n [0.75, 1.0, 1.0],\n [1.0, 1.0, 1.0]],\n 'blue': [[0.0, 0.0, 0.0],\n [0.5, 0.0, 0.0],\n [1.0, 1.0, 1.0]]}\n\n\ndef plot_linearmap(cdict):\n newcmp = LinearSegmentedColormap('testCmap', segmentdata=cdict, N=256)\n rgba = newcmp(np.linspace(0, 1, 256))\n fig, ax = plt.subplots(figsize=(4, 3), layout='constrained')\n col = ['r', 'g', 'b']\n for xx in [0.25, 0.5, 0.75]:\n ax.axvline(xx, color='0.7', linestyle='--')\n for i in range(3):\n ax.plot(np.arange(256)/256, rgba[:, i], color=col[i])\n ax.set_xlabel('index')\n ax.set_ylabel('RGB')\n plt.show()\n\nplot_linearmap(cdict)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In order to make a discontinuity at an anchor point, the third column is\ndifferent than the second. The matrix for each of \"red\", \"green\", \"blue\",\nand optionally \"alpha\" is set up as::\n\n cdict['red'] = [...\n [x[i] yleft[i] yright[i]],\n [x[i+1] yleft[i+1] yright[i+1]],\n ...]\n\nand for values passed to the colormap between ``x[i]`` and ``x[i+1]``,\nthe interpolation is between ``yright[i]`` and ``yleft[i+1]``.\n\nIn the example below there is a discontinuity in red at 0.5. The\ninterpolation between 0 and 0.5 goes from 0.3 to 1, and between 0.5 and 1\nit goes from 0.9 to 1. Note that ``red[0, 1]``, and ``red[2, 2]`` are both\nsuperfluous to the interpolation because ``red[0, 1]`` (i.e., ``yleft[0]``)\nis the value to the left of 0, and ``red[2, 2]`` (i.e., ``yright[2]``) is the\nvalue to the right of 1, which are outside the color mapping domain.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "cdict['red'] = [[0.0, 0.0, 0.3],\n [0.5, 1.0, 0.9],\n [1.0, 1.0, 1.0]]\nplot_linearmap(cdict)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Directly creating a segmented colormap from a list\n\nThe approach described above is very versatile, but admittedly a bit\ncumbersome to implement. For some basic cases, the use of\n`.LinearSegmentedColormap.from_list` may be easier. This creates a segmented\ncolormap with equal spacings from a supplied list of colors.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "colors = [\"darkorange\", \"gold\", \"lawngreen\", \"lightseagreen\"]\ncmap1 = LinearSegmentedColormap.from_list(\"mycmap\", colors)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If desired, the nodes of the colormap can be given as numbers between 0 and\n1. For example, one could have the reddish part take more space in the\ncolormap.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "nodes = [0.0, 0.4, 0.8, 1.0]\ncmap2 = LinearSegmentedColormap.from_list(\"mycmap\", list(zip(nodes, colors)))\n\nplot_examples([cmap1, cmap2])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n## Reversing a colormap\n\n`.Colormap.reversed` creates a new colormap that is a reversed version of\nthe original colormap.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "colors = [\"#ffffcc\", \"#a1dab4\", \"#41b6c4\", \"#2c7fb8\", \"#253494\"]\nmy_cmap = ListedColormap(colors, name=\"my_cmap\")\n\nmy_cmap_r = my_cmap.reversed()\n\nplot_examples([my_cmap, my_cmap_r])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If no name is passed in, ``.reversed`` also names the copy by\n`appending '_r' ` to the original colormap's\nname.\n\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n## Registering a colormap\n\nColormaps can be added to the `matplotlib.colormaps` list of named colormaps.\nThis allows the colormaps to be accessed by name in plotting functions:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# my_cmap, my_cmap_r from reversing a colormap\nmpl.colormaps.register(cmap=my_cmap)\nmpl.colormaps.register(cmap=my_cmap_r)\n\ndata = [[1, 2, 3, 4, 5]]\n\nfig, (ax1, ax2) = plt.subplots(nrows=2)\n\nax1.imshow(data, cmap='my_cmap')\nax2.imshow(data, cmap='my_cmap_r')\n\nplt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ ".. admonition:: References\n\n The use of the following functions, methods, classes and modules is shown\n in this example:\n\n - `matplotlib.axes.Axes.pcolormesh`\n - `matplotlib.figure.Figure.colorbar`\n - `matplotlib.colors`\n - `matplotlib.colors.LinearSegmentedColormap`\n - `matplotlib.colors.ListedColormap`\n - `matplotlib.cm`\n - `matplotlib.colormaps`\n\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.13.2" } }, "nbformat": 4, "nbformat_minor": 0 }