-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
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
Cycler #4258
Conversation
Helper-class to compose arbitrary, complex, property cycles. An alternate implementation + documentation forth coming.
I think I like this version better. More classes, but less implicit magic.
Also note that a = SingleCycler('c', ['r', 'purple', 'lime'])
b = SingleCycler('lw', [1, 5])
c = SingleCycler('markevery', [10, 50])
m = SingleCycler('marker', 'o')
fig, ax_lst = plt.subplots(2, 2)
def cycle_demo(ax, cycler):
c_len = len(cycler)
th = np.linspace(0, 2*np.pi, 1024)
y = np.sin(th)
for j, p in zip(range(2*c_len), cycler):
ax.plot(th, j + y, **p)
for ax, cc in zip(ax_lst.ravel(), [b*a, a*b, (c*m)*a, a*(c*m)]):
cycle_demo(ax, cc) |
return len(list(self.finite_iter())) | ||
|
||
|
||
class _base_cycler(object): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might make sense to make this a fully public base class. The advantage would be that then for either a SingleCycler or a CompoundCycler one could test with isinstance(obj, CyclerBase)
. This would be getting closer to your first implementation in which there is just a single Cycler
class, which is appealing because it is closer to the way one would think about using these objects.
I'm favoring the |
Also, if we do want to have in-place modification ( |
- make class method private - provide simple user facing factory method
- make _base_cycler public as BaseCycler
- added `__imul__` and `__iadd__` - added paranoia copy functions to make sure that child Cyclers can't be changed after they are composed (which would mess up the key tracking logic)
Don't open the chance for outside users to mess with contents of the `_keys` attribute.
- if multi-property cycler, raise - if already the Cycler we want, return copy - if cycler of a different property, extract the values and continue This behavior follows the pattern of numpy to copy in `np.array`.
Some of these copies should maybe be deep copies if we feel like being really paranoid. |
both of the above examples work with |
Yes, I really like this. +1 or the |
Return a list of the style dictionaries yielded by this Cycler
If we know the length of the left and right and the op, we can directly calculate what the length will be with out actually iterating over everything.
attn @dchabot |
@matplotlib/developers We now have two proposals for how to overhaul cycling, this PR and #4314 . I am (trying not to be biased) in favor of this implementation because it provides composition and is a bit less brittle. Can we get a consensus on which way to go before I spend time to document this and shoe-horn it into the existing cycle infrastructure? Is it worth merging this as-is (maybe with some examples in the sphinx docs) without touching |
I'm in favor of your proposal. I think its generality is not only elegant, but will prove useful. |
I'm +1 here. I like that this is pretty much just a kwarg generator, and can therefore easily scale to other plots, artists, etc. (both in and outside MPL). I also 2nd Eric's notion that this PR should go by itself and to something separate to change |
I'm very much +1 for this. It gives a nice clean and concise way to do something that can be currently quite faffy. Particularly like the construction using How would the single-class interface look for compound definitions?
|
@tacaswell - I have totally neglected my mpl duties recently, so sorry you've not had and feedback from me 😞. First things first, I really like it. I think it will help in several situations within the mpl codebase, and will valuable to users who want the ability to define n colors and m arbitrary other styles, and combine them together. A few high level review points:
|
The other option for the I like the |
and I think I am on board with splitting this off into it's own package. My current plan is to keep the development here and then split it off later (before we tag a version of mpl which depends on it) and make sure it hist pypi + conda before the next version of mpl. |
@pelson Starting to look at implemeting the slicing, I am not sure how do it for compound cycles. Ex (cycler('c', ['r', 'g', 'b']) * cycler('lw', [1, 2]))[:5] can not be represented as a multiplication so would have to be decomposed into cycler('c', ['r', 'g', 'b', 'r', 'g']) + cycler('lw', [1, 2, 1, 2, 1]) which seems a bit silly, but now that I type this out does not seem so unreasonable. |
Covers everything but the repr Tests '+' behavior which may be changed.
It is far simpler for users that want infinite cycling to wrap the ``Cycler`` in a call to `itertools.cycle` than it is for the user that wants a finite iterator to sort out how to get the finite version. This simplifies the API and makes it a bit more guessable/intuitive.
Add a method to return an equivalent Cycler composed using only addition composition.
Add the ability to multiply a Cycler by an integer to increase the length of the cycle. This has the side effect of simplifying the Cycler to only use addition composition
Implement `__getitem__` for Cycler by broadcasting down to the lists in the transposed data. Raises on trying to use anything else but a slice. Numpy-style fancy slicing might be worth adding. Adding integer slicing is probably of minilmal value.
Now that we have both multiplication and slicing, it is easy to get correct length cyclers to add together
@pelson Added slicing and made |
I added PR against this PR adding an IPython rich display hook. Demo |
ENH : Add a rich display hook to Cycler.
Thanks @danielballan ! |
I have started to split this PR off into it's own repo: https://github.com/tacaswell/cycler I will eventually move it into the matplotlib org once I have fleshed out all extra bits and pieces and people are happy enough with it. |
👍 - thanks @tacaswell. I think we are at the point of closing this PR in favour of a new PR which adds cycler as a dependency, is that right? Is it worth getting other projects on board early so that we can share experiences and ideas? I'm thinking bokeh, vispy etc. |
Yes. I will try to get functional docs on cycler tonight + a 0.1 tag pushed to pypi + travis to give people an easy way to play with it. The goal would be to do 1.0 on cycler a week before mpl 1.5. Leaving this branch existing for the time being just in case. |
As discussed in several places the problem of doing arbitrary style cycles over arbitrary parameters and inputs is a bit vexing. Inspired by
Holoviews
and sensible composable objects, I have implementedCyclers
which give an easy API to construct these cycles.There are two types of composition
+
which is an 'inner' product implemented by zipping the two cycles together and*
which is an 'outer' product which is implemented withitertools.product
.There are two implementation (one of which will be deleted), but am a bit torn on which one I like better. The
Cycler
class is a stand alone class which handles both 'single' cycles and 'compound' cycles via aclassmethod
constructor and some implicit magic. The other is a set of three classes {_base_cycler
,SingleCycler
, andCompoundCycler
} which handle respectively the composition logic, 'single' cycles and compound cycles. I am leaning towards the 3 class implementation because it has less implicit magic and users don't have to use a class method to get useful objects.In both cases, the objects have
len
,__iter__
(which is infinite),finite_iter
(which in__iter__
, but finite), andkeys
(which says what properties the cycle is over).The composition fails if the same property appears on both sides.
These are useful on their own, but I think replacing the color cycle on
Axes
with one of these would be a major usability gain.I am not wedded to any of the class names.
Documentation, examples, and testing will come later if people think this is a good idea. Should probably also add an in-place version of
+
and*
as well.attn @WeatherGod @efiring @mdboom @pelson @danielballan