Skip to content

errorbar doesn't follow plot order #17139

Closed

Description

@cosama

Bug report

matplotlib.pyplot.errorbar does follow the plot order, in particular the markers seem to be plot always last. Calls to other plot functions will thus be covered by the errobar markers.

Code for reproduction

import matplotlib.pyplot as plt
plt.errorbar(range(10), range(10), fmt='o')
plt.plot(range(10), range(10), 'x')
plt.show()

Expected outcome

the 'x's should be on top but they are covered by the 'o' of errorbar.

Matplotlib version

  • Operating system: Fedora 31
  • Matplotlib version: 3.2.1
  • Matplotlib backend: Qt5Agg
  • Python version: 3.7.6

Installation from pip3

Activity

  1. jklymak commented on Apr 15, 2020

    @jklymak
    Member

    You can probably workaround w zorder kwarg.

  2. cosama commented on Apr 15, 2020

    @cosama
    Author

    @jklymak Thanks for the reply. I tried that earlier and I can confirm that plt.errorbar(range(10), range(10), fmt='o', zorder=-1) produces the expected result.

    That is a good fix for now, but long term I think it would be good to fix this.

  3. anntzer commented on Apr 20, 2020

    @anntzer
    Contributor

    I agree this is a bug, and think that the correct fix is along the lines of

    diff --git i/lib/matplotlib/axes/_axes.py w/lib/matplotlib/axes/_axes.py
    index 3f9d7fede..3da0b40e8 100644
    --- i/lib/matplotlib/axes/_axes.py
    +++ w/lib/matplotlib/axes/_axes.py
    @@ -3244,7 +3244,6 @@ class Axes(_AxesBase):
             # anything that comes in as 'None', drop so the default thing
             # happens down stream
             kwargs = {k: v for k, v in kwargs.items() if v is not None}
    -        kwargs.setdefault('zorder', 2)
     
             try:
                 offset, errorevery = errorevery
    @@ -3306,8 +3305,6 @@ class Axes(_AxesBase):
             plot_line_style = {
                 **base_style,
                 **kwargs,
    -            'zorder': (kwargs['zorder'] - .1 if barsabove else
    -                       kwargs['zorder'] + .1),
             }
     
             # make the style dict for the line collections (the bars)
    @@ -3352,7 +3349,6 @@ class Axes(_AxesBase):
             data_line = None
             if plot_line:
                 data_line = mlines.Line2D(x, y, **plot_line_style)
    -            self.add_line(data_line)
     
             barcols = []
             caplines = []
    @@ -3510,8 +3506,15 @@ class Axes(_AxesBase):
                         xup, yup = xywhere(x, y, uplims & everymask)
                         caplines.append(mlines.Line2D(xup, yup, marker='_',
                                                       **eb_cap_style))
    -        for l in caplines:
    -            self.add_line(l)
    +
    +        if barsabove:
    +            self.add_line(data_line)
    +            for l in caplines:
    +                self.add_line(l)
    +        else:
    +            for l in caplines:
    +                self.add_line(l)
    +            self.add_line(data_line)
     
             self._request_autoscale_view()
             errorbar_container = ErrorbarContainer((data_line, tuple(caplines),

    -- basically, achieve support for barsabove by just plotting the elements in the right order, rather than manipulating their zorder, as the latter misinteracts with other plot elements.

    As usual, brownie points for whoever takes up this patch and turns it into a proper PR with tests/api_changes/etc.

  4. cosama commented on Apr 22, 2020

    @cosama
    Author

    I started to look into this, and it seems that the vertical errorbars (bar columns) are not following the plot order either, they use vlines which even can make them fall behind the last plot's markers.

    I will try to work on this some more, maybe figure out a way to not use vlines.

    The vertical bars also won't work together with the patch suggested by @anntzer

  5. anntzer commented on Apr 22, 2020

    @anntzer
    Contributor

    zorder gets copied into eb_lines_style, why doesn't that work? can you provide a repro?

  6. cosama commented on Apr 22, 2020

    @cosama
    Author

    @anntzer: It should have worked before applying the patch, but with the changes you proposed above we remove all the zorder calls. I tried to add in eb_lines_style.defautl('zorder', 2.0) which still looks like they fall behind lines previously plotted and raising it to 2.1 means it will appear on top of everything.

    Also the calls to vlines mean those objects are instantaneously plot, so that needs to be considered in the calls to add_lines, later on, otherwise the vertical bars will always fall behind the line markers.

    I work on it on https://github.com/cosama/matplotlib/tree/fix-errobar-order, I added a test lib/matplotlib/tests/test_axes.py::test_errorbar_default_order which illustrates how I would imagine it.

  7. anntzer commented on Apr 22, 2020

    @anntzer
    Contributor

    Ah yes, so the zorder still needs to be put in the kwargs (but without the 0.1 offset), and the call to vlines needs to be done in the right order relative to the rest rather than immediately.
    ... except that I guess collections and lines are zsorted separately, argh.

  8. QuLogic commented on Apr 22, 2020

    @QuLogic
    Member

    See also #1622.

  9. cosama commented on Apr 22, 2020

    @cosama
    Author

    @anntzer. The zsorted issue was kind of on my mind, I know where little about the internals of matplotlib but I was suspecting that might be the case. This seems to agree very well with what I observe.

    @QuLogic Thanks for the link, that seems to be very much related.

    I have to work right now, but will try to get back to it later today. I think the use of vlines is not optimal here maybe the error bars could be painted by something else?

  10. cosama commented on Apr 22, 2020

    @cosama
    Author

    Drawing the bar columns as Lines2D brings us pretty close to the expected result, but breaks a lot of other tests. It is pretty ugly and not very efficient but my personal opinion, I rather have something slow that does what I would expect than something that behaves oddly. Pushed the fix to my branch, updated the link above, it was broken.

  11. tacaswell commented on Apr 6, 2021

    @tacaswell
    Member

    Closed by #18216

  12. added this to the v3.5.0 milestone on Apr 6, 2021
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

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions