0

How do I set the background box size of legends in matplotlib?


Consider the left graph row alignment in pythons matplotlib (code posted in the end). The legends have been moved to the right because otherwise it covers essential data. They align vertically. But I feel like the different horizontal widths of the legends are kind of edgy. Therefore I want to change them to all have the same background box as shown in the right graph.

Is there any way to achieve that?

Current output Wanted output
Row alignment of graphs Desired alignment of the legends

Code to reproduce the shown example:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec

loc = "center left"
x = np.linspace(-2 * np.pi, 2 * np.pi, 100)

fig = plt.figure(figsize=(5, 5), constrained_layout=True)
gs = GridSpec(3, 1, figure=fig)

ax = fig.add_subplot(gs[0,:])
ax.plot(x, np.tan(x), label=r"$\tan(x) = \frac{\sin(x)}{\cos(x)}$")
ax.plot(x, np.tan(2*x), label=r"$\tan(2x)$")
ax.plot(x, np.tan(3*x), label=r"$\tan(3x)$")
ax.legend(loc=loc, bbox_to_anchor=(1.02, 0.5))

ax = fig.add_subplot(gs[1,:])
ax.plot(x, np.sin(x), label=r"$\sin(x)$")
ax.plot(x, np.sin(2*x), label=r"$\sin(2x)$")
ax.plot(x, np.sin(4*x), label=r"$\sin(4x)$")
ax.legend(loc=loc, bbox_to_anchor=(1.02, 0.5))

ax = fig.add_subplot(gs[2,:])
ax.plot(x, np.sin(x) * np.cos(x), label=r"$\sin(x) \times \cos(x)$")
ax.plot(x, np.sin(2*x) * np.cos(2*x), label=r"$\sin(2x) \times \cos(2x)$")
ax.plot(x, np.sin(3*x) * np.cos(3*x), label=r"$\sin(3x) \times \cos(3x)$")
ax.legend(loc=loc, bbox_to_anchor=(1.02, 0.5))

plt.show()
miile7
  • 2,547
  • 3
  • 23
  • 38
  • 1
    DId you look into [Fix size of legend in matplotlib](https://stackoverflow.com/questions/41026125/fix-size-of-legend-in-matplotlib) suggesting to use `bbox_to_anchor=(x0, y0, width, height)`? – JohanC Apr 20 '21 at 08:56
  • @JohanC That looks very promising, I will check it out. Thank you – miile7 Apr 20 '21 at 08:58
  • @JohanC Ok, that only changes the bounding box (as I requested in the question). I'm more interested to change the "background rectangular" of the legend. I will modify my question to make that more clear. Still thanks for the suggestion. – miile7 Apr 20 '21 at 09:01
  • 2
    add in `mode='expand'` to get the box to fill the bbox defined with `bbox_to_anchor`, as suggested by @JohanC – tmdavison Apr 20 '21 at 09:09
  • @tmdavision That works, together with resizing the grid spec. Thank you. Does anyone of you want to create an answer? I will mark it as solved. If not I'll answer my own question. – miile7 Apr 20 '21 at 12:24
  • I’d just remove the box. – Jody Klymak Apr 20 '21 at 14:41

1 Answers1

0

As mentioned in the comments by @JohanC and @tmdavision, one can use the bbox_to_anchor=(x0, y0, width, height) together with mode='expand' to create the desired output. The following code shows the modifications.

Note that it is necessary to change the grid spec since it does not resize properly otherwise.

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec

loc = "center left"
x = np.linspace(-2 * np.pi, 2 * np.pi, 100)

fig = plt.figure(figsize=(5, 5), constrained_layout=True)
gs = GridSpec(3, 5, figure=fig)

ax = fig.add_subplot(gs[0,:3])
ax.plot(x, np.tan(x), label=r"$\tan(x) = \frac{\sin(x)}{\cos(x)}$")
ax.plot(x, np.tan(2*x), label=r"$\tan(2x)$")
ax.plot(x, np.tan(3*x), label=r"$\tan(3x)$")
ax.legend(loc=loc, bbox_to_anchor=(1.02, 0.5, 0.7, 0.1), mode="expand")

ax = fig.add_subplot(gs[1,:3])
ax.plot(x, np.sin(x), label=r"$\sin(x)$")
ax.plot(x, np.sin(2*x), label=r"$\sin(2x)$")
ax.plot(x, np.sin(4*x), label=r"$\sin(4x)$")
ax.legend(loc=loc, bbox_to_anchor=(1.02, 0.5, 0.7, 0.1), mode="expand")

ax = fig.add_subplot(gs[2,:3])
ax.plot(x, np.sin(x) * np.cos(x), label=r"$\sin(x) \times \cos(x)$")
ax.plot(x, np.sin(2*x) * np.cos(2*x), label=r"$\sin(2x) \times \cos(2x)$")
ax.plot(x, np.sin(3*x) * np.cos(3*x), label=r"$\sin(3x) \times \cos(3x)$")
ax.legend(loc=loc, bbox_to_anchor=(1.02, 0.5, 0.7, 0.1), mode="expand")

plt.show()
miile7
  • 2,547
  • 3
  • 23
  • 38