use matplotlib color map for color cycle

Posted on

Question :

use matplotlib color map for color cycle

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

||

Answer #1:

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

Answer #2:

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

Answer #3:

“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()

Leave a Reply

Your email address will not be published.