Skip to content

Cycler #4258

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 25 commits into from
Closed

Cycler #4258

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
5e23ba2
WIP : first pass at adding a Cycler
tacaswell Mar 21, 2015
5539698
WIP : version 2 of cycler classes
tacaswell Mar 21, 2015
958b0b6
MNT : tweak Cycler API
tacaswell Mar 22, 2015
6b0696d
MNT : Cycler-family API tweaks
tacaswell Mar 22, 2015
2f57756
ENH : added in-place operations
tacaswell Mar 22, 2015
4aef726
ENH : added verbose repr
tacaswell Mar 22, 2015
105fcd6
MNT : make keys property return a copy
tacaswell Mar 22, 2015
a0c387a
MNT : simplify repr
tacaswell Mar 22, 2015
2355776
ENH : make `cycler` deal with Cycler input
tacaswell Mar 22, 2015
74e61e1
MNT : remove second version of Cycler classes
tacaswell Mar 23, 2015
e6910e4
ENH : add to_list method
tacaswell Mar 23, 2015
1532863
PRF : make __len__ more efficient
tacaswell Mar 23, 2015
d5ec6d8
TST : first round of tests for Cycler
tacaswell Apr 22, 2015
0da274f
API : make __iter__ be finite
tacaswell Apr 27, 2015
52b5dfd
DOC/WIP : first draft a docs for cycler
tacaswell Apr 27, 2015
d4195f7
WIP : more edits to docs
tacaswell May 5, 2015
3e8bd8e
ENH : add simplify
tacaswell May 6, 2015
6fea7a4
ENH : add integer multiplication
tacaswell May 6, 2015
2ffc5e1
TST : test commutativity of cycler addition
tacaswell May 6, 2015
126d7df
ENH : implement `__getitem__` for Cycler
tacaswell May 6, 2015
a56ea63
API : Only allow addition of equal length cycles
tacaswell May 6, 2015
58610f4
DOC : more edits to docs
tacaswell May 6, 2015
c76b976
DOC : more work on documentation
tacaswell May 7, 2015
14df70f
ENH: Add a rich display hook to Cycler.
danielballan May 7, 2015
2600179
Merge pull request #11 from danielballan/cycler-html
tacaswell May 7, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
extensions.append('matplotlib.sphinxext.ipython_console_highlighting')
else:
print("Using IPython's ipython_console_highlighting directive")
extensions.append('IPython.sphinxext.ipython_directive')
extensions.append('IPython.sphinxext.ipython_console_highlighting')

try:
Expand Down
285 changes: 285 additions & 0 deletions doc/users/cycler.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
.. _cycler_guide:
.. currentmodule:: matplotlib.cycler

==========================
Style/kwarg cycler Guide
==========================

`~matplotlib.cycler.Cycler` API
===============================

.. autosummary::
:toctree: generated/

cycler
Cycler


The public API of `Cycler` consists of a class
`~matplotlib.cycler.Cycler` and a factory function
`~matplotlib.cycler.cycler`. The class takes care of the composition
and iteration logic while the function provides a simple interface for
creating 'base' `Cycler` objects.


Motivation
==========


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 `~matplotlib.cycler.Cycler` class, a composable
kwarg iterator, was developed.

`Cycler` Usage
==============

Basic
-----

A 'base' `Cycler` object is somewhat useful and can be used to easily
cycle over a single style. To create a base `Cycler` use the `cycler`
function to link a key/style/kwarg to series of values. The key can be
any hashable object (as it will eventually be used as the key in a `dict`).

.. ipython:: python

from __future__ import print_function
from matplotlib.cycler import cycler


color_cycle = cycler('color', ['r', 'g', 'b'])
color_cycle

The `Cycler` object knows it's length and keys:

.. ipython:: python


len(color_cycle)
color_cycle.keys

Iterating over this object will yield a series of `dicts` keyed on
the key with a single value from the series

.. ipython:: python

for v in color_cycle:
print(v)

Basic `Cycler` objects can be passed as the second argument to `cycler`
which is copy cyclers to a new key.

.. ipython:: python

cycler('ec', color_cycle)


Composition
-----------

A single `Cycler` is not all that useful, they can just as easily be
replaced by a single `for` loop. Fortunately, `Cycler` objects can be
composed to easily generate complex, multi-key cycles.

Addition
~~~~~~~~

Equal length `Cycler` s 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

for a, b in zip(lw_cycle + color_cycle, color_cycle + lw_cycle):
print(a == b)


Multiplication
~~~~~~~~~~~~~~

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.prod` )

.. 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 a, b in zip(c_m, m_c):
print(a, b)




Integer Multiplication
~~~~~~~~~~~~~~~~~~~~~~

`Cycler` s can also be multiplied by integer values to increase the length.

.. ipython:: python

color_cycle * 2
2 * color_cycle



Slicing
-------

Cycles can be sliced with `silce` objects

.. ipython:: python

color_cycle[::-1]
color_cycle[:2]
color_cycle[1:]

to return a sub-set of the cycle as a new `Cycler`. They can also be multiplied
by scalars to make fixed length periodic cycles

Examples
--------


.. plot::
:include-source:

from matplotlib.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, 10), cycle(color_cycle)):
ax2.plot(x, x*i, **sty)


.. plot::
:include-source:

from matplotlib.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, 10), cycle(color_cycle)):
ax2.plot(x, x*i, **sty)


Exceptions
----------


A `ValueError` is raised if unequal length `Cycler` s are added together

.. ipython:: python
:okexcept:

color_cycle + ls_cycle

or if two cycles which have overlapping keys are composed

.. ipython:: python
:okexcept:

color_cycle + color_cycle
color_cycle * color_cycle
5 changes: 1 addition & 4 deletions doc/users/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,10 @@ User's Guide

intro.rst
configuration.rst
cycler.rst
beginner.rst
developer.rst
whats_new.rst
github_stats.rst
license.rst
credits.rst




Loading