4

I want to move the legend of a figure with several subfigures to the upper center relative to the entire figure. I tried to pass a legend object created with fig.legend to fig.savefig using bbox_extra_artist, as described here. However unlike described the legend gets cut-off:

enter image description here

This is the code that I used:

import matplotlib.pyplot as plt
import numpy as np

x = np.arange(-2*np.pi, 2*np.pi, 0.1)
fig, axes = plt.subplots(nrows=5, ncols=3, sharex=True, sharey=False)
legendLines = []
for rI, rA in enumerate(axes):
    for cI, ax in enumerate(rA):
        line, = ax.plot(x, np.sin(x), label='Sine')
        legendLines.append(line)
        line, = ax.plot(x, np.cos(x), label='Cosine')
        legendLines.append(line)
        line, = ax.plot(x, np.arctan(x), label='Inverse tan')
        legendLines.append(line)
        if cI == 0:
            ax.set_ylabel('foo')
        if rI == len(axes) -1:
            ax.set_xlabel('bar')
        if rI == 0:
            ax.set_title('baz')

legend = fig.legend(legendLines[:3], ['Sine', 'Cosine', 'Inverse Tan'], 'upper center', frameon=False, ncol=3, borderaxespad=-0.7)
outFile = 'test.pdf'
fig.set_size_inches(8,7)
fig.tight_layout()
fig.savefig(outFile, bbox_extra_artists=[legend], bbox_inches='tight')
plt.close()

I'm looking for a technique that let me define the legend location relative to the entire figure not just a subfigure.

Update:

If I replace

legend = fig.legend(legendLines[:3], ['Sine', 'Cosine', 'Inverse Tan'], 'upper center', frameon=False, ncol=3, borderaxespad=-0.7)

with

legend = fig.legend(legendLines[:3], ['Sine', 'Cosine', 'Inverse Tan'], loc='lower center', frameon=False, ncol=3, bbox_to_anchor=(.5, 0.965))

the result is the same: plot area is not increased and legend gets shifted off the plot.

(System: matplotlib-1.2.0-py2.7-macosx-10.8-intel)

Community
  • 1
  • 1
Faber
  • 1,504
  • 2
  • 13
  • 21

2 Answers2

2

If you replace the line that generates the legend with this one:

legend = fig.legend(legendLines[:3], ['Sine', 'Cosine', 'Inverse Tan'], loc='lower center', frameon=False, ncol=3, bbox_to_anchor=(.5, 1.))

everything seems to work fine.

Even if bbox_to_anchor=(0.5, 1.5) the legends is saved into pdf fine:

enter image description here

Which might mean that updating matplotlib to the latest version is likely to fix the problem.

Primer
  • 10,092
  • 5
  • 43
  • 55
  • Can you paste output of this: `plt.rcParams['savefig.pad_inches']` – Primer Apr 09 '15 at 17:39
  • Output of `plt.rcParams['savefig.pad_inches']` is `0.1` (before and after plotting) – Faber Apr 09 '15 at 17:56
  • Sorry, can't reproduce the problem... However far from the ax the legend is being pushed here it gets captured with `savefig` correctly. You may want to try to increase the padding adding this to `savefig`: `pad_inches=1`. (but it seems like unlikely solution). The higher the number the more everything gets squeezed into the figure. See example in my answer. – Primer Apr 09 '15 at 18:41
  • Hmm … seems like it's working for you. Can you tell me your OS and python/matplotlib version? Thx – Faber Apr 09 '15 at 18:45
  • @Primer's answer works for me. OSX 10.10.2, matplotlib 1.4.3, python 3.4.3. – AGS Apr 09 '15 at 18:51
  • I am on Windows 8.1, matplotlib 1.4.3, python 2.7.9 – Primer Apr 09 '15 at 19:02
0

Updating to the latest matplotlib version 1.4.3 solved the problem.

Faber
  • 1,504
  • 2
  • 13
  • 21
  • Are you saying that even with `borderaxespad=-0.7` and without `bbox_to_anchor` in parameters of the `fig.legend` you were able to have the legend fully captured in pdf? Cause it did not work for me. – Primer Apr 09 '15 at 22:16
  • I guess `bbox_to_anchor` is the way to go. However, `loc='upper center', frameon=False, ncol=3, borderaxespad=0` without `bbox_to_anchor` gives also the desired result. Thx for your hints! – Faber Apr 09 '15 at 23:13