Matplotlib Plot Lines with Colors Through Colormap
Asked Answered
S

5

74

I am plotting multiple lines on a single plot and I want them to run through the spectrum of a colormap, not just the same 6 or 7 colors. The code is akin to this:

for i in range(20):
     for k in range(100):
          y[k] = i*x[i]
     plt.plot(x,y)
plt.show()

Both with colormap "jet" and another that I imported from seaborn, I get the same 7 colors repeated in the same order. I would like to be able to plot up to ~60 different lines, all with different colors.

Sorry answered 5/7, 2016 at 16:49 Comment(1)
Does this answer your question? use matplotlib color map for color cycleNocti
S
120

The Matplotlib colormaps accept an argument (0..1, scalar or array) which you use to get colors from a colormap. For example:

col = pl.cm.jet([0.25,0.75])    

Gives you an array with (two) RGBA colors:

array([[ 0. , 0.50392157, 1. , 1. ], [ 1. , 0.58169935, 0. , 1. ]])

You can use that to create N different colors:

import numpy as np
import matplotlib.pyplot as pl

x = np.linspace(0, 2*np.pi, 64)
y = np.cos(x) 

pl.figure()
pl.plot(x,y)

n = 20
colors = pl.cm.jet(np.linspace(0,1,n))

for i in range(n):
    pl.plot(x, i*y, color=colors[i])

enter image description here

Stableboy answered 6/7, 2016 at 8:2 Comment(5)
Can someone please point me to where in the docs this idea of implementation exists? I am finding learning to use cmaps in matplotlib via the docs simply impossible. I'm constantly coming back to SO to find answers to the dead ends. There is no shortage of documentation on how to make them, but I've found nothing like this answer on how to actually use them in a plot.Bergmans
There is no option (that I know of) that lets you "color all my plot() lines cycling through this or that cmap", so you will have to use one of the solutions from this question. For the functions that do accept colormaps (like e.g. scatter, pcolormesh, etc) it is perfectly well documented.Stableboy
do you know why this is? I also found the docs on scatter and other plots, but any idea why is this missing for the standard Line2D plot?Bergmans
With Line2D instead of separate plot() calls, Matplotlib could indeed color the lines according to some specified cmap. If you think it would be useful, you can always issue a feature request here: github.com/matplotlib/matplotlib/issuesStableboy
PyLab is the combination of the PyPlot and NumPy namespaces into a single one (to avoid having to import two namespaces). Using Pylab is now discouraged. For this example you can use import matplotlib.pyplot as pl instead of import matplotlib.pylab as plDisrelish
N
38

Bart's solution is nice and simple but has two shortcomings.

  1. plt.colorbar() won't work in a nice way because the line plots aren't mappable (compared to, e.g., an image)

  2. It can be slow for large numbers of lines due to the for loop (though this is maybe not a problem for most applications?)

These issues can be addressed by using LineCollection. However, this isn't too user-friendly in my (humble) opinion. There is an open suggestion on GitHub for adding a multicolor line plot function, similar to the plt.scatter(...) function.

Here is a working example I was able to hack together

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection

def multiline(xs, ys, c, ax=None, **kwargs):
    """Plot lines with different colorings

    Parameters
    ----------
    xs : iterable container of x coordinates
    ys : iterable container of y coordinates
    c : iterable container of numbers mapped to colormap
    ax (optional): Axes to plot on.
    kwargs (optional): passed to LineCollection

    Notes:
        len(xs) == len(ys) == len(c) is the number of line segments
        len(xs[i]) == len(ys[i]) is the number of points for each line (indexed by i)

    Returns
    -------
    lc : LineCollection instance.
    """

    # find axes
    ax = plt.gca() if ax is None else ax

    # create LineCollection
    segments = [np.column_stack([x, y]) for x, y in zip(xs, ys)]
    lc = LineCollection(segments, **kwargs)

    # set coloring of line segments
    #    Note: I get an error if I pass c as a list here... not sure why.
    lc.set_array(np.asarray(c))

    # add lines to axes and rescale 
    #    Note: adding a collection doesn't autoscalee xlim/ylim
    ax.add_collection(lc)
    ax.autoscale()
    return lc

Here is a very simple example:

xs = [[0, 1],
      [0, 1, 2]]
ys = [[0, 0],
      [1, 2, 1]]
c = [0, 1]

lc = multiline(xs, ys, c, cmap='bwr', lw=2)

Produces:

Example 1

And something a little more sophisticated:

n_lines = 30
x = np.arange(100)

yint = np.arange(0, n_lines*10, 10)
ys = np.array([x + b for b in yint])
xs = np.array([x for i in range(n_lines)]) # could also use np.tile

colors = np.arange(n_lines)

fig, ax = plt.subplots()
lc = multiline(xs, ys, yint, cmap='bwr', lw=2)

axcb = fig.colorbar(lc)
axcb.set_label('Y-intercept')
ax.set_title('Line Collection with mapped colors')

Produces:

enter image description here

Hope this helps!

Nightlong answered 25/4, 2018 at 18:51 Comment(0)
C
27

An anternative to Bart's answer, in which you do not specify the color in each call to plt.plot is to define a new color cycle with set_prop_cycle. His example can be translated into the following code (I've also changed the import of matplotlib to the recommended style):

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 2*np.pi, 64)
y = np.cos(x) 

n = 20
ax = plt.axes()
ax.set_prop_cycle('color',[plt.cm.jet(i) for i in np.linspace(0, 1, n)])

for i in range(n):
    plt.plot(x, i*y)
Chapiter answered 24/9, 2018 at 9:10 Comment(0)
C
6

If you are using continuous color pallets like brg, hsv, jet or the default one then you can do like this:

color = plt.cm.hsv(r) # r is 0 to 1 inclusive

Now you can pass this color value to any API you want like this:

line = matplotlib.lines.Line2D(xdata, ydata, color=color)
Chet answered 10/1, 2019 at 1:11 Comment(1)
This approach seems easier than the previous ones!Agreeable
I
3

This approach seems to me like the most concise, user-friendly and does not require a loop to be used. It does not rely on user-made functions either.

import numpy as np
import matplotlib.pyplot as plt

# make 5 lines
n_lines = 5
x = np.arange(0, 2).reshape(-1, 1)
A = np.linspace(0, 2, n_lines).reshape(1, -1)
Y = x @ A

# create colormap
cm = plt.cm.bwr(np.linspace(0, 1, n_lines))

# plot
ax = plt.subplot(111)
ax.set_prop_cycle('color', list(cm))
ax.plot(x, Y)
plt.show()

Resulting figure here

Inductance answered 2/3, 2022 at 16:30 Comment(1)
Thank you! This was by far the simplest answer.Vengeful

© 2022 - 2025 — McMap. All rights reserved.