Thanks to visit codestin.com
Credit goes to github.com

Skip to content

ENH: add by_key #26

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

Merged
merged 4 commits into from
Dec 13, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
33 changes: 20 additions & 13 deletions cycler.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ def _from_iter(cls, label, itr):
def __getitem__(self, key):
# TODO : maybe add numpy style fancy slicing
if isinstance(key, slice):
trans = self._transpose()
trans = self.by_key()
return reduce(add, (_cycler(k, v[key])
for k, v in six.iteritems(trans)))
else:
Expand Down Expand Up @@ -255,7 +255,7 @@ def __mul__(self, other):
if isinstance(other, Cycler):
return Cycler(self, other, product)
elif isinstance(other, int):
trans = self._transpose()
trans = self.by_key()
return reduce(add, (_cycler(k, v*other)
for k, v in six.iteritems(trans)))
else:
Expand Down Expand Up @@ -346,17 +346,21 @@ def _repr_html_(self):
output += "</table>"
return output

def _transpose(self):
"""
Internal helper function which iterates through the
styles and returns a dict of lists instead of a list of
dicts. This is needed for multiplying by integers and
for __getitem__
def by_key(self):
"""Values by key

This returns the transposed values of the cycler. Iterating
over a `Cycler` yields dicts with a single value for each key,
this method returns a `dict` of `list` which are the values
for the given key.

The returned value can be used to create an equivalent `Cycler`
using only `+`.

Returns
-------
trans : dict
dict of lists for the styles
transpose : dict
dict of lists of the values for each key.
"""

# TODO : sort out if this is a bottle neck, if there is a better way
Expand All @@ -371,6 +375,9 @@ def _transpose(self):
out[k].append(d[k])
return out

# for back compatibility
_transpose = by_key
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this, since it was private to begin with?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was 50/50 on this. I was thinking of being semi-evil and using the private _transpose in mpl so that it will work with current tagged version of cycler so we don't have to do a full release cycle on cycler before implementing the key introspection on mpl.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah -- that seems like more than a good enough reason.


def simplify(self):
"""Simplify the Cycler

Expand All @@ -386,7 +393,7 @@ def simplify(self):
# ((a + b) + (c + d))
# I would believe that there is some performance implications

trans = self._transpose()
trans = self.by_key()
return reduce(add, (_cycler(k, v) for k, v in six.iteritems(trans)))

def concat(self, other):
Expand Down Expand Up @@ -453,8 +460,8 @@ def concat(left, right):

raise ValueError(msg)

_l = left._transpose()
_r = right._transpose()
_l = left.by_key()
_r = right.by_key()
return reduce(add, (_cycler(k, _l[k] + _r[k]) for k in left.keys))


Expand Down
20 changes: 20 additions & 0 deletions doc/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,26 @@ Cycles can be sliced with :obj:`slice` objects

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

Expand Down
32 changes: 32 additions & 0 deletions test_cycler.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
assert_raises, assert_true)
from itertools import product, cycle, chain
from operator import add, iadd, mul, imul
from collections import defaultdict


def _cycler_helper(c, length, keys, values):
Expand Down Expand Up @@ -95,6 +96,8 @@ def test_failures():
assert_raises(ValueError, iadd, c1, c2)
assert_raises(ValueError, mul, c1, c2)
assert_raises(ValueError, imul, c1, c2)
assert_raises(TypeError, iadd, c2, 'aardvark')
assert_raises(TypeError, imul, c2, 'aardvark')

c3 = cycler(ec=c1)

Expand Down Expand Up @@ -265,6 +268,8 @@ def test_eq():
yield _eq_test_helper, a, c, False
d = cycler(c='ymk')
yield _eq_test_helper, b, d, False
e = cycler(c='orange')
yield _eq_test_helper, b, e, False


def test_cycler_exceptions():
Expand Down Expand Up @@ -296,3 +301,30 @@ def test_concat_fail():
b = cycler('b', range(3))
assert_raises(ValueError, concat, a, b)
assert_raises(ValueError, a.concat, b)


def _by_key_helper(cy):
res = cy.by_key()
target = defaultdict(list)
for sty in cy:
for k, v in sty.items():
target[k].append(v)

assert_equal(res, target)


def test_by_key_add():
input_dict = dict(c=list('rgb'), lw=[1, 2, 3])
cy = cycler(c=input_dict['c']) + cycler(lw=input_dict['lw'])
res = cy.by_key()
assert_equal(res, input_dict)
yield _by_key_helper, cy


def test_by_key_mul():
input_dict = dict(c=list('rg'), lw=[1, 2, 3])
cy = cycler(c=input_dict['c']) * cycler(lw=input_dict['lw'])
res = cy.by_key()
assert_equal(input_dict['lw'] * len(input_dict['c']),
res['lw'])
yield _by_key_helper, cy