Skip to content
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

Allow getitem from a cycler #27

Open
tritemio opened this issue Jan 26, 2016 · 4 comments
Open

Allow getitem from a cycler #27

tritemio opened this issue Jan 26, 2016 · 4 comments
Milestone

Comments

@tritemio
Copy link

It would be useful to add __getitem__ functionality to cycler.

The use case is a "crowded" for loop where you want to get the cycler style without using a loop variable. Something like:

data_cycler = cycler('alpha', (0.2, 0.4, 0.6)) + cycler('color', ('r', 'g', 'b'))
fit_cycler = cycler('color', ('k', 'm', 'orange'))

for i, (data, fit) in enumerate(zip(data_list, fit_list)):
    ...
    plt.plot('x', 'y', data=data, data_cycler[i])
    plt.plot('x', 'y', data=data, fit_cycler[i])
    ...

In this case the index i is computed anyway, so using it for the cyclers would be handy, instead of adding the cycler inside zip().

Ideally mycycler[i] should return the item with index i % len(mycycler).

I haven't looked at the code yet. Would this be possible? Would a PR be welcome?

@tacaswell
Copy link
Member

Cycler already has some support for __getitem__ which lets you slice it, ex cy2 = cy[::2] but it does not work on a single element. (http://matplotlib.org/cycler/#slicing)

I am not convinced that using index is clearer than using zip, somethig like

styles = zip(data_cycler, fit_cycler)
values = zip(data_list, fit_list)
for vals, cys in zip(styles, values):
    for v, c in zip(vals, cys):
        ax.plot('x', 'y', data=v, **c)

is a bit clearer (and avoids what I assume is the typo where you meant data=fit in the second plot)

To do it efficiently you would probably have to hold a list of the transposed cycler (ex list(cy)), right now when you iterate over a cycler everything is lazy. It might be simpler to in these cases hold a listified version of the cycler, the main win of cycler is the composition, not necessarily it's use as a special snow-flake object.

That said, we would have use of this functionality upstream in mpl.

I am -0 on this, I won't merge it, but will not block someone else from doing so.

@tritemio
Copy link
Author

@tacaswell yep, I fixed the typo in the example. Anyway, it was just an example. The point is, there are legitimate situations where indexing a cycler can be convenient.

Another example more similar to what I'm doing ATM. I keep a few datatsets in dictionaries whose keys (the same for all dict) are parameters. Would be nice to be able to plot them like this (let's pretend I still have fit and data curves):

for i, (param, data) in enumerate(sorted(data_dict.items())):
    ax.plot('x', 'y', data=data, **data_cycler[i])
    ax.plot(data.x, model_func(data.x **fit_dict[param]), **fit_cycler[i])
    ax.text(1, 0.9 - i*0.1, 'parameter = %d, tau = %.1fs' % (param, fit_dict[param]['tau']))

instead of:

for i, (param, datasty, fitsty) in enumerate(zip(sorted(data_dict), data_cycler, fit_cycler)):
    ax.plot('x', 'y', data=data_dict[param], **datasty)
    ax.plot(data.x, model_func(data.x **fit_dict[param]), **fitsty)
    ax.text(1, 0.9 - i*0.1, 'parameter = %d, tau = %.1fs' % (param, fit_dict[param]['tau']))

All these "style" variables in the latter example are just visual noise, distracting from the important thing: the data I'm looping upon.

@tacaswell
Copy link
Member

The example at https://github.com/matplotlib/cycler/blob/master/doc/source/index.rst#persistent-cycles (which needs to get rebuilt and pushed to the main page) I think captures your second use case in a nicer way.

@tacaswell tacaswell added wontfix and removed wontfix labels Jun 4, 2016
@tacaswell tacaswell added this to the v1.0 milestone Jun 4, 2016
@tacaswell
Copy link
Member

I think that the defaultdict construction addresses this use case.

Do we want wrap it in a function and ship it with cycler? I am leaning towards yes.

tacaswell added a commit to tacaswell/cycler that referenced this issue Jun 5, 2016
Function to generate a default dict with a cycler bound to it.  This
functionality is currently in the documentation as an example.

Closes matplotlib#27
tacaswell added a commit to tacaswell/cycler that referenced this issue Jun 6, 2016
Function to generate a default dict with a cycler bound to it.  This
functionality is currently in the documentation as an example.

Closes matplotlib#27
@QuLogic QuLogic modified the milestones: v0.10, v1.0 Oct 25, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants