File: index.rst

package info (click to toggle)
python-cycler 0.12.1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 184 kB
  • sloc: python: 544; makefile: 156
file content (405 lines) | stat: -rw-r--r-- 8,731 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
.. currentmodule:: cycler

===================
 Composable cycles
===================

.. 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.


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

Base
----

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)


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

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.

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

   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)


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




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

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

.. ipython:: python

   color_cycle * 2
   2 * color_cycle


Concatenation
~~~~~~~~~~~~~

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


Slicing
-------

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

Inspecting the `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)


Examples
--------

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)


Persistent Cycles
-----------------

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.

Exceptions
----------

A :obj:`ValueError` is raised if unequal length `Cycler`\s 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


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 `Cycler` class, a composable keyword
argument iterator, was developed.