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

Add cycles of differing lengths #41

Open
moi90 opened this issue Feb 14, 2017 · 10 comments
Open

Add cycles of differing lengths #41

moi90 opened this issue Feb 14, 2017 · 10 comments

Comments

@moi90
Copy link

moi90 commented Feb 14, 2017

It would be nice to be able to add cycles of differing lengths whose properties cycle independently.

@tacaswell
Copy link
Member

tacaswell commented Feb 14, 2017

I am not sure we want to support that in the implementation of + as I do not think this is the typical use-case.

You can write a helper function for this pretty easily:

def force_add(cyc_a, cyc_b):
    try:
        return cyc_a + cyc_b
    except ValueError:
        return cyc_a * len(cyc_b) + cyc_b * len(cyc_a)

Might be worth adding a version of this (that uses least-common-multiple?) to the docs?

@zommuter
Copy link

zommuter commented Aug 8, 2017

Another operator could be used for this, maybe & or /? Or ** due to the similarity to *.

edit & is already suggested as concatenation operator in #1.

@tacaswell
Copy link
Member

What is the use-case for this?

@zommuter
Copy link

zommuter commented Aug 9, 2017

As an example, say you want to cycle through three colors [rgb] and four linestyles ["-",":","--","-.",]. That already gives you twelve unique combinations which helps better differentiation of many lines. I think I saw a post of yourself on StackOverflow where you mentioned how one can simply use

cycler('color', list('rbg')*4) + cycler('linestyle', ['-', '--', ':', '-.']*3)

But another operator to automate this would save code. And in contrast to the multiplication of two cyclers you'd cycle through both styles simultaneously instead of sequentially.

@bjmuld
Copy link

bjmuld commented Jan 14, 2019

Yes please. I think this is a very common use-case for publishing where best-practices for reproducibility assume colors can't be distinguished, and so require unique combinations of marker and linestyle type properties.

@bjmuld
Copy link

bjmuld commented Jan 14, 2019

In what use-case is the exception more beneficial?

@tacaswell
Copy link
Member

There is an ambiguity in adding two un-equal length cyclers. @bjmuld is making the case for implicitly extending it so a + b -> a * len(b) + b * len(a) (the actual implementation would be a bit more clever and trim to the LCM).

On the other hand, you could make the argument that a + b -> a[:min(len(a), len(b)] + b[:min(len(a), len(b)) which would trim the cycle to the minimum length of the two cycles you are adding.
This is consistent with what zip does.

We chose to raise because it is not clear (at least to me) which of those two behaviors is the "expected" one. On one hand, there is clearly a use for the LCM version, but on other hand the min-length version is consistent with zip. By raising cycler is telling the user "you asked me to do something ambiguous, please clarify".

Maybe the LCM version is a candidate for @?

@timhoffm
Copy link
Member

Actually, it depends on the intention if you want MIN or LCM.

a) Adding a secondary property -> MIN
Assume you have a line a line plot with color=['r', 'g', 'b'] and you want to add a line style to make sure the lines can still be distinguished in black/white. And assume that you add linestyle=['-', '--']. LCM would draw the third line 'b-' while MIN would draw the third line 'r-' as the first line. In the LCM case you might not notice that linestyle is the same when looking at the colored plot. MIN is better here as it wouldn't allow same secondary properties (ls) with different primary properties (color).

b) Creating more variety -> LCM
Actually, I don't see a usecase for LCM. More variety and the example mentioned above are best handled by the product operator cycler(color=['r', 'g', 'b']) * cycler(linestyle=['-', '--', ':', '-.']).

When really thinking about it, LCM is a bit strange: We want to change both properties synchronously for every element (without the synchronous requirement we can simply use *). This implies a certain relation between the i-th properties. But then after one cycle we don't care about the initial relation anymore.

I would be fine with implementing the zip-like MIN behavior for +.

@bjmuld
Copy link

bjmuld commented Jan 15, 2019

Actually, I don't see a usecase for LCM. More variety and the example mentioned above are best handled by the product operator cycler(color=['r', 'g', 'b']) * cycler(linestyle=['-', '--', ':', '-.']).

The product operator returns a cycler of length LCM(a,b), so I'm confused by the "LCM" vs. "+" vs "*" taxonomy above [edit: is it just a non-simplified a*b ].

My only displeasure in the product operator is the order of the generated sequences.
having 'b-' followed by 'r.' in sequence is more useful to me than 'b-' followed by 'b.'
To achieve this in the present implementation requires careful manual creation(/extension) of the property lists in order to ensure their lengths match... which is more painful as the required number of unique attribute sets increase.

@tacaswell
Copy link
Member

For sequences whose lengths are relatively * and LCM+ have the same values re-ordered, however for pairs who's lengths are not relatively prime the LCM version will never pick out some pairs (ex, for a len2 and len6 sequence, you will never get the second element of the short sequence with the (0, 2, 4) entries in the longer one. You could go down the path of using space filling curves (ex https://en.wikipedia.org/wiki/Peano_curve) to provide a different path through space but it is not may not be an obvious path (and may in the end be more confusing).

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

5 participants