Bug in print_figure with bbox_inches='tight'? Plots in ipython notebook losing titles and labels...

Hi all,

in ipython for the qtconsole and notebook, we send inline figures using

fig.canvas.print_figure(bytes_io, format=fmt, bbox_inches='tight')

as seen here:

https://github.com/ipython/ipython/blob/master/IPython/core/pylabtools.py#L104

However, this produces truncated figure titles. Consider this code:

f, ax = plt.subplots()
ax.plot(rand(100))
ax.set_title('Axis title')
f.suptitle('Figure title');

···

###

which produces this in the notebook:

http://img546.imageshack.us/img546/5448/selection001c.png

As you can see, the figure title gets truncated.

We started using bbox_inches='tight' because otherwise in many cases
the images were coming back with insane amounts of white padding, and
Stefan vdW suggested this fix. But it seems that in other cases it
actually loses data, which is even worse...

A slightly more complicated example, using basemap, not only truncates
the title but also all the x and y labels:

from mpl_toolkits.basemap import Basemap

lon0, lat0, lon1, lat1 = (84.38, 26.22, 88.9, 29.8)
resolution = 'i'

parallels = np.linspace(lat0, lat1, 5)
meridians = np.linspace(lon0, lon1, 5)

f, ax = plt.subplots()
m = Basemap(lon0, lat0, lon1, lat1, resolution=resolution, ax=ax)
m.drawcountries(color=(1,1,0)) # country boundaries in pure yellow
m.drawrivers(color=(0,1,1)) # rivers in cyan
m.bluemarble() # NASA bluemarble image
m.drawmeridians(meridians, labels=[0,0,0,1], fmt='%.2f')
m.drawparallels(parallels, labels=[1,0,0,0], fmt='%.2f')
f.suptitle('The Himalayas');

#####

produces:

http://img192.imageshack.us/img192/986/selection002e.png

This looks like a matplotlib bug, but I have no idea how easy it is to
fix. In the meantime, should we in ipython just revert back to not
using bbox_inches, or is there an alternative?

Thanks!

f

Not a bug. There are only so many artist objects we assume for determining the tight bbox. Suptitle is not one of them. However, If you collect all of the artists that you want considered, you can pass in a list of them to bbox_artists (IIRC) to have them considered as well.

Now, with respect to the Basemap example, there might be a bug there, but it can still be worked around by passing in the list of labels to the kwarg.

Cheers!
Ben Root

···

On Sunday, January 29, 2012, Fernando Perez <fperez.net@…149…> wrote:

Hi all,

in ipython for the qtconsole and notebook, we send inline figures using

fig.canvas.print_figure(bytes_io, format=fmt, bbox_inches=‘tight’)

as seen here:

https://github.com/ipython/ipython/blob/master/IPython/core/pylabtools.py#L104

However, this produces truncated figure titles. Consider this code:

f, ax = plt.subplots()
ax.plot(rand(100))
ax.set_title(‘Axis title’)
f.suptitle(‘Figure title’);

which produces this in the notebook:

http://img546.imageshack.us/img546/5448/selection001c.png

As you can see, the figure title gets truncated.

We started using bbox_inches=‘tight’ because otherwise in many cases
the images were coming back with insane amounts of white padding, and

Stefan vdW suggested this fix. But it seems that in other cases it
actually loses data, which is even worse…

A slightly more complicated example, using basemap, not only truncates
the title but also all the x and y labels:

from mpl_toolkits.basemap import Basemap

lon0, lat0, lon1, lat1 = (84.38, 26.22, 88.9, 29.8)
resolution = ‘i’

parallels = np.linspace(lat0, lat1, 5)
meridians = np.linspace(lon0, lon1, 5)

f, ax = plt.subplots()
m = Basemap(lon0, lat0, lon1, lat1, resolution=resolution, ax=ax)
m.drawcountries(color=(1,1,0)) # country boundaries in pure yellow
m.drawrivers(color=(0,1,1)) # rivers in cyan

m.bluemarble() # NASA bluemarble image
m.drawmeridians(meridians, labels=[0,0,0,1], fmt=‘%.2f’)
m.drawparallels(parallels, labels=[1,0,0,0], fmt=‘%.2f’)
f.suptitle(‘The Himalayas’);

produces:

http://img192.imageshack.us/img192/986/selection002e.png

This looks like a matplotlib bug, but I have no idea how easy it is to

fix. In the meantime, should we in ipython just revert back to not
using bbox_inches, or is there an alternative?

Thanks!

f

The lat and lon labels in Basemap are axes.text objects, which
apparently are not considered when computing the bbox either.

-Jeff
···

On 1/29/12 8:42 AM, Benjamin Root wrote:

  On Sunday, January 29, 2012, Fernando Perez <fperez.net@...149...      > > wrote:

  > Hi all,

  >

  > in ipython for the qtconsole and notebook, we send inline

figures using

  >

  > fig.canvas.print_figure(bytes_io, format=fmt,

bbox_inches=‘tight’)

  >

  > as seen here:

  >

  > [https://github.com/ipython/ipython/blob/master/IPython/core/pylabtools.py#L104](https://github.com/ipython/ipython/blob/master/IPython/core/pylabtools.py#L104)

  >

  > However, this produces truncated figure titles.  Consider

this code:

  >

  > f, ax = plt.subplots()

  > ax.plot(rand(100))

  > ax.set_title('Axis title')

  > f.suptitle('Figure title');

  >

  > ###

  >

  > which produces this in the notebook:

  >

  > [http://img546.imageshack.us/img546/5448/selection001c.png](http://img546.imageshack.us/img546/5448/selection001c.png)

  >

  > As you can see, the figure title gets truncated.

  >

  > We started using bbox_inches='tight' because otherwise in

many cases

  > the images were coming back with insane amounts of white

padding, and

  > Stefan vdW suggested this fix.  But it seems that in other

cases it

  > actually loses data, which is even worse...

  >

  > A slightly more complicated example, using basemap, not only

truncates

  > the title but also all the x and y labels:

  >

  > from mpl_toolkits.basemap import Basemap

  >

  > lon0, lat0, lon1, lat1 = (84.38, 26.22, 88.9, 29.8)

  > resolution = 'i'

  >

  > parallels = np.linspace(lat0, lat1, 5)

  > meridians = np.linspace(lon0, lon1, 5)

  >

  > f, ax = plt.subplots()

  > m = Basemap(lon0, lat0, lon1, lat1, resolution=resolution,

ax=ax)

  > m.drawcountries(color=(1,1,0))  # country boundaries in pure

yellow

  > m.drawrivers(color=(0,1,1))  # rivers in cyan

  > m.bluemarble()  # NASA bluemarble image

  > m.drawmeridians(meridians, labels=[0,0,0,1], fmt='%.2f')

  > m.drawparallels(parallels, labels=[1,0,0,0], fmt='%.2f')

  > f.suptitle('The Himalayas');

  >

  > #####

  >

  > produces:

  >

  > [http://img192.imageshack.us/img192/986/selection002e.png](http://img192.imageshack.us/img192/986/selection002e.png)

  >

  >

  > This looks like a matplotlib bug, but I have no idea how easy

it is to

  > fix.  In the meantime, should we in ipython just revert back

to not

  > using bbox_inches, or is there an alternative?

  >

  > Thanks!

  >

  > f

  >



  Not a bug.  There are only so many artist objects we assume for

determining the tight bbox. Suptitle is not one of them.
However, If you collect all of the artists that you want
considered, you can pass in a list of them to bbox_artists (IIRC)
to have them considered as well.

  Now, with respect to the Basemap example, there might be a bug

there, but it can still be worked around by passing in the list of
labels to the kwarg.

  Cheers!

  Ben Root

Why is this the desired behavior?

-- Nathaniel

···

On Sun, Jan 29, 2012 at 7:42 AM, Benjamin Root <ben.root@...553...> wrote:

Not a bug. There are only so many artist objects we assume for determining the tight bbox. Suptitle is not one of them.

I was just going to ask the same. And as Jeff Whitaker points out,
all x and y (longitude/latitude) labels also get clipped.

I can understand not considering the position of arbitrarily laid out
text that a user could have put in a random location. But clipping
something that is provided by a standard api call, such as a figure
title, does seem much more like a bug than a feature to me, I'm afraid.

Cheers,

f

···

On Sun, Jan 29, 2012 at 10:22 AM, Nathaniel Smith <njs@...503...> wrote:

Not a bug. There are only so many artist objects we assume for determining the tight bbox. Suptitle is not one of them.

Why is this the desired behavior?

I meant to put at the end of that sentence: "in the basemap example".
The simple plot has no clipping issues with labels, only with the
suptitle.

Cheers,

f

···

On Sun, Jan 29, 2012 at 10:30 AM, Fernando Perez <fperez.net@...149...> wrote:

And as Jeff Whitaker points out,
all x and y (longitude/latitude) labels also get clipped.

I certainly have no objections. Most likely it was an oversight.

Ben Root

···

On Sunday, January 29, 2012, Fernando Perez <fperez.net@…149…> wrote:

On Sun, Jan 29, 2012 at 10:22 AM, Nathaniel Smith <njs@…503…> wrote:

Not a bug. There are only so many artist objects we assume for determining the tight bbox. Suptitle is not one of them.

Why is this the desired behavior?

I was just going to ask the same. And as Jeff Whitaker points out,

all x and y (longitude/latitude) labels also get clipped.

I can understand not considering the position of arbitrarily laid out
text that a user could have put in a random location. But clipping

something that is provided by a standard api call, such as a figure
title, does seem much more like a bug than a feature to me, I’m afraid.

Cheers,

f

OK, thanks. Filed so at least there's a record of it:

We'll find a workaround in ipython in the meantime.

Cheers,

f

···

On Sun, Jan 29, 2012 at 11:25 AM, Benjamin Root <ben.root@...553...> wrote:

I certainly have no objections. Most likely it was an oversight.

Not a bug. There are only so many artist objects we assume for determining the tight bbox. Suptitle is not one of them.

Why is this the desired behavior?

I was just going to ask the same. And as Jeff Whitaker points out,
all x and y (longitude/latitude) labels also get clipped.

I can understand not considering the position of arbitrarily laid out
text that a user could have put in a random location.

From matplotlib's perspective, the lat/lon labels in Basemap are randomly located text objects - so it's not likely to ever work for Basemap plots unless matplotlib takes into account all the artist objects associated with a figure.

-Jeff

···

On 1/29/12 11:30 AM, Fernando Perez wrote:

On Sun, Jan 29, 2012 at 10:22 AM, Nathaniel Smith<njs@...503...> wrote:
   But clipping
something that is provided by a standard api call, such as a figure
title, does seem much more like a bug than a feature to me, I'm afraid.

Cheers,

f

Ah, OK. That's good to know, because then it means that the automatic
use of 'tight' we make in ipython is probably over-aggressive then.
I'll add your note above to the bug report, and we'll think how to
best deal with it in ipython then.

Cheers,

f

···

On Sun, Jan 29, 2012 at 12:26 PM, Jeff Whitaker <jswhit@...196...> wrote:

From matplotlib's perspective, the lat/lon labels in Basemap are
randomly located text objects - so it's not likely to ever work for
Basemap plots unless matplotlib takes into account all the artist
objects associated with a figure.

My primary reason behind not accounting all the artists was that, in
general, Matplotlib does not know the exact bounding box of artists
when the artists are clipped.

In the current implementation, axes title, axis labels and ticklabels
are only accounted, and we have an optional parameter of
*bbox_extra_artists* so that users can manually change this behavior.

By the way, for now, I'm more inclined to change the behavior to
account all the texts artists (at least), although we may see white
space sometimes.

Regards,

-JJ

···

On Mon, Jan 30, 2012 at 5:26 AM, Jeff Whitaker <jswhit@...196...> wrote:

unless matplotlib takes into account all the artist
objects associated with a figure.

Please see if this PR works.

Regards,

-JJ

···

On Mon, Jan 30, 2012 at 1:03 PM, Jae-Joon Lee <lee.j.joon@...149...> wrote:

On Mon, Jan 30, 2012 at 5:26 AM, Jeff Whitaker <jswhit@...196...> wrote:

unless matplotlib takes into account all the artist
objects associated with a figure.

My primary reason behind not accounting all the artists was that, in
general, Matplotlib does not know the exact bounding box of artists
when the artists are clipped.

In the current implementation, axes title, axis labels and ticklabels
are only accounted, and we have an optional parameter of
*bbox_extra_artists* so that users can manually change this behavior.

By the way, for now, I'm more inclined to change the behavior to
account all the texts artists (at least), although we may see white
space sometimes.

Regards,

-JJ