FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

fill_between() misbehaves with logarithmic X axis · Issue #7371 · matplotlib/matplotlib · GitHub

Repository navigation

fill_between() misbehaves with logarithmic X axis #7371

Closed as not planned
Closed as not planned

Description

I'm seeing fill_between() drawing polygons incorrectly on a logarithmic X axis.

import numpy as np
import matplotlib
matplotlib.use('agg')
import matplotlib.pyplot as plt
import sys

plt.xscale('log')

xs = np.array([10, 100])
ys = np.array([10, -25])
zeros = np.array([0, 0])

plt.plot(xs, ys)

plt.fill_between(xs, zeros, ys, where=ys > zeros, facecolor='green', interpolate=True)
plt.fill_between(xs, zeros, ys, where=ys < zeros, facecolor='red', interpolate=True)

plt.savefig(sys.argv[1])

If I comment out the plt.xscale('log') line everything comes out as expected:

But with the log axis the fill_between() polygons don't align with the plt.plot(xs, ys) line:

(This is with matplotlib 1.5.3, installed via pip.)

Activity

  1. efiring commented on Nov 1, 2016

    Member

    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.

  2. zevweiss commented on Nov 1, 2016

    Author

    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?

  3. efiring commented on Nov 1, 2016

    Member

    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.

  4. added this to the milestone on Nov 1, 2016
  5. added
    Difficulty: Hardhttps://matplotlib.org/devdocs/devel/contribute.html#good-first-issues
    and removed
    Difficulty: Hardhttps://matplotlib.org/devdocs/devel/contribute.html#good-first-issues
    on Nov 1, 2016
  6. removed this from the milestone on Nov 1, 2016
  7. tacaswell commented on Nov 1, 2016

    Member

    I think the problem here is actually with plot and fill_between is behaving correctly.

    import numpy as np
    import matplotlib
    
    import matplotlib.pyplot as plt
    
    
    plt.xscale('log')
    
    xs = np.array([10, 100])
    ys = np.array([10, -25])
    zeros = np.array([0, 0])
    
    x_lots = np.linspace(10, 100, 512)
    y_lots = np.linspace(10, -25, 512)
    plt.plot(x_lots, y_lots)
    
    plt.fill_between(xs, zeros, ys, where=ys > zeros, facecolor='green', interpolate=True)
    plt.fill_between(xs, zeros, ys, where=ys < zeros, facecolor='red', interpolate=True)
    plt.show()

    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.

  8. efiring commented on Nov 1, 2016

    Member

    @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.

  9. tacaswell commented on Nov 1, 2016

    Member

    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 np
    import matplotlib
    
    import matplotlib.pyplot as plt
    
    
    plt.xscale('log')
    
    xs = np.array([10, 100])
    ys = np.array([10, -25])
    
    
    x_lots = np.linspace(10, 100, 512)
    y_lots = np.linspace(10, -25, 512)
    plt.plot(x_lots, y_lots)
    
    plt.fill_between(xs, zeros, ys, where=ys > zeros, facecolor='green', interpolate=True, alpha=.5)
    plt.fill_between(xs, zeros, ys, where=ys < zeros, facecolor='red', interpolate=True, alpha=.5)
    
    plt.fill_between(x_lots, 0, y_lots, where=y_lots > 0, facecolor='green', interpolate=True, alpha=.5)
    plt.fill_between(x_lots, 0, y_lots, where=y_lots < 0, facecolor='red', interpolate=True, alpha=.5)

    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).

  10. WeatherGod commented on Nov 1, 2016

    Member

    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:

    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

    plt.xscale('log')

    xs = np.array([10, 100])
    ys = np.array([10, -25])

    x_lots = np.linspace(10, 100, 512)
    y_lots = np.linspace(10, -25, 512)
    plt.plot(x_lots, y_lots)

    plt.fill_between(xs, zeros, ys, where=ys > zeros, facecolor='green', interpolate=True, alpha=.5)
    plt.fill_between(xs, zeros, ys, where=ys < zeros, facecolor='red', interpolate=True, alpha=.5)

    plt.fill_between(x_lots, 0, y_lots, where=y_lots > 0, facecolor='green', interpolate=True, alpha=.5)
    plt.fill_between(x_lots, 0, y_lots, where=y_lots < 0, facecolor='red', interpolate=True, alpha=.5)

    [image: so]
    https://cloud.githubusercontent.com/assets/199813/19905103/b32fabe8-a04b-11e6-9c00-06a8ac6d9643.png

    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).

    —
    You are receiving this because you are subscribed to this thread.
    Reply to this email directly, view it on GitHub
    #7371 (comment),
    or mute the thread
    https://github.com/notifications/unsubscribe-auth/AARy-CG4psQafnwoDxjFLvoi7VTV7nHBks5q55rSgaJpZM4KlzIT
    .

  11. efiring commented on Nov 1, 2016

    Member

    On 2016/11/01 10:26 AM, Benjamin Root wrote:

    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.

  12. efiring commented on Nov 1, 2016

    Member

    On 2016/11/01 10:02 AM, Thomas A Caswell wrote:

    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.

  13. efiring commented on Nov 6, 2016

    Member

    @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.

  14. 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!

  15. QuLogic commented on May 2, 2023

    Member

    This seems to still be broken unfortunately.

  16. reopened this on May 2, 2023
  17. timhoffm commented on May 2, 2023

    Member

    I suppose, interpolation should be done in screen space

    plt.fill_between(xs, zeros, ys, where=ys > zeros, facecolor='green', interpolate=True)
    plt.fill_between(xs, zeros, ys, where=ys < zeros, facecolor='red', interpolate=True)
    

    should cover the same areas as

    plt.fill_between(xs, zeros, ys, facecolor='green', interpolate=True)
    

    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

  18. 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!

  19. added
    status: closed as inactiveIssues closed by the "Stale" Github Action. Please comment on any you think should still be open.
    on Jun 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions


      Back | FazBrowse Home | New Git URL