Get matplotlib color cycle state
Accessing the color cycle iterator
There's no "user-facing" (a.k.a. "public") method to access the underlying iterator, but you can access it through "private" (by convention) methods. However, you'd can't get the state of an iterator
without changing it.
Setting the color cycle
Quick aside: You can set the color/property cycle in a variety of ways (e.g. ax.set_color_cycle
in versions <1.5 or ax.set_prop_cycler
in >=1.5). Have a look at the example here for version 1.5 or greater, or the previous style here.
Accessing the underlying iterator
However, while there's no public-facing method to access the iterable, you can access it for a given axes object (ax
) through the _get_lines
helper class instance. ax._get_lines
is a touch confusingly named, but it's the behind-the-scenes machinery that allows the plot
command to process all of the odd and varied ways that plot
can be called. Among other things, it's what keeps track of what colors to automatically assign. Similarly, there's ax._get_patches_for_fill
to control cycling through default fill colors and patch properties.
At any rate, the color cycle iterable is ax._get_lines.color_cycle
for lines and ax._get_patches_for_fill.color_cycle
for patches. On matplotlib >=1.5, this has changed to use the cycler
library, and the iterable is called prop_cycler
instead of color_cycle
and yields a dict
of properties instead of only a color.
All in all, you'd do something like:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
color_cycle = ax._get_lines.color_cycle
# or ax._get_lines.prop_cycler on version >= 1.5
# Note that prop_cycler cycles over dicts, so you'll want next(cycle)['color']
You can't view the state of an iterator
However, this object is a "bare" iterator
. We can easily get the next item (e.g. next_color = next(color_cycle)
, but that means that the next color after that is what will be plotted. By design, there's no way to get the current state of an iterator without changing it.
In v1.5
or greater, it would be nice to get the cycler
object that's used, as we could infer its current state. However, the cycler
object itself isn't accessible (publicly or privately) anywhere. Instead, only the itertools.cycle
instance created from the cycler
object is accessible. Either way, there's no way to get to the underlying state of the color/property cycler.
Match the color of the previously plotted item instead
In your case, it sounds like you're wanting to match the color of something that was just plotted. Instead of trying to determine what the color/property will be, set the color/etc of your new item based on the properties of what's plotted.
For example, in the case you described, I'd do something like this:
import matplotlib.pyplot as plt
import numpy as np
def custom_plot(x, y, **kwargs):
ax = kwargs.pop('ax', plt.gca())
base_line, = ax.plot(x, y, **kwargs)
ax.fill_between(x, 0.9*y, 1.1*y, facecolor=base_line.get_color(), alpha=0.5)
x = np.linspace(0, 1, 10)
custom_plot(x, x)
custom_plot(x, 2*x)
custom_plot(x, -x, color='yellow', lw=3)
plt.show()
It's not the only way, but its cleaner than trying to get the color of the plotted line before-hand, in this case.
Here's a way that works in 1.5 which will hopefully be future-proof as it doesn't rely on methods prepended with underscores:
colors = plt.rcParams["axes.prop_cycle"].by_key()["color"]
This will give you a list of the colors defined in order for the present style.
Note: In the latest versions of matplotlib (>= 1.5) _get_lines
has changed. You now need to use next(ax._get_lines.prop_cycler)['color']
in Python 2 or 3 (or ax._get_lines.prop_cycler.next()['color']
in Python 2) to get the next color from the color cycle.
Wherever possible use the more direct approach shown in the lower part of @joe-kington's answer. As _get_lines
is not API-facing it might change again in a not backward compatible manner in the future.
Sure, this will do it.
#rainbow
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0,2*np.pi)
ax= plt.subplot(1,1,1)
ax.plot(np.sin(x))
ax.plot(np.cos(x))
rainbow = ax._get_lines.color_cycle
print rainbow
for i, color in enumerate(rainbow):
if i<10:
print color,
Gives:
<itertools.cycle object at 0x034CB288>
r c m y k b g r c m
Here is the itertools function that matplotlib uses itertools.cycle
Edit: Thanks for the comment, it seems that it is not possible to copy an iterator. An idea would be to dump a full cycle and keep track of which value you are using, let me get back on that.
Edit2: Allright, this will give you the next color and make a new iterator that behaves as if next was not called. This does not preserve the order of coloring, just the next color value, I leave that to you.
This gives the following output, notice that steepness in the plot corresponds to index, eg first g is the bottomest graph and so on.
#rainbow
import matplotlib.pyplot as plt
import numpy as np
import collections
import itertools
x = np.linspace(0,2*np.pi)
ax= plt.subplot(1,1,1)
def create_rainbow():
rainbow = [ax._get_lines.color_cycle.next()]
while True:
nextval = ax._get_lines.color_cycle.next()
if nextval not in rainbow:
rainbow.append(nextval)
else:
return rainbow
def next_color(axis_handle=ax):
rainbow = create_rainbow()
double_rainbow = collections.deque(rainbow)
nextval = ax._get_lines.color_cycle.next()
double_rainbow.rotate(-1)
return nextval, itertools.cycle(double_rainbow)
for i in range(1,10):
nextval, ax._get_lines.color_cycle = next_color(ax)
print "Next color is: ", nextval
ax.plot(i*(x))
plt.savefig("SO_rotate_color.png")
plt.show()
Console
Next color is: g
Next color is: c
Next color is: y
Next color is: b
Next color is: r
Next color is: m
Next color is: k
Next color is: g
Next color is: c