use matplotlib color map for color cycle

Question:

If I create colors by e.g:

import numpy as np
from matplotlib import pyplot as plt

n = 6
color = plt.cm.coolwarm(np.linspace(0.1,0.9,n))
color

color is a numpy array:

array([[ 0.34832334,  0.46571115,  0.88834616,  1.        ],
       [ 0.56518158,  0.69943844,  0.99663507,  1.        ],
       [ 0.77737753,  0.84092121,  0.9461493 ,  1.        ],
       [ 0.93577377,  0.8122367 ,  0.74715647,  1.        ],
       [ 0.96049006,  0.61627642,  0.4954666 ,  1.        ],
       [ 0.83936494,  0.32185622,  0.26492398,  1.        ]])

However, If I plug in the RGB values (without the alpha value 1) as tuples in my .mplstyle file (map(tuple,color[:,0:-1])), I get an error similar to this one:

in file "/home/moritz/.config/matplotlib/stylelib/ggplot.mplstyle"
    Key axes.color_cycle: [(0.34832334141176474 does not look like a color arg
  (val, error_details, msg))

Any ideas why?

Asked By: Moritz

||

Answers:

Edit 04/2021: As of matplotlib 2.2.0, the key axes.color_cycle has been deprecated (source: API changes).
The new method is to use set_prop_cycle (source: matplotlib.axes.Axes.set_prop_cycle API)


The details are in the matplotlibrc itself, actually: it needs a string rep (hex or letter or word, not tuple).

import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl

fig, ax1 = plt.subplots(1,1)

ys = np.random.random((5, 6))
ax1.plot(range(5), ys)
ax1.set_title('Default color cycle')
plt.show()

# From the sample matplotlibrc:
#axes.color_cycle    : b, g, r, c, m, y, k  # color cycle for plot lines
                                            # as list of string colorspecs:
                                            # single letter, long name, or
                                            # web-style hex

# setting color cycle after calling plt.subplots doesn't "take"
# try some hex values as **string** colorspecs
mpl.rcParams['axes.color_cycle'] = ['#129845','#271254', '#FA4411', '#098765', '#000009']

fig, ax2 = plt.subplots(1,1)
ax2.plot(range(5), ys)
ax2.set_title('New color cycle')


n = 6
color = plt.cm.coolwarm(np.linspace(0.1,0.9,n)) # This returns RGBA; convert:
hexcolor = map(lambda rgb:'#%02x%02x%02x' % (rgb[0]*255,rgb[1]*255,rgb[2]*255),
               tuple(color[:,0:-1]))

mpl.rcParams['axes.color_cycle'] = hexcolor

fig, ax3 = plt.subplots(1,1)
ax3.plot(range(5), ys)
ax3.set_title('Color cycle from colormap')

plt.show()

enter image description here
enter image description here
enter image description here

Answered By: cphlewis

For Matplotlib 2.2, using the cycler module will do the trick, without the need to convert to Hex values.

import cycler

n = 100
color = pyplot.cm.viridis(np.linspace(0, 1,n))
mpl.rcParams['axes.prop_cycle'] = cycler.cycler('color', color)
Answered By: Gerges

“Continuous” colormap

If you want to cycle through N colors from a “continous” colormap, like e.g. the default viridis map, the solution by @Gerges works nicely.

import matplotlib.pyplot as plt

N = 6
plt.rcParams["axes.prop_cycle"] = plt.cycler("color", plt.cm.viridis(np.linspace(0,1,N)))

fig, ax = plt.subplots()
for i in range(N):
    ax.plot([0,1], [i, 2*i])

plt.show()

“Discrete” colormap

Matplotlib provides a few colormap that are “discrete” in the sense that they hold some low number of distinct colors for qualitative visuals, like the tab10 colormap. To cycle through such colormap, the solution might be to not use N but just port all colors of the map to the cycler.

import matplotlib.pyplot as plt

plt.rcParams["axes.prop_cycle"] = plt.cycler("color", plt.cm.tab20c.colors)

fig, ax = plt.subplots()
for i in range(15):
    ax.plot([0,1], [i, 2*i])

plt.show()

Note that only ListedColormaps have the .colors attribute, so this only works for those colormap, but not e.g. the jet map.

Combined solution

The following is a general purpose function that takes a colormap as input and outputs a corresponding cycler. I originally proposed this solution in this matplotlib issue.

from matplotlib.pyplot import cycler
import numpy as np
from matplotlib.colors import LinearSegmentedColormap, ListedColormap
import matplotlib.cm

def get_cycle(cmap, N=None, use_index="auto"):
    if isinstance(cmap, str):
        if use_index == "auto":
            if cmap in ['Pastel1', 'Pastel2', 'Paired', 'Accent',
                        'Dark2', 'Set1', 'Set2', 'Set3',
                        'tab10', 'tab20', 'tab20b', 'tab20c']:
                use_index=True
            else:
                use_index=False
        cmap = matplotlib.cm.get_cmap(cmap)
    if not N:
        N = cmap.N
    if use_index=="auto":
        if cmap.N > 100:
            use_index=False
        elif isinstance(cmap, LinearSegmentedColormap):
            use_index=False
        elif isinstance(cmap, ListedColormap):
            use_index=True
    if use_index:
        ind = np.arange(int(N)) % cmap.N
        return cycler("color",cmap(ind))
    else:
        colors = cmap(np.linspace(0,1,N))
        return cycler("color",colors)

Usage for the “continuous” case:

import matplotlib.pyplot as plt
N = 6
plt.rcParams["axes.prop_cycle"] = get_cycle("viridis", N)

fig, ax = plt.subplots()
for i in range(N):
    ax.plot([0,1], [i, 2*i])

plt.show()

Usage for the “discrete” case

import matplotlib.pyplot as plt

plt.rcParams["axes.prop_cycle"] = get_cycle("tab20c")

fig, ax = plt.subplots()
for i in range(15):
    ax.plot([0,1], [i, 2*i])

plt.show()

If you want something locally defined, you can add before plotting:

fig, ax4 = plt.subplots(1,1)
ax4.set_prop_cycle(color=plt.cm.plasma(np.linspace(0, 1, n))
ax4.plot(range(5), ys)
ax4.set_title('Color cycle from colormap (local)')
Answered By: Louis-Amand

TLDR:

For those looking to retrieving single color from cmap, use cmap.colors[INDEX] instead of cmap[INDEX].


Following on how to create cmap from this answer:

import matplotlib.pyplot as plt
import matplotlib
import numpy as np

cmap = matplotlib.colors.ListedColormap(matplotlib.cm.get_cmap("Set3").colors[:5])
plt.scatter(np.random.uniform(0, 10, 50), np.random.uniform(0, 10, 50), c=np.random.uniform(0, 10, 50), cmap=cmap)
plt.colorbar()
plt.show()

You can get the color using cmap.colors[INDEX], such as cmap.colors[0].

print(cmap.colors[0])
plt.plot(np.random.uniform(0, 10, 50), color=cmap.colors[0], label=cmap.colors[0])
plt.legend(loc='best')
plt.show()
Answered By: Muhammad Yasirroni
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.