You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Inconsistent eager vs delayed resolution of Cn in plot() #12967
Note: Delayed evaluation of 'C0' may be counter-intuitive. The color_cycle is evaluated immediately.
This example might be anticipated to have the same colors for the line and the marker. But they have not
delayed evaluation is the intended behavior because these are aliases. This
allows for applying styles after construction of the plot, which helps with
separation of concerns in some libraries.
perhaps. It has been discussed before. The property cycle mechanism that I
implemented was a generalization of the color cycle mechanism, which was
modeled off of Matlab's color-cycling behaior, and predates styling. In
matlab, there was no way to change the color cycling or styling so there
was no solid basis for when to evaluate them. It is entirely reasonable to
consider whether or not they should be delayed. But the key thing is, right
now, CN notation was designed and intended to be aliases. There is no other
possibility for delayed evaluation in matplotlib, meanwhile, id one wanted
eager evaluation, then they can explicitly define the property values
themselves.
should also change the first line color to red and the second line color to green (my bet is that code that relies on implicitly advancing the property cycle is thousands of times more common than explicit use of Cn). Actually that wouldn't be too hard to do, one can just make plot() use Cn as default line color instead of explicitly advancing the property cycle. But... what if the property cycle is cycling on another property? e.g.
If we want to make that work in a consistent fashion, we suddenly need to make all properties support Cn-like syntax, or at least some way to internally say, "this property should be set at draw time based on the n-th value of the default property cycle".
While I don't think this is impossible to implement, it would clearly be a pretty big endeavor, so one should instead ask whether that is really worth it?
applying styles after construction of the plot [...] helps with separation of concerns in some libraries.
To be honest, it is not clear to me why this helps with separation of concerns, nor why the libraries could not apply the style before constructing the plot instead of after (and again, right now, this basically works only for a very limited subset of plots, namely those that plot lines using (some variant of) plot(..., c="Cn")). (I guess this could help if one wants to fully restyle a figure after generating it, but realistically we're nowhere close to that being doable.)
I wouldn't mind moving towards fully delayed evaluation, but only if there is a realistic plan for that (and again, it would be a lot of work, per the above -- feel free to mention if I missed anything, of course). In the meantime, I think consistent eager evaluation of properties is clearly preferable, and would support changing the behavior of the few places that currently use delayed evaluation. If someday, fully delayed evaluation is finally implemented, then that'd be a much bigger API break anyways, and I don't think it would be made more or less painful by moving, right now, to eager evaluation.
I think moving to eager evaluation is the correct path here. I have a vague memory of there being a justification for the delayed evaluation, however these subtle inconsistencies are probably too high of a cost to pay (unless we have a concrete application).
Agree w/ @tacaswell in that I don't see the use case for lazy evaluation. If I want to play with styles I just remake the plot each time. I don't think changing parameters later in a script should magically affect things you've done previously in the script, unless you are directly manipulating the objects you created. That just seems confusing and needlessly mysterious.
It’s much simpler to implement and also to understand for the user. Delayed resolution would have a hidden state of which plot element was created in which order so that we can later find out which element gets which property from the cycle. Also, if I create a Line2D and query it for its color, I wouldn‘t get a color but an intermediate marker like C0. That would be technically correct, but is harder to understand for the user.
I don‘t see a need for restyling an existing plot. Redoing the plot is almost always ok. I also assume that there are subtle difficulties when trying to restyle more complex plots.
Separation of concerns can be achieved with contexts.
Then what is the point of aliases? We could have implemented (much more
easily, might I add) a function that returned the Nth color of the current
color cycle.
I will also note that the very reason why the current CN notation has a "C"
in it is because I pointed out at the time that the concept could be
generalized to other properties such as markers, hashes, etc. Originally,
it was just going to be a 0-9 integer. To address the question of "what if
the property cycle does not have a color", that is already taken into
account in the implementation (IIRC, it falls back to the default line
color, but it might be black... I can't remember).
On Sat, Dec 29, 2018 at 6:35 PM Tim Hoffmann ***@***.***> wrote:
+1 for eager resolution.
It’s much simpler to implement and also to understand for the user.
Delayed resolution would have a hidden state of which plot element was
created in which order so that we can later find out which element gets
which property from the cycle. Also, if I create a Line2D and query it for
its color, I wouldn‘t get a color but an intermediate marker like C0. That
would be technically correct, but is harder to understand for the user.
I don‘t see a need for restyling an existing plot. Redoing the plot is
almost always ok. I also assume that there are subtle difficulties when
trying to restyle more complex plots.
Separation of concerns can be achieved with contexts.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#12967 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AARy-DJdWH480RWQGGJZRF6XFeMJp9FDks5u9_wvgaJpZM4ZK1xs>
.
Well, it's not too late to admit that having a function returning the nth color would have been a better design (and yes, we're likely already falling back on black when the property cycle does not include color, but that's not the problem here).
Again, I'm actually personally not against delayed evaluation, but right now we're very, very far away from having that (we both agree that it would be desirable to have it for all properties, if we do want to have that). Until someone comes up with a PR (or at least a design) implementing that, it seems better to be consistent by using eager evaluation everywhere, rather than an inconsistent mishmash.
How would we do eager evaluation for an Artist that doesn't have an axes
assigned to it yet? Naively, it would be the default property cycler, but
that may not be the same cycler that the eventual axes has when the artist
is assigned to the axes.
On Sun, Dec 30, 2018 at 12:28 PM Antony Lee ***@***.***> wrote:
Well, it's not too late to admit that having a function returning the nth
color would have been a better design (and yes, we're likely already
falling back on black when the property cycle does not include color, but
that's not the problem here).
Again, I'm actually personally not against delayed evaluation, but right
now we're very, very far away from having that (we both agree that it would
be desirable to have it for all properties, if we do want to have that).
Until someone comes up with a PR (or at least a design) implementing that,
it seems better to be consistent by using eager evaluation everywhere,
rather than an inconsistent mishmash.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#12967 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AARy-KEOvKi15qBbAqfQI4TNqqvkGY9hks5u-Pe4gaJpZM4ZK1xs>
.
results in lines in "tab:blue"/"tab:orange", not "r"/"g".
(In fact, I just realized that if you want to have delayed evaluation, having to look up the owning axes instead of the default property cycler would be an additional complication, because this would mean the evaluation cannot be implemented just once by the renderer (which doesn't know what the owning axes is) (right now when a Cn color gets sent to mplcairo's draw_path method it indeed (effectively) looks up rcParams["axes.prop_cycle"]), but would have to be done by each artist draw() separately.)
This issue has been marked "inactive" because it has been 365 days since the last comment. If this issue is still present in recent Matplotlib releases, or the feature request is still wanted, please leave a comment and this label will be removed. If there are no updates in another 30 days, this issue will be automatically closed, but you are free to re-open or create a new issue if needed. We value issue reports, and this procedure is meant to help us resurface and prioritize issues that have not been addressed yet, not make them disappear. Thanks for your help!
Most people don't change the property cycle (Axes.set_prop_cycle or the rcParam) after creating the Axes. I believe the most common pattern is 1. change the rcParam 2. create the Axes, in which case the prop cycles are the same and it doesn't matter from which to take the colors.
Advantage: Nothing to do, and likely good enough.
(2) Use the Axes property cycle.
This likely needs a bit of restructuring:
How would we do eager evaluation for an Artist that doesn't have an axes
assigned to it yet?
Real standalone artists would still go to the default property cycle.
But plotting functions usually follow the pattern 1. Create the artist 2. add it to the Axes. In step 1. the artist is formally standalone. We would need a mechanism to handle the axes context during creation. (Note for plot(), we have _process_plot_var_args as a preprocessing step that takes over these aspects, but it's not generaliziable, we'd need something more universal that works for all artists)
Advantage: Logically more consistent. The automatic colors are chosen from the Axes property cycle, one could expect that "CN" then also goes to that property cycle.
I'm inclined to go with option (1). This has been the behavior for several years, and I'm not aware of a complaint - apart from the issue created here. The behavior seems good enough. We can always reconsider. There's not much difference in consistency/user impact between changing now and changing later. So let's save the effort for now.
This has been the behavior for several years, and I'm not aware of a complaint - apart from the issue created here.
Someone at scipy tracked #28475 (inconsistent resolution of none) to being an issue w/ eager conversion to color that relies on 'none'/empty array for if statements to preserve the correct alpha values.
This has been the behavior for several years, and I'm not aware of a complaint - apart from the issue created here.
Someone at scipy tracked #28475 (inconsistent resolution of none) to being an issue w/ eager conversion to color that relies on 'none'/empty array for if statements to preserve the correct alpha values.
What is the connection to this issue? I read is that there's an issue with the result of to_rgba_array("none") and somebody found out that it has to do with eager conversion. Ok, now we have two issues that relate to eager resolution. Yours looks like an actual bug. The "CN" topic is a bit surprising, but I won't call it a bug. But more importantly, they don't have anything to do with each other.
Bullet point one of using eager resolution everywhere - was just trying to point out that that may create more issues/exacerbate the bugs we already have, b/c I assumed you were saying eager resolution for all colors and not just CN colors (b/c that inconsistency would be a mess).
Thanks for the clarification. I haven't dug into the details of the issue you mentioned, but just from the title - what hinders us from adding if c == "none": return np.array([[0., 0., 0., 0.]])? Anyway, I'm convinced that lazy evaluation is generally significantly more complex to implement and harder to understand. I thus believe any step towards more lazy evaluation will worsen the situation. If we have a mixture, that is tolerable as a status quo. But if touching something we should move towards eager resolution.
what hinders us from adding if c == "none": return np.array([[0., 0., 0., 0.]])?
It breaks contours and hatches, as described in the issue summary. And I worry that casting "none" the same as the other none in same_color could cause unintended side effects since the conversion is inconsistent.
To be investigated: Is the assumption “none” == (0., 0., 0., 0.) invalid or is the breakage an implementation detail of contours?
If the latter, we should fix the contour implementation and then change to_rgba_array. If the former, we need to reconsider the meaning of “none”.
Activity
timhoffm commentedon Dec 25, 2018
Note: Delayed evaluation of 'C0' may be counter-intuitive. The color_cycle is evaluated immediately.
This example might be anticipated to have the same colors for the line and the marker. But they have not
Generated with the delayed evaluation in #13053:
WeatherGod commentedon Dec 26, 2018
timhoffm commentedon Dec 26, 2018
But that would mean that the property cycle should also be evaluated lazily?
WeatherGod commentedon Dec 26, 2018
timhoffm commentedon Dec 26, 2018
Similar, but for patches: #11710
anntzer commentedon Dec 29, 2018
I was originally fine with delayed evaluation (after all I even wrote #11711) but... I think @timhoffm made a critical point here:
If the goal is to "allow for applying styles after construction of the plot", then certainly
should also change the first line color to red and the second line color to green (my bet is that code that relies on implicitly advancing the property cycle is thousands of times more common than explicit use of Cn). Actually that wouldn't be too hard to do, one can just make
plot()useCnas default line color instead of explicitly advancing the property cycle. But... what if the property cycle is cycling on another property? e.g.If we want to make that work in a consistent fashion, we suddenly need to make all properties support Cn-like syntax, or at least some way to internally say, "this property should be set at draw time based on the n-th value of the default property cycle".
While I don't think this is impossible to implement, it would clearly be a pretty big endeavor, so one should instead ask whether that is really worth it?
@WeatherGod says that delayed evaluation
To be honest, it is not clear to me why this helps with separation of concerns, nor why the libraries could not apply the style before constructing the plot instead of after (and again, right now, this basically works only for a very limited subset of plots, namely those that plot lines using (some variant of)
plot(..., c="Cn")). (I guess this could help if one wants to fully restyle a figure after generating it, but realistically we're nowhere close to that being doable.)I wouldn't mind moving towards fully delayed evaluation, but only if there is a realistic plan for that (and again, it would be a lot of work, per the above -- feel free to mention if I missed anything, of course). In the meantime, I think consistent eager evaluation of properties is clearly preferable, and would support changing the behavior of the few places that currently use delayed evaluation. If someday, fully delayed evaluation is finally implemented, then that'd be a much bigger API break anyways, and I don't think it would be made more or less painful by moving, right now, to eager evaluation.
tacaswell commentedon Dec 29, 2018
I think moving to eager evaluation is the correct path here. I have a vague memory of there being a justification for the delayed evaluation, however these subtle inconsistencies are probably too high of a cost to pay (unless we have a concrete application).
jklymak commentedon Dec 29, 2018
Agree w/ @tacaswell in that I don't see the use case for lazy evaluation. If I want to play with styles I just remake the plot each time. I don't think changing parameters later in a script should magically affect things you've done previously in the script, unless you are directly manipulating the objects you created. That just seems confusing and needlessly mysterious.
timhoffm commentedon Dec 29, 2018
+1 for eager resolution.
It’s much simpler to implement and also to understand for the user. Delayed resolution would have a hidden state of which plot element was created in which order so that we can later find out which element gets which property from the cycle. Also, if I create a Line2D and query it for its color, I wouldn‘t get a color but an intermediate marker like C0. That would be technically correct, but is harder to understand for the user.
I don‘t see a need for restyling an existing plot. Redoing the plot is almost always ok. I also assume that there are subtle difficulties when trying to restyle more complex plots.
Separation of concerns can be achieved with contexts.
WeatherGod commentedon Dec 30, 2018
anntzer commentedon Dec 30, 2018
Well, it's not too late to admit that having a function returning the nth color would have been a better design (and yes, we're likely already falling back on black when the property cycle does not include color, but that's not the problem here).
Again, I'm actually personally not against delayed evaluation, but right now we're very, very far away from having that (we both agree that it would be desirable to have it for all properties, if we do want to have that). Until someone comes up with a PR (or at least a design) implementing that, it seems better to be consistent by using eager evaluation everywhere, rather than an inconsistent mishmash.
WeatherGod commentedon Dec 30, 2018
anntzer commentedon Dec 30, 2018
You realize that Cn colors already always look up the default property cycler and never care about the owning axes?
results in lines in "tab:blue"/"tab:orange", not "r"/"g".
(In fact, I just realized that if you want to have delayed evaluation, having to look up the owning axes instead of the default property cycler would be an additional complication, because this would mean the evaluation cannot be implemented just once by the renderer (which doesn't know what the owning axes is) (right now when a Cn color gets sent to mplcairo's draw_path method it indeed (effectively) looks up rcParams["axes.prop_cycle"]), but would have to be done by each artist draw() separately.)
10 remaining items
github-actions commentedon Nov 15, 2024
This issue has been marked "inactive" because it has been 365 days since the last comment. If this issue is still present in recent Matplotlib releases, or the feature request is still wanted, please leave a comment and this label will be removed. If there are no updates in another 30 days, this issue will be automatically closed, but you are free to re-open or create a new issue if needed. We value issue reports, and this procedure is meant to help us resurface and prioritize issues that have not been addressed yet, not make them disappear. Thanks for your help!
timhoffm commentedon Nov 15, 2024
How do we want to proceed on this? I suggest:
(1) Stick to resolution to the default property cycle.
Advantage: Nothing to do, and likely good enough.
(2) Use the Axes property cycle.
This likely needs a bit of restructuring:
Real standalone artists would still go to the default property cycle.
But plotting functions usually follow the pattern 1. Create the artist 2. add it to the Axes. In step 1. the artist is formally standalone. We would need a mechanism to handle the axes context during creation. (Note for plot(), we have
_process_plot_var_argsas a preprocessing step that takes over these aspects, but it's not generaliziable, we'd need something more universal that works for all artists)Advantage: Logically more consistent. The automatic colors are chosen from the Axes property cycle, one could expect that "CN" then also goes to that property cycle.
I'm inclined to go with option (1). This has been the behavior for several years, and I'm not aware of a complaint - apart from the issue created here. The behavior seems good enough. We can always reconsider. There's not much difference in consistency/user impact between changing now and changing later. So let's save the effort for now.
story645 commentedon Nov 15, 2024
Someone at scipy tracked #28475 (inconsistent resolution of none) to being an issue w/ eager conversion to color that relies on 'none'/empty array for if statements to preserve the correct alpha values.
timhoffm commentedon Nov 16, 2024
What is the connection to this issue? I read is that there's an issue with the result of
to_rgba_array("none")and somebody found out that it has to do with eager conversion. Ok, now we have two issues that relate to eager resolution. Yours looks like an actual bug. The "CN" topic is a bit surprising, but I won't call it a bug. But more importantly, they don't have anything to do with each other.story645 commentedon Nov 17, 2024
Bullet point one of using eager resolution everywhere - was just trying to point out that that may create more issues/exacerbate the bugs we already have, b/c I assumed you were saying eager resolution for all colors and not just CN colors (b/c that inconsistency would be a mess).
timhoffm commentedon Nov 17, 2024
Thanks for the clarification. I haven't dug into the details of the issue you mentioned, but just from the title - what hinders us from adding
if c == "none": return np.array([[0., 0., 0., 0.]])? Anyway, I'm convinced that lazy evaluation is generally significantly more complex to implement and harder to understand. I thus believe any step towards more lazy evaluation will worsen the situation. If we have a mixture, that is tolerable as a status quo. But if touching something we should move towards eager resolution.story645 commentedon Nov 19, 2024
It breaks contours and hatches, as described in the issue summary. And I worry that casting "none" the same as the other none in
same_colorcould cause unintended side effects since the conversion is inconsistent.timhoffm commentedon Nov 20, 2024
To be investigated: Is the assumption “none” == (0., 0., 0., 0.) invalid or is the breakage an implementation detail of contours?
timhoffm commentedon Nov 20, 2024
To be investigated: Is the assumption “none” == (0., 0., 0., 0.) invalid or is the breakage an implementation detail of contours?
If the latter, we should fix the contour implementation and then change
to_rgba_array. If the former, we need to reconsider the meaning of “none”.