8

I encountered a rather strange behavior of legend and the errorbar plot commands. I am using Python xy 2.7.3.1 with matplotlib 1.1.1 The code below exemplifies the observed behavior:

import pylab as P
import numpy as N

x1=N.linspace(0,6,10)
y1=N.sin(x1)
x2=N.linspace(0,6,5000)
y2=N.sin(x2)
xerr = N.repeat(0.01,10)
yerr = N.repeat(0.01,10)

#error bar caps visible in scatter dots
P.figure()
P.subplot(121)
P.title("strange error bar caps")
P.scatter(x1,y1,s=100,c="k",zorder=1)
P.errorbar(x1,y1,yerr=yerr,xerr=xerr,color="0.7", 
    ecolor="0.7",fmt=None, zorder=0)
P.plot(x2,y2,label="a label")
P.legend(loc="center")

P.subplot(122)
P.title("strange legend behaviour")
P.scatter(x1,y1,s=100,c="k",zorder=100)
P.errorbar(x1,y1,yerr=yerr,xerr=xerr,color="0.7", 
    ecolor="0.7",fmt=None, zorder=99)
P.plot(x2,y2,label="a label", zorder=101)
P.legend(loc="center")
P.show()

which yields this plot:

error bar caps above scatter plot, plot line above legend

As you can see, the errorbar caps are overwriting the scatter plot. If I increase zorder enough this does not happen any more, but the plot line overwrites the legend. I have the suspicion that the problem is related to this zorder problem with matplotlib.

Quick, dirty, hacky solutions also appreciated.

Edit (thanks @nordev): the desired outcome is the following:

  • errorbars, as well as the ending caps shall be below the scatter plot point.
  • the line plot shall be above the scatter and the error bars
  • the legend shall be above all others

Adjusting the zorder according to your answer:

  • P.legend(zorder=100) --> self.legend_ = mlegend.Legend(self, handles, labels, **kwargs) TypeError: __init__() got an unexpected keyword argument 'zorder'
  • P.errorbar(zorder=0), P.scatter(zorder=1), ... as correctly suggested by you, still yields the same plot, the error bar caps are still above the scatter dots. I corrected the example above accordingly.
halfer
  • 19,824
  • 17
  • 99
  • 186
Faultier
  • 1,296
  • 2
  • 15
  • 21
  • To me this seems correct. The object with the highest `zorder` is placed on top, so the plot above seems correct according to the code. If you want the legend on top, just specify the `zorder` argument for that as well. Also, you don't specify how you want your output to look like, so coming with a solution is not trivial. – sodd May 27 '13 at 09:35
  • legend has no z-order, thus I cannot give that argument to it. I will rephrase the wanted result in the question, thanks for pointing that out. On the left pic, the errorbars are below the scatter plot, but the error bar caps are above, that is just wrong. On the right pic that works correctly, but the line is above the legend, which is unintended. – Faultier May 27 '13 at 10:47
  • You can change the zorder of the `legend` with `P.legend(loc="center").set_zorder(102)` to get it on top in the second subplot. Also, in your comment above you say that the errorbars are below the scatter plot, which seems not to be the case for me, and I suspect it is not for you either. t is easier to see if you use `yerr` and `xerr` bigger than 0.01. – sodd May 27 '13 at 10:51
  • Also, see my answer below, as I explain why you get the output you get. I'm using Python 2.7.5. and matplotlib 1.2.1. – sodd May 27 '13 at 10:58
  • The zorder problem you linked to above is fixed in matplotlib 1.2.1, so if possible you should update your matplotlib to avid these problems. – sodd May 27 '13 at 11:06
  • The errorbars are below the scatter, but the strokes limiting the bars are above the scatter. I will update the question accordingly. Sadly, this is only seen with these awkward sized errorbars (or if they overlap from another point of the plot like in my real dataset). The `.set_zorder()` for the legend worked perfectly for me. I even updated my `python xy` but it seems that the latest `matplotlib` was not included yet. – Faultier May 27 '13 at 11:08

2 Answers2

12

The created plots are correct according to the code you have posted. The objects with the lowest zorder is placed on the bottom, while the object with the highest zorder is placed on top. The zorder problem you linked to is fixed in matplotlib version 1.2.1, so you should update your install if possible.

In your first subplot the errorbars are plotted on top of the scatter points because errorbar is called with zorder=2, while scatter is called with zorder=1 - meaning the errorbars will overlay the scatter points.

In your second subplot, you have called errorbar with zorder=99, scatter with zorder=100 and plot with zorder=101 - meaning that the errorbars will be placed underneath both the scatter points and the line.

The reason the legend is displayed on top of the line in the first subplot, while it is on top of the same line in the second subplot, is due to the fact that you haven't explicitly set the legend objecta zorder value, meaning it will use its default value (which I believe is 5). To change the legends zorder, simply use P.legend(loc="center").set_zorder(102) where 102 is the desired zorder-value.

So in order to produce your desired output, you have to set the zorder arguments accordingly. As you haven't described your desired output in your question, it is hard for me to "correct" your code, as I don't know in which order you want the objects to be drawn.

sodd
  • 12,482
  • 3
  • 54
  • 62
  • Thanks so much for your effort. I will get my hands on version 1.2.1 asap - hate wasting time b/c of me using deprecated versions -.- – Faultier May 27 '13 at 11:10
  • There are workaround for this in matplotlib 1.1.1 as well, I believe, but that is far less elegant and intuitive than it is in 1.2.1. The easiest (and most preventive) way of avoiding problems, is keeping your matplotlib up-to-date. – sodd May 27 '13 at 11:14
  • 1
    @Faultier To be a bit contrary, the advantage of using old software is that there are known work arounds for most of the bugs, with new software you don't even know what the bugs are;) – tacaswell May 27 '13 at 14:17
  • @tcaswell Good point! In my opinion there will "always" be both pros and cons tied to the usage of old and new software, so in the end I guess it is up to the user what is more important (e.g. new functionality vs. known workarounds for bugs etc.). I should have been clearer in my previous comment; the easiest way to avoid _known_ problems (at least to some extent), is to keep the software up to date. – sodd May 27 '13 at 14:38
-1

i use

plt.legend(loc='center').set_zorder(100)
zz_liao
  • 14
  • 2