Fill_between is using linear interpolation to find the zero-crossing. To get the behavior you are expecting it would need to transform from data to screen space (or any equivalent coordinate system), interpolate, and then transform back to data space to get the zero-crossing. Normally with fill_between this doesn't make much difference because the curves have enough points in x that the interpolation is over a small interval.
Thanks for the prompt response. I may be missing something obvious, but is there any reason why interpolated zero-crossings are "special"? I notice it seems to interpolate non-zero-crossing segments fine, for what it's worth:
And since I don't have any sense of it -- is what you described a feasible solution that could be incorporated fairly easily, or would that be overly complex/computationally-expensive?
Yes, the zero-crossings are special because they require inserting a data point in the array. That point is needed to define the polygon being filled. It's the start or end point of the line segment along the axis.
It looks like it would not be very difficult to handle the case where the y-axis is linear and the x-axis is one of the standard named scales (log, symlog, or logit). The axis has a _scale attribute, which has a get_transform method. The transform has methods for forward and inverse transformations. The transformations of x-coordinates would be done inside the get_interp_point function that is defined inside the Axes.fill_between, using transform_non_affine before the interpolation and inverted afterwards.
@tacaswell, that was also my first reaction, but that is looking at the problem with the assumption that a line between two points on a log scale should be a log curve. This is not what we want, however, as a plotting library. We want to preserve the ability to make a straight line between any two points. Consider a spectrum on a log-log plot. If the theory says the PSD should go as the inverse second power of the frequency, you want to be able to plot a straight-line segment with a slope of -2.
Fill_between is a bit different because it is inserting a data point--or a point that is most likely to be interpreted visually as a data point. It's debatable, but I came around to the idea that is should indeed do that interpolation linearly in data space, not in screen space as it does now. Yes, it would also make sense for the user in such an instance to make sure that the data points are closely spaced, and we could respond to this by leaving out the complexity of the transform-interpolate-inverse transform sequence. I'm close to neutral on this.
I am confused, I think fill_between does do the interpolation in data-space currently and the OP here is asking for it to be done in screen space (is there a comma missing?).
Both of these methods behave better if you up the density:
I am pretty strongly 👎 on doing the zero-crossing logic in screen space.
I think it also makes sense for us to continue to connect point in screen-space with strait lines independent of the underlying transform, anything else will lead to excessive complication (for now).
don't we have a transform_curve() method in the transform stack? Consider
the case of drawing a curve in polar space, I am pretty sure that draws the
transformed line. It was all part of the bezier curve feature added by Mike
way back when, IIRC.
I am confused, I think fill_between does do the interpolation in
data-space currently and the OP here is asking for it to be done in screen
space (is there a comma missing?).
Both of these methods behave better if you up the density:
import numpy as npimport matplotlib
import matplotlib.pyplot as plt
I am pretty strongly 👎 on doing the zero-crossing logic in screen space.
I think it also makes sense for us to continue to connect point in
screen-space with strait lines independent of the underlying transform,
anything else will lead to excessive complication (for now).
don't we have a transform_curve() method in the transform stack? Consider
the case of drawing a curve in polar space, I am pretty sure that draws the
transformed line. It was all part of the bezier curve feature added by Mike
way back when, IIRC.
Yes, that occurred to me also--but adding in that sort of functionality
as a general option is a big change.
I am confused, I think |fill_between| does do the interpolation in
data-space currently and the OP here is asking for it to be done in
screen space (is there a comma missing?).
Yes, that is what the OP is expecting, and the behavior for which I was
trying to describe an algorithm. I might have described something
backwards in an earlier comment.
@tacaswell, I think your statements,
"I am pretty strongly 👎 on doing the zero-crossing logic in screen space."
and
"I think it also makes sense for us to continue to connect point in screen-space with strait lines independent of the underlying transform, anything else will lead to excessive complication (for now)."
are inconsistent. What the OP is requesting is exactly the latter. We are inserting a point in the fill_between case only because we need it in order to make the polygons to be filled. The line should still be straight between the two data points that the user has supplied. In other words, we should not be trying to generate a data point via linear interpolation in data space; we should be finding the zero-crossing in screen space, and transforming it to data coordinates only because we need them for the polygons.
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!
Note: The interpolation is done on execution of fill_between(). This means the result will depend on whether we set the log scale before or after the command. But IMHO that's something we can live with.
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!
Activity
efiring commented on Nov 1, 2016
Fill_between is using linear interpolation to find the zero-crossing. To get the behavior you are expecting it would need to transform from data to screen space (or any equivalent coordinate system), interpolate, and then transform back to data space to get the zero-crossing. Normally with fill_between this doesn't make much difference because the curves have enough points in x that the interpolation is over a small interval.
zevweiss commented on Nov 1, 2016
Thanks for the prompt response. I may be missing something obvious, but is there any reason why interpolated zero-crossings are "special"? I notice it seems to interpolate non-zero-crossing segments fine, for what it's worth:
And since I don't have any sense of it -- is what you described a feasible solution that could be incorporated fairly easily, or would that be overly complex/computationally-expensive?
efiring commented on Nov 1, 2016
Yes, the zero-crossings are special because they require inserting a data point in the array. That point is needed to define the polygon being filled. It's the start or end point of the line segment along the axis.
It looks like it would not be very difficult to handle the case where the y-axis is linear and the x-axis is one of the standard named scales (log, symlog, or logit). The axis has a _scale attribute, which has a get_transform method. The transform has methods for forward and inverse transformations. The transformations of x-coordinates would be done inside the get_interp_point function that is defined inside the Axes.fill_between, using transform_non_affine before the interpolation and inverted afterwards.
tacaswell commented on Nov 1, 2016
I think the problem here is actually with plot and fill_between is behaving correctly.
The problem is that there are enough points in your data and the plot is giving you an incorrect estimate of where the zero crossing is.
efiring commented on Nov 1, 2016
@tacaswell, that was also my first reaction, but that is looking at the problem with the assumption that a line between two points on a log scale should be a log curve. This is not what we want, however, as a plotting library. We want to preserve the ability to make a straight line between any two points. Consider a spectrum on a log-log plot. If the theory says the PSD should go as the inverse second power of the frequency, you want to be able to plot a straight-line segment with a slope of -2.
Fill_between is a bit different because it is inserting a data point--or a point that is most likely to be interpreted visually as a data point. It's debatable, but I came around to the idea that is should indeed do that interpolation linearly in data space, not in screen space as it does now. Yes, it would also make sense for the user in such an instance to make sure that the data points are closely spaced, and we could respond to this by leaving out the complexity of the transform-interpolate-inverse transform sequence. I'm close to neutral on this.
tacaswell commented on Nov 1, 2016
I am confused, I think fill_between does do the interpolation in data-space currently and the OP here is asking for it to be done in screen space (is there a comma missing?).
Both of these methods behave better if you up the density:
I am pretty strongly 👎 on doing the zero-crossing logic in screen space.
I think it also makes sense for us to continue to connect point in screen-space with strait lines independent of the underlying transform, anything else will lead to excessive complication (for now).
WeatherGod commented on Nov 1, 2016
don't we have a transform_curve() method in the transform stack? Consider
the case of drawing a curve in polar space, I am pretty sure that draws the
transformed line. It was all part of the bezier curve feature added by Mike
way back when, IIRC.
On Tue, Nov 1, 2016 at 4:02 PM, Thomas A Caswell notifications@github.com
wrote:
efiring commented on Nov 1, 2016
On 2016/11/01 10:26 AM, Benjamin Root wrote:
Yes, that occurred to me also--but adding in that sort of functionality
as a general option is a big change.
efiring commented on Nov 1, 2016
On 2016/11/01 10:02 AM, Thomas A Caswell wrote:
Yes, that is what the OP is expecting, and the behavior for which I was
trying to describe an algorithm. I might have described something
backwards in an earlier comment.
efiring commented on Nov 6, 2016
@tacaswell, I think your statements,
"I am pretty strongly 👎 on doing the zero-crossing logic in screen space."
and
"I think it also makes sense for us to continue to connect point in screen-space with strait lines independent of the underlying transform, anything else will lead to excessive complication (for now)."
are inconsistent. What the OP is requesting is exactly the latter. We are inserting a point in the fill_between case only because we need it in order to make the polygons to be filled. The line should still be straight between the two data points that the user has supplied. In other words, we should not be trying to generate a data point via linear interpolation in data space; we should be finding the zero-crossing in screen space, and transforming it to data coordinates only because we need them for the polygons.
github-actions commented on Apr 1, 2023
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!
QuLogic commented on May 2, 2023
This seems to still be broken unfortunately.
timhoffm commented on May 2, 2023
I suppose, interpolation should be done in screen space
should cover the same areas as
Note: The interpolation is done on execution of fill_between(). This means the result will depend on whether we set the log scale before or after the command. But IMHO that's something we can live with.
The relevant code section is
https://github.com/matplotlib/matplotlib/blob/8166596ff7f6d1797d97b8b50e8b41d8e7fe373c/lib/matplotlib/axes/_axes.py#LL5399-L5421
github-actions commented on May 3, 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!