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)
# Reset 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",
)
Вот что это дает:
Как видно из этих примеров, легенда во всех случаях слишком широкая и всегда немного смещена влево. Обратите внимание на макет="constrained". Без этого можно наблюдать обратное; легенда всегда слишком длинная справа.
Самое странное для меня то, что я могу отлаживать метод find_ideal_bbox, просто используя fig.text() для найденных координат, и текст размещается там, где я ожидаю.
Что мне не хватает? Почему легенды не используют bbox, о котором я им говорю?
Я хочу, чтобы общая легенда нескольких подграфиков занимала ширину областей графика подграфиков. Вот иллюстрация: [img]https://i.sstatic.net/E4M2mxaZ.png[/img]
Вот что я пробовал / минимальный воспроизводимый пример то, что, как я ожидаю, должно работать, основано на документации: [code]import matplotlib.pyplot as plt
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)
# Reset 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", ) [/code] Вот что это дает: [img]https://i.sstatic.net/AtwJRC8J.png[/img]
[img]https://i.sstatic.net/bZvXax0U.png[/img]
[img]https://i.sstatic.net/51oLy3dH.png[/img]
Как видно из этих примеров, легенда во всех случаях слишком широкая и всегда немного смещена влево. Обратите внимание на макет="constrained". Без этого можно наблюдать обратное; легенда всегда слишком длинная справа. Самое странное для меня то, что я могу отлаживать метод find_ideal_bbox, просто используя fig.text() для найденных координат, и текст размещается там, где я ожидаю. Что мне не хватает? Почему легенды не используют bbox, о котором я им говорю?