When plotting a series of lines, it's nice to pull a series of colors from a colormap (especially if there's some sequential relationship between lines). In fact, this has been asked (and answered) multiple times on the Matplotlib mailing list (e.g., [1] and [2]) and on StackOverflow (e.g., [3] and [4]).
Sequential curves
Matplotlib makes it really simple to use evenly-spaced intervals of a colormap: you just call the colormap with evenly-spaced values between 0 and 1. For example, let's plot a sinusoidal curve with different phase shifts and use colors from the "cool" colormap to color each curve:
import numpy as np
import matplotlib.pyplot as plt
n_lines = 5
x = np.linspace(0, 10)
phase_shift = np.linspace(0, np.pi, n_lines)
color_idx = np.linspace(0, 1, n_lines)
for i, shift in zip(color_idx, phase_shift):
plt.plot(x, np.sin(x - shift), color=plt.cm.cool(i), lw=3)
plt.show()
Alternatively, you can set the color cycle of the plot axes:
ax = plt.axes()
ax.set_color_cycle([plt.cm.cool(i) for i in np.linspace(0, 1, n_lines)])
for shift in phase_shift:
plt.plot(x, np.sin(x - shift), lw=3)
I prefer this method because the loop definition is a bit simpler (i.e., no call to zip). I've added this method to a utility package called mpltools:
from mpltools import color
ax = plt.axes()
color.cycle_cmap(n_lines, cmap=plt.cm.cool, ax=ax)
for shift in phase_shift:
plt.plot(x, np.sin(x - shift), lw=3)
The cycle_cmap function doesn't really save that much typing, but I find it easier to read, nonetheless. Also, it has some pre-defined limits for colormaps to prevent use of very-light colors (which have low-contrast on white backgrounds).
Choosing colors for a varying parameter
A related concept arises when you want the line color to match a parameter value. This is slightly more complicated because you need to normalize the values you pass to the color map.
Below, I plot a sinusoidal curve with different rates of exponential decay and label those rates with different colors:
pvalues = np.logspace(-1, 0, 4)
pmin = pvalues[0]
pmax = pvalues[-1]
def norm(pval):
return (pval - pmin) / float(pmax - pmin)
x = np.linspace(0, 10)
for pval in pvalues:
y = np.sin(x) * np.exp(-pval * x)
color = plt.cm.YlOrBr(norm(pval))
plt.plot(x, y, 's', color=color)
leg = plt.legend(['%0.1f' % v for v in pvalues], ncol=2)
leg.set_title('decay rate')
plt.show()
To simplify this process, I wrote a simple factory function (function that returns a function) called color_mapper:
from mpltools import color
pvalues = np.logspace(-1, 0, 4)
prange = [pvalues[0], pvalues[-1]]
map_color = color.color_mapper(prange, cmap='YlOrBr')
x = np.linspace(0, 10)
for pval in pvalues:
y = np.sin(x) * np.exp(-pval * x)
plt.plot(x, y, 's', color=map_color(pval))
[1] | http://old.nabble.com/custom-color-cycle-from-cmap-td28177653.html#a28177653 |
[2] | http://www.mail-archive.com/matplotlib-users@lists.sourceforge.net/msg20977.html |
[3] | http://stackoverflow.com/a/4390117/260303 |
[4] | http://stackoverflow.com/a/8391452/260303 |
Comments
comments powered by Disqus