First to explain the output from the question: When using the 2-tuple notation for bbox_to_anchor
, a bounding box without extent is created. The mode="expand"
will expand the legend horizontally into this bounding box, which has zero extend, effectively shrinking it to zero size.
The problem is that mode="expand"
will expand the legend only horizontally.
From the documentation:
mode
: {“expand”, None}
If mode is set to "expand" the legend will be horizontally expanded to fill the axes area (or bbox_to_anchor if defines the legend’s size).
For a solution you need to dig deep into the legend internals. First off you need to set the bbox-to-anchor with a 4-tuple, specifying also width and height of the bbox, bbox_to_anchor=(x0,y0,width,height)
, where all numbers are in normalized axes coordinates. Then you need to calculate the height of of the legend's _legend_box
. Since there is some padding being set, you need to subtract that padding from the bounding box's height. In order to calculate the padding the current legend's fontsize must be known. All of this has to take place after the axes' position is last changed.
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 2*np.pi, 100)
data = [np.sin(x * np.pi/float(el)) for el in range(1, 5)]
fig, ax = plt.subplots(1)
for key, el in enumerate(data):
ax.plot(x, el, label=str(key))
# legend:
leg = ax.legend(bbox_to_anchor=(1.04,0.0,0.2,1), loc="lower left",
borderaxespad=0, mode='expand')
plt.tight_layout(rect=[0,0,0.8,1])
# do this after calling tight layout or changing axes positions in any way:
fontsize = fig.canvas.get_renderer().points_to_pixels(leg._fontsize)
pad = 2 * (leg.borderaxespad + leg.borderpad) * fontsize
leg._legend_box.set_height(leg.get_bbox_to_anchor().height-pad)
plt.show()