Legend placement on graphs with fill_between

Hi all,

I'm trying to generate graphs from my test results, with regions coloured with succeeded and failing tests. It nearly works, but I have the following problem. I am providing the data with fill_between, which returns PolyCollection objects which cannot be provided to a legend. So I use the "proxy artist" trick, as described here

http://matplotlib.sourceforge.net/users/legend_guide.html#plotting-guide-legend

The problem is that my legend then gets placed over the graph. As it has been generated from proxy artists, it presumably doesn't take account of the real artists when deciding where to place itself.

I tried plotting lines as well as filling regions (commented lines in the code below) which works for the legend placement but leaves an ugly red line inside the green region, where presumably it is trying to show me that it's plotted a line.

As I in general have no idea what the data will look like I believe I need to use the "best" placement for the legend, I cannot hardcode it.

Any advice greatly appreciated. I paste below exactly what my program does (this is autogenerated from my real program).

Regards,
Geoff Bache

#!/usr/bin/env python

import pylab

pylab.clf()
figure4 = pylab.figure(1)
figure4.set_figwidth(9.4488188976377945)
figure4.set_figheight(7.8740157480314963)
axessubplot4 = pylab.subplot(111)
text4 = pylab.title("Test results for Application: 'Application One' Version: 'version2'", fontsize=10, family='monospace')
#axessubplot4.plot([0, 1, 2, 3, 4, 5], [2, 2, 2, 8, 8, 15], color='#CEEFBD', linewidth=2, linestyle='-', label='Succeeded tests')
#axessubplot4.plot([1, 2, 3], [2, 4, 8], color='#FF3118', linewidth=2, linestyle='-', label='Failed tests')
#axessubplot4.plot([4, 5], [8, 18], color='#FF3118', linewidth=2, linestyle='-', label='Failed tests')
axessubplot4.fill_between([0, 1, 2, 3, 4, 5], [0, 0, 0, 0, 0, 0], [2, 2, 2, 8, 8, 15], color='#CEEFBD', linewidth=2, linestyle='-')
axessubplot4.fill_between([1, 2, 3], [2, 2, 8], [2, 4, 8], color='#FF3118', linewidth=2, linestyle='-')
axessubplot4.fill_between([4, 5], [8, 15], [8, 18], color='#FF3118', linewidth=2, linestyle='-')
pylab.xticks([0, 1, 2, 3, 4, 5], ['14Jan2006', '15Jan2006', '16Jan2006', '17Jan2006', '18Jan2006', '19Jan2006'])
silent_list8 = axessubplot4.get_xticklabels()
pylab.setp(silent_list8, 'rotation', 90, fontsize=7)
p = pylab.Rectangle((0, 0), 1, 1, fc="#FF3118")
p2 = pylab.Rectangle((0, 0), 1, 1, fc="#CEEFBD")
legend4 = axessubplot4.legend([ p, p2 ], ('Failed tests', 'Succeeded tests'), 'best', shadow=False)
fancybboxpatch4 = legend4.get_frame()
fancybboxpatch4.set_alpha(0.5)
figure4.subplots_adjust(bottom=0.25)
figure4.savefig('results.app1.version2.png', dpi=100)

What about creating a proxy artist which is a simple polygon that has
the same outline as your fill_between polygon?

In [539]: t = np.arange(0, 1, 0.05)

In [540]: y = np.sin(2*np.pi*t)

In [541]: verts = zip(t, y)

In [542]: proxy = mpatches.Polygon(verts, facecolor='yellow')

The only reason fill_between uses a PolyCollection is to support the
"where" keyword argument for non-contiguous fill regions, which you do
not appear to be using. Thus you could simply create the polygon
yourself with a little calculation (see mlab.poly_between for a helper
function) and then just add that patch to the axes rather than using
fill_between::

  t = np.arange(0, 1, 0.05)
  ymin = np.sin(2*np.pi*t)-5
  ymax = np.sin(2*np.pi*t)+5
  xs, ys = mlab.poly_between(t, ymin, ymax)
  verts = zip(xs, ys)
  poly = mpatches.Polygon (verts, facecolor='red', label='my poly')
  ax = subplot(111)
  ax.add_patch(poly)
  ax.legend(loc='best')
  ax.axis([0, 1, -6, 6])
  plt.draw()

···

On Thu, Feb 11, 2010 at 9:43 AM, Geoff Bache <geoff.bache@...2973...> wrote:

Hi all,

I'm trying to generate graphs from my test results, with regions
coloured with succeeded and failing tests. It nearly works, but I have
the following problem. I am providing the data with fill_between, which
returns PolyCollection objects which cannot be provided to a legend. So
I use the "proxy artist" trick, as described here

http://matplotlib.sourceforge.net/users/legend_guide.html#plotting-guide-legend

Or, you may fool the algorithm to find the best location by adding
invisible lines.
For example,

axessubplot4.set_autoscale_on(False)
l1, = axessubplot4.plot([4, 5], [8, 18])
l1.set_visible(False)
axessubplot4.set_autoscale_on(True)

Regards,

-JJ

···

On Thu, Feb 11, 2010 at 10:58 AM, John Hunter <jdh2358@...287...> wrote:

On Thu, Feb 11, 2010 at 9:43 AM, Geoff Bache <geoff.bache@...2973...> wrote:

Hi all,

I'm trying to generate graphs from my test results, with regions
coloured with succeeded and failing tests. It nearly works, but I have
the following problem. I am providing the data with fill_between, which
returns PolyCollection objects which cannot be provided to a legend. So
I use the "proxy artist" trick, as described here

http://matplotlib.sourceforge.net/users/legend_guide.html#plotting-guide-legend

What about creating a proxy artist which is a simple polygon that has
the same outline as your fill_between polygon?

In [539]: t = np.arange(0, 1, 0.05)

In [540]: y = np.sin(2*np.pi*t)

In [541]: verts = zip(t, y)

In [542]: proxy = mpatches.Polygon(verts, facecolor='yellow')

The only reason fill_between uses a PolyCollection is to support the
"where" keyword argument for non-contiguous fill regions, which you do
not appear to be using. Thus you could simply create the polygon
yourself with a little calculation (see mlab.poly_between for a helper
function) and then just add that patch to the axes rather than using
fill_between::

t = np.arange(0, 1, 0.05)
ymin = np.sin(2*np.pi*t)-5
ymax = np.sin(2*np.pi*t)+5
xs, ys = mlab.poly_between(t, ymin, ymax)
verts = zip(xs, ys)
poly = mpatches.Polygon (verts, facecolor='red', label='my poly')
ax = subplot(111)
ax.add_patch(poly)
ax.legend(loc='best')
ax.axis([0, 1, -6, 6])
plt.draw()

------------------------------------------------------------------------------
SOLARIS 10 is the OS for Data Centers - provides features such as DTrace,
Predictive Self Healing and Award Winning ZFS. Get Solaris 10 NOW
http://p.sf.net/sfu/solaris-dev2dev
_______________________________________________
Matplotlib-users mailing list
Matplotlib-users@lists.sourceforge.net
matplotlib-users List Signup and Options

Many thanks for the suggestions, will try these out today.

Incidentally, it looks like there is an interesting bug in the mail
archive software. Viewing my original message there I can see that it
has removed all instances of the combination "pre" (that's p then r
then e if it eats this too!). It does this even in the middle of
words, such as "previous", "appreciate" etc.

Regards,
Geoff

···

On Thu, Feb 11, 2010 at 6:44 PM, Jae-Joon Lee <lee.j.joon@...287...> wrote:

Or, you may fool the algorithm to find the best location by adding
invisible lines.
For example,

axessubplot4.set_autoscale_on(False)
l1, = axessubplot4.plot([4, 5], [8, 18])
l1.set_visible(False)
axessubplot4.set_autoscale_on(True)

Regards,

-JJ

On Thu, Feb 11, 2010 at 10:58 AM, John Hunter <jdh2358@...287...> wrote:

On Thu, Feb 11, 2010 at 9:43 AM, Geoff Bache <geoff.bache@...2973...> wrote:

Hi all,

I'm trying to generate graphs from my test results, with regions
coloured with succeeded and failing tests. It nearly works, but I have
the following problem. I am providing the data with fill_between, which
returns PolyCollection objects which cannot be provided to a legend. So
I use the "proxy artist" trick, as described here

http://matplotlib.sourceforge.net/users/legend_guide.html#plotting-guide-legend

What about creating a proxy artist which is a simple polygon that has
the same outline as your fill_between polygon?

In [539]: t = np.arange(0, 1, 0.05)

In [540]: y = np.sin(2*np.pi*t)

In [541]: verts = zip(t, y)

In [542]: proxy = mpatches.Polygon(verts, facecolor='yellow')

The only reason fill_between uses a PolyCollection is to support the
"where" keyword argument for non-contiguous fill regions, which you do
not appear to be using. Thus you could simply create the polygon
yourself with a little calculation (see mlab.poly_between for a helper
function) and then just add that patch to the axes rather than using
fill_between::

t = np.arange(0, 1, 0.05)
ymin = np.sin(2*np.pi*t)-5
ymax = np.sin(2*np.pi*t)+5
xs, ys = mlab.poly_between(t, ymin, ymax)
verts = zip(xs, ys)
poly = mpatches.Polygon (verts, facecolor='red', label='my poly')
ax = subplot(111)
ax.add_patch(poly)
ax.legend(loc='best')
ax.axis([0, 1, -6, 6])
plt.draw()

------------------------------------------------------------------------------
SOLARIS 10 is the OS for Data Centers - provides features such as DTrace,
Predictive Self Healing and Award Winning ZFS. Get Solaris 10 NOW
http://p.sf.net/sfu/solaris-dev2dev
_______________________________________________
Matplotlib-users mailing list
Matplotlib-users@lists.sourceforge.net
matplotlib-users List Signup and Options

------------------------------------------------------------------------------
SOLARIS 10 is the OS for Data Centers - provides features such as DTrace,
Predictive Self Healing and Award Winning ZFS. Get Solaris 10 NOW
http://p.sf.net/sfu/solaris-dev2dev
_______________________________________________
Matplotlib-users mailing list
Matplotlib-users@lists.sourceforge.net
matplotlib-users List Signup and Options

Actually, not using it wasn't out of choice. I couldn't figure out how
to make it stop plotting at one point at start again at the next. In
my example, is there a way to plot the red regions using a single call
and "where"? I tried this

where = [False, True, True, True, False, True]
axessubplot4.fill_between([0, 1, 2, 3, 4, 5], [2, 2, 2, 8, 8, 15], [2,
2, 4, 8, 8, 18], where=where, color='#FF3118', linewidth=2,
linestyle='-')

but that fails to plot either to or from point 4, whereas actually I
just want it to leave out the region between points 3 and 4 (where the
values are equal)

Regards,
Geoff

···

On Thu, Feb 11, 2010 at 4:58 PM, John Hunter <jdh2358@...287...> wrote:

On Thu, Feb 11, 2010 at 9:43 AM, Geoff Bache <geoff.bache@...2973...> wrote:

Hi all,

I'm trying to generate graphs from my test results, with regions
coloured with succeeded and failing tests. It nearly works, but I have
the following problem. I am providing the data with fill_between, which
returns PolyCollection objects which cannot be provided to a legend. So
I use the "proxy artist" trick, as described here

http://matplotlib.sourceforge.net/users/legend_guide.html#plotting-guide-legend

The only reason fill_between uses a PolyCollection is to support the
"where" keyword argument for non-contiguous fill regions, which you do
not appear to be using.