[Python] How to have a matplotlib legend spawn the plot areas of the subplots?

Discussão em 'Python' iniciado por Stack, Agosto 11, 2025.

  1. Stack

    Stack Membro Participativo

    I want to have the shared legend of multiple subplots take the width of the plot areas of the subplots.

    Here is an illustration:

    [​IMG]

    Here's what I tried / a minimum reproducible example of what I expect should work based on the documentation:


    import matplotlib.pyplot as plt

    fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(7, 4), layout="constrained")

    # plot some dummy data
    x = [1, 2, 3, 4, 5]
    y1 = [1, 2, 3, 4, 5]
    y2 = [5, 4, 3, 2, 1]
    axes[0].plot(x, y1, label="Line 1")
    axes[1].plot(x, y1, label="Line 1")
    axes[1].plot(x, y2, label="Line 2")


    def find_ideal_bbox(axes):
    # Use the box that spans the combined plot width (excluding figure side margins)
    # Collect axes positions in figure coordinates
    positions = [ax.get_position() for ax in axes]
    if positions:
    x0 = min(p.xmin for p in positions)
    x1 = max(p.xmax for p in positions)
    # Full vertical extent (0->1) so outside locations can align horizontally;
    # width restricted to the subplot grid width.
    return (x0, 0.0, x1 - x0, 1.0)
    return None


    # Collect all handles and labels from all axes
    handles, labels = [], []
    for ax in axes:
    h, label_list = ax.get_legend_handles_labels()
    for handle, label in zip(h, label_list):
    if label not in labels: # Avoid duplicates
    handles.append(handle)
    labels.append(label)


    # add one legend for both axes
    fig.legend(
    handles,
    labels,
    loc="outside upper center",
    ncol=2,
    bbox_to_anchor=find_ideal_bbox(axes),
    mode="expand",
    borderaxespad=-1, # move it outside the figure
    columnspacing=1.0, # Reduce space between columns (default is 2.0)
    handletextpad=0.4,
    # Reduce space between symbol and text (default is 0.8)
    )


    # finally, save the figure in three different sizes
    widths = [None, 3.16, 4.21]

    # make sure they have the same aspect ratio
    original_size = fig.get_size_inches()
    aspect_ratio = original_size[1] / original_size[0]
    for w in widths:
    if w is not None:
    fig.set_size_inches(w, w * aspect_ratio)

    # re-set the legend size
    fig.legends[0].set_bbox_to_anchor(find_ideal_bbox(axes))

    fig.savefig(
    f"mvr_figure_{w}.png" if w is not None else "mvr_figure.png",
    bbox_inches="tight",
    )


    Here's what this results in:

    [​IMG]

    [​IMG]

    [​IMG]

    As can bee seen in these examples, the legend is too wide in all cases, and is always slightly too much to the left. Note the layout="constrained" – without this, the opposite can be observed, the legend is always a bit too long to the right.

    The strangest thing to me is that I can debug the find_ideal_bbox method by simply using fig.text() on the coordinates that are found, and the text is placed where I expect it to.

    What am I missing? Why are the legends not using the bbox I tell them to?

    Continue reading...

Compartilhe esta Página