-1

I am trying to show legends for each subplot in the figure that fit properly in the ax size (autoscaling fontsize, boxsize, etc.). I have tried many ways to fit legends but I have not found a non-intrincate manner. For example, in the answers to another posts (e.g. How to change legend size with matplotlib.pyplot) the user have to decide the legend size (fontsize and titlesize) but I am asking for a way to automatically fit the legend inside axes in a figure with multiple subplots.

I want the legend size to fit inside each ax in the Figure 1 created with the example code attached with ax.legend() line commented. In Figure 2 I show the figure obtained with ax.legend() line active : legends do not fit inside axes. Attached code is a modified version of the example in Matplotlib Examples : Creating multiple subplots using plt.subplots

import matplotlib.pyplot as plt
import numpy as np

# Some example data to display
x = np.linspace(0, 2 * np.pi, 400)
a = (x ** 2)
b = (x ** 3)
c = (x ** 4)
functions = [a,b,c]

label = "This is a long label for a legend and I try to fit to its ax"
fig, axs = plt.subplots(2, 2)
for y in functions :
    axs[0, 0].plot(x, y, label=label)
    axs[0, 1].plot(x, -y, label=label)
    axs[1, 0].plot(x, y, label=label)
    axs[1, 1].plot(x, -y, label=label)

for ax in axs.flat:
    ax.set_title('Title')
    ax.set(xlabel='x-label', ylabel='y-label')
    ax.legend() # commented for the Figure 1 and active for the Figure 2
fig.tight_layout()

Thank you in advance.

allabur
  • 38
  • 3

1 Answers1

0

Changing the size of the figure away from the default Matplotlib figure sizes should be able to help with your problem (I personally like to use plt.rcParams but just know that this changes all figure sizes downstream). At a certain point, if your legend is still too big even with a big figure, then you should consider editing the length of your legend. The point of graphs is to give the reader a snapshot understanding of your data. If they have to read a paragraph just in the legend, then the graph is not structured correctly.

import matplotlib.pyplot as plt
import numpy as np

# Some example data to display
x = np.linspace(0, 2 * np.pi, 400)
a = (x ** 2)
b = (x ** 3)
c = (x ** 4)
functions = [a,b,c]

label = "This is a long label for a legend and I try to fit to its ax"
plt.rcParams["figure.figsize"] = (20,10)
fig, axs = plt.subplots(2, 2)
for y in functions :
    axs[0, 0].plot(x, y, label=label)
    axs[0, 1].plot(x, -y, label=label)
    axs[1, 0].plot(x, y, label=label)
    axs[1, 1].plot(x, -y, label=label)

for ax in axs.flat:
    ax.set_title('Title')
    ax.set(xlabel='x-label', ylabel='y-label')
    ax.legend() # commented for the Figure 1 and active for the Figure 2
fig.tight_layout()

Output graph:enter image description here

~~ Some more control ~~

To not change the figure size at all but only change the legend size based on the length of the legend (i.e. Dynamically) try something like this. It defines the max size that the legend will get (to stop short legend texts from creating huge legends), finds the length of the text in the legend, finds what the legend Size should be (but not larger than you set max legend size), and sets the legend size accordingly.

import matplotlib.pyplot as plt
import numpy as np

# Some example data to display
x = np.linspace(0, 2 * np.pi, 400)
a = (x ** 2)
b = (x ** 3)
c = (x ** 4)
functions = [a,b,c]

label1 = "This is a long label for a legend and I try to fit to its ax"
label2 = "Short label"
label3 = "This is a long label for a legend and I try to fit to its ax, even longer for no reason basically a paragraph look at this thing keep going"
label4 = "This is a long label for a legend"
labels=[label1, label2, label3, label4]
fig, axs = plt.subplots(2, 2)
for y in functions :
    axs[0, 0].plot(x, y, label=label1)
    axs[0, 1].plot(x, -y, label=label2)
    axs[1, 0].plot(x, y, label=label3)
    axs[1, 1].plot(x, -y, label=label4)

maxLegendSize = 2
for ax, label in zip(axs.flat, labels):
    ax.set_title('Title')
    ax.set(xlabel='x-label', ylabel='y-label')
    legendSize = maxLegendSize * (100/len(label))
    if legendSize > maxLegendSize:
        legendSize = maxLegendSize
    ax.legend(prop={'size': legendSize})
fig.tight_layout()
plt.show()

Default figure size (with maxLegendSize = 2): enter image description here

With a changed figure size and thus a changed maxLegendSize = 10: enter image description here

Michael S.
  • 3,050
  • 4
  • 19
  • 34
  • Thanks for your answer @MichaelS. I knew this method but I wanted to know if there is a way to fit the legend automatically inside the axes of the subplot without changing other parameters because I did not find a way to do it (may be is not possible). – allabur Jul 29 '22 at 13:30
  • @allabur check the edit. I'm not sure why you don't want to just change the figure size as it would most likely be the easiest solution but the edit shows a way to tailor it to the length of the label. – Michael S. Jul 29 '22 at 16:06
  • Thank you @MichaelS., I was looking for a way to do it automatically because I have to graph a hundred figures with different subplots and legends and I was wondering how could I don't take care of the scale of legend. As I figured out, this is not an easy task to do. Again, thanks for your answer and I hope more people will try to give their point of view or technique to use. – allabur Aug 03 '22 at 14:24