.. currentmodule:: cycler
.. only:: html :Version: |version| :Date: |today|
Docs | https://matplotlib.org/cycler |
PyPI | https://pypi.python.org/pypi/Cycler |
GitHub | https://github.com/matplotlib/cycler |
:py:mod:`cycler` API
.. autosummary:: :toctree: generated/ cycler Cycler concat
The public API of :py:mod:`cycler` consists of a class Cycler, a factory function :func:`cycler`, and a concatenation function :func:`concat`. The factory function provides a simple interface for creating 'base' Cycler objects while the class takes care of the composition and iteration logic.
A single entry Cycler object can be used to easily cycle over a single style. To create the Cycler use the :py:func:`cycler` function to link a key/style/keyword argument to series of values. The key must be hashable (as it will eventually be used as the key in a :obj:`dict`).
.. ipython:: python from __future__ import print_function from cycler import cycler color_cycle = cycler(color=['r', 'g', 'b']) color_cycle
The Cycler knows its length and keys:
.. ipython:: python len(color_cycle) color_cycle.keys
Iterating over this object will yield a series of :obj:`dict` objects keyed on the label
.. ipython:: python for v in color_cycle: print(v)
Cycler objects can be passed as the argument to :func:`cycler` which returns a new Cycler with a new label, but the same values.
.. ipython:: python cycler(ec=color_cycle)
Iterating over a Cycler results in the finite list of entries, to get an infinite cycle, call the Cycler object (a-la a generator)
.. ipython:: python cc = color_cycle() for j, c in zip(range(5), cc): print(j, c)
A single Cycler can just as easily be replaced by a single for
loop. The power of Cycler objects is that they can be composed to easily
create complex multi-key cycles.
Equal length Cyclers with different keys can be added to get the 'inner' product of two cycles
.. ipython:: python lw_cycle = cycler(lw=range(1, 4)) wc = lw_cycle + color_cycle
The result has the same length and has keys which are the union of the two input Cycler's.
.. ipython:: python len(wc) wc.keys
and iterating over the result is the zip of the two input cycles
.. ipython:: python for s in wc: print(s)
As with arithmetic, addition is commutative
.. ipython:: python lw_c = lw_cycle + color_cycle c_lw = color_cycle + lw_cycle for j, (a, b) in enumerate(zip(lw_c, c_lw)): print('({j}) A: {A!r} B: {B!r}'.format(j=j, A=a, B=b))
For convenience, the :func:`cycler` function can have multiple key-value pairs and will automatically compose them into a single Cycler via addition
.. ipython:: python wc = cycler(c=['r', 'g', 'b'], lw=range(3)) for s in wc: print(s)
Any pair of Cycler can be multiplied
.. ipython:: python m_cycle = cycler(marker=['s', 'o']) m_c = m_cycle * color_cycle
which gives the 'outer product' of the two cycles (same as :func:`itertools.product` )
.. ipython:: python len(m_c) m_c.keys for s in m_c: print(s)
Note that unlike addition, multiplication is not commutative (like matrices)
.. ipython:: python c_m = color_cycle * m_cycle for j, (a, b) in enumerate(zip(c_m, m_c)): print('({j}) A: {A!r} B: {B!r}'.format(j=j, A=a, B=b))
Cyclers can also be multiplied by integer values to increase the length.
.. ipython:: python color_cycle * 2 2 * color_cycle
Cycler objects can be concatenated either via the :py:meth:`Cycler.concat` method
.. ipython:: python color_cycle.concat(color_cycle)
or the top-level :py:func:`concat` function
.. ipython:: python from cycler import concat concat(color_cycle, color_cycle)
Cycles can be sliced with :obj:`slice` objects
.. ipython:: python color_cycle[::-1] color_cycle[:2] color_cycle[1:]
to return a sub-set of the cycle as a new Cycler.
To inspect the values of the transposed Cycler use the Cycler.by_key method:
.. ipython:: python c_m.by_key()
This dict can be mutated and used to create a new Cycler with the updated values
.. ipython:: python bk = c_m.by_key() bk['color'] = ['green'] * len(c_m) cycler(**bk)
We can use Cycler instances to cycle over one or more kwarg
to
~matplotlib.axes.Axes.plot :
.. plot:: :include-source: from cycler import cycler from itertools import cycle fig, (ax1, ax2) = plt.subplots(1, 2, tight_layout=True, figsize=(8, 4)) x = np.arange(10) color_cycle = cycler(c=['r', 'g', 'b']) for i, sty in enumerate(color_cycle): ax1.plot(x, x*(i+1), **sty) for i, sty in zip(range(1, 5), cycle(color_cycle)): ax2.plot(x, x*i, **sty)
.. plot:: :include-source: from cycler import cycler from itertools import cycle fig, (ax1, ax2) = plt.subplots(1, 2, tight_layout=True, figsize=(8, 4)) x = np.arange(10) color_cycle = cycler(c=['r', 'g', 'b']) ls_cycle = cycler('ls', ['-', '--']) lw_cycle = cycler('lw', range(1, 4)) sty_cycle = ls_cycle * (color_cycle + lw_cycle) for i, sty in enumerate(sty_cycle): ax1.plot(x, x*(i+1), **sty) sty_cycle = (color_cycle + lw_cycle) * ls_cycle for i, sty in enumerate(sty_cycle): ax2.plot(x, x*(i+1), **sty)
It can be useful to associate a given label with a style via dictionary lookup and to dynamically generate that mapping. This can easily be accomplished using a ~collections.defaultdict
.. ipython:: python from cycler import cycler as cy from collections import defaultdict cyl = cy('c', 'rgb') + cy('lw', range(1, 4))
To get a finite set of styles
.. ipython:: python finite_cy_iter = iter(cyl) dd_finite = defaultdict(lambda : next(finite_cy_iter))
or repeating
.. ipython:: python loop_cy_iter = cyl() dd_loop = defaultdict(lambda : next(loop_cy_iter))
This can be helpful when plotting complex data which has both a classification and a label
finite_cy_iter = iter(cyl) styles = defaultdict(lambda : next(finite_cy_iter)) for group, label, data in DataSet: ax.plot(data, label=label, **styles[group])
which will result in every data
with the same group
being plotted with
the same style.
A :obj:`ValueError` is raised if unequal length Cyclers are added together
.. ipython:: python :okexcept: cycler(c=['r', 'g', 'b']) + cycler(ls=['-', '--'])
or if two cycles which have overlapping keys are composed
.. ipython:: python :okexcept: color_cycle = cycler(c=['r', 'g', 'b']) color_cycle + color_cycle
When plotting more than one line it is common to want to be able to cycle over one or more artist styles. For simple cases than can be done with out too much trouble:
.. plot:: :include-source: fig, ax = plt.subplots(tight_layout=True) x = np.linspace(0, 2*np.pi, 1024) for i, (lw, c) in enumerate(zip(range(4), ['r', 'g', 'b', 'k'])): ax.plot(x, np.sin(x - i * np.pi / 4), label=r'$\phi = {{{0}}} \pi / 4$'.format(i), lw=lw + 1, c=c) ax.set_xlim([0, 2*np.pi]) ax.set_title(r'$y=\sin(\theta + \phi)$') ax.set_ylabel(r'[arb]') ax.set_xlabel(r'$\theta$ [rad]') ax.legend(loc=0)
However, if you want to do something more complicated:
.. plot:: :include-source: fig, ax = plt.subplots(tight_layout=True) x = np.linspace(0, 2*np.pi, 1024) for i, (lw, c) in enumerate(zip(range(4), ['r', 'g', 'b', 'k'])): if i % 2: ls = '-' else: ls = '--' ax.plot(x, np.sin(x - i * np.pi / 4), label=r'$\phi = {{{0}}} \pi / 4$'.format(i), lw=lw + 1, c=c, ls=ls) ax.set_xlim([0, 2*np.pi]) ax.set_title(r'$y=\sin(\theta + \phi)$') ax.set_ylabel(r'[arb]') ax.set_xlabel(r'$\theta$ [rad]') ax.legend(loc=0)
the plotting logic can quickly become very involved. To address this and allow
easy cycling over arbitrary kwargs
the Cycler class, a composable keyword
argument iterator, was developed.