Cupitor
Cupitor

Reputation: 11637

Alignment of text and the icon in the legend box (matplotlib)

I want to reduce the size of the text in the legend of my plot so that the tikz numbers could stay relatively large. The problem is when I reduce the size of the font in the legend, the alignment between the icon in the legend and the text get impaired. How can I pair them again? The following is a test:

import numpy as np
from  matplotlib import pyplot as plt
plt.rc('text', usetex = True)
font = {'family' : 'normal',
        'weight' : 'normal',
        'size'   : 25}
plt.rc('font', **font)
fig, ax = plt.subplots(1, 1)
a = np.arange(10)
b = np.random.randn(10)
ax.errorbar(a, b, yerr=0.5, fmt='o', color='g', ecolor='g', capthick=1, linestyle = '-', linewidth=2, elinewidth=1, label = "Test")
legend = plt.legend(bbox_to_anchor=(0., 1.02, 1., .102), loc=2, ncol=6, mode="expand", borderaxespad=0.2)
plt.setp(legend.get_texts(), fontsize='15') #legend 'list' fontsize
fig.set_size_inches(14.5, 10.5)
plt.savefig('/Users/Cupitor/Test.png')

As you can appreciate in the below picture the text in the legend is not any more central with respect to the green figure on its left: enter image description here

Upvotes: 2

Views: 6020

Answers (1)

Jean-Sébastien
Jean-Sébastien

Reputation: 2697

I am experiencing the same issue in Python 2.7 and 3.3, Ubuntu 15.04, matplotlib 1.4.2, and using the Agg backend. It seems that after setting directly the fontsize of the legend's text artists, either through plt.setp or using the set_size method, the text does not properly center-aligned vertically with its corresponding handle, no matter the option used for the vertical alignment of the text ('center', 'top', 'bottom', 'baseline').

The only way I've found to address this issue is to manually align the height of the bounding boxes of the text to the height of the bounding boxes of the handles. Based on the code you provided in your OP, below is an example that shows how this can be done:

import matplotlib as mpl
mpl.use('TkAgg')
import numpy as np
from  matplotlib import pyplot as plt

plt.close('all')

plt.rc('text', usetex = True)
font = {'family' : 'normal',
        'weight' : 'normal',
        'size'   : 25}
plt.rc('font', **font)

fig = plt.figure()
fig.set_size_inches(14.5, 10.5)
ax = fig.add_axes([0.1, 0.1, 0.85, 0.75])

#---- plot some data ----

ax.plot(np.arange(10), np.random.randn(10), 'rd', ms=15,
        label='The quick brown fox...')

ax.errorbar(np.arange(10), np.random.randn(10), yerr=0.5, fmt='o',
            color='g', ecolor='g', capthick=1,ls = '-', lw=2, elinewidth=1, 
            label = "... jumps over the lazy dog.")

#---- plot legend ----

legend = plt.legend(bbox_to_anchor=(0., 1.02, 1., 0.), handletextpad=0.3,
                    loc='lower left', ncol=6, mode="expand", borderaxespad=0,
                    numpoints=1, handlelength=0.5)

#---- adjust fontsize and va ----

plt.setp(legend.get_texts(), fontsize='15', va='bottom')

#---- set legend's label vert. pos. manually ----

h = legend.legendHandles
t = legend.texts
renderer = fig.canvas.get_renderer()

for i in range(len(h)):
    hbbox = h[i].get_window_extent(renderer) # bounding box of handle
    tbbox = t[i].get_window_extent(renderer) # bounding box of text

    x = tbbox.x0 # keep default horizontal position    
    y = (hbbox.height - tbbox.height) / 2. + hbbox.y0 # vertically center the
    # bbox of the text to the bbox of the handle.

    t[i].set_position((x, y)) # set new position of the text

plt.show(block=False)
plt.savefig('legend_text_va.png')

which results in:

enter image description here

Setting va='bottom' in plt.setp seems to yield better results. I believe that the bbox of the text is more accurate when the vertical alignment is set to bottom, but I haven't investigated further to confirm this.

Upvotes: 2

Related Questions