From 17f810881cf5d5d0c9b9fdc9cb670ef19077f385 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Mon, 30 Nov 2015 17:24:50 -0500 Subject: [PATCH 1/4] ENH: add by_key Promote `_transpose` to the public method `by_key`. --- cycler.py | 30 +++++++++++++++++------------- doc/source/index.rst | 20 ++++++++++++++++++++ 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/cycler.py b/cycler.py index 0ac2376..09e5f62 100644 --- a/cycler.py +++ b/cycler.py @@ -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: @@ -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: @@ -346,17 +346,21 @@ def _repr_html_(self): output += "" 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 @@ -386,7 +390,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): @@ -453,8 +457,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)) diff --git a/doc/source/index.rst b/doc/source/index.rst index ddf21a7..b62a5ba 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -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 -------- From 120633d8c46f72b8bf9f77169d515bbd780fe726 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Mon, 30 Nov 2015 17:29:07 -0500 Subject: [PATCH 2/4] MNT: maintain back compatibility on private API --- cycler.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cycler.py b/cycler.py index 09e5f62..45d1557 100644 --- a/cycler.py +++ b/cycler.py @@ -375,6 +375,9 @@ def by_key(self): out[k].append(d[k]) return out + # for back compatibility + _transpose = by_key + def simplify(self): """Simplify the Cycler From dee6ecadbb77dc52d791cf891db04a2623af745e Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Tue, 1 Dec 2015 16:24:44 -0500 Subject: [PATCH 3/4] TST: push back to 100% test coverage --- test_cycler.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test_cycler.py b/test_cycler.py index d1dc11e..f2dc043 100644 --- a/test_cycler.py +++ b/test_cycler.py @@ -95,6 +95,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) @@ -265,6 +267,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(): From 714b3466187486d6ee72f4adbad47e0a9a4fb958 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Tue, 1 Dec 2015 16:37:26 -0500 Subject: [PATCH 4/4] TST: add explicit tests for by_key Used internally for slicing so was implicitly covered. --- test_cycler.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test_cycler.py b/test_cycler.py index f2dc043..f65a4cd 100644 --- a/test_cycler.py +++ b/test_cycler.py @@ -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): @@ -300,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