Python中绘图外的图例-matplotlib

Python中绘图外的图例-matplotlib,python,matplotlib,legend,Python,Matplotlib,Legend,我试图在matplotlib中的情节之外放置一个相当广泛的传说。图例有很多条目,每个条目都可能很长(但我不知道确切的长度) 显然,这很容易使用 legendHandle = plt.legend(loc = "center left", bbox_to_anchor = (1, 0.5)) 但问题是,图例被窗口的边缘切断了。我花了很长时间寻找解决方案。到目前为止,我能找到的最好的东西是: box = ax.get_position() ax.set_position([box.x0, box.

我试图在matplotlib中的情节之外放置一个相当广泛的传说。图例有很多条目,每个条目都可能很长(但我不知道确切的长度)

显然,这很容易使用

legendHandle = plt.legend(loc = "center left", bbox_to_anchor = (1, 0.5))
但问题是,图例被窗口的边缘切断了。我花了很长时间寻找解决方案。到目前为止,我能找到的最好的东西是:

box = ax.get_position()
ax.set_position([box.x0, box.y0, box.width * 0.8, box.height])
plt.legend(loc = "center left", bbox_to_anchor = (1, 0.5))
不幸的是,这并不能真正解决我的问题。由于应用于长方体宽度的显式系数为0.8,因此这仅适用于图形和图例宽度的一个特定组合。如果我调整了“体形”窗口的大小,或者如果我的图例条目具有不同的长度,那么它将不起作用

我只是不明白在人物之外放置一个图例怎么会如此困难。我习惯了Matlab,它很简单

legend('Location', 'eastoutside')

Python中是否有我所缺少的类似内容?

在尝试了很多之后,这是我能想到的最好方法:

from matplotlib.lines import Line2D
from matplotlib.gridspec import GridSpec
from enum import Enum

class Location(Enum):
    EastOutside = 1
    WestOutside = 2
    NorthOutside = 3
    SouthOutside = 4

class Legend:
    def __init__(self, figure, plotAxes, location: Location):
        self.figure = figure
        self.plotAxes = plotAxes
        self.location = location

        # Create a separate subplot for the legend. Actual location doesn't matter - will be modified anyway.
        self.legendAxes = figure.add_subplot(1, 2, 1)
        self.legendAxes.clear() # remove old lines
        self.legendAxes.set_axis_off()

        # Add all lines from the plot to the legend subplot
        for line in plotAxes.get_lines():
            legendLine = Line2D([], [])
            legendLine.update_from(line)
            self.legendAxes.add_line(legendLine)

        if self.location == Location.EastOutside:
            self.legend = self.legendAxes.legend(loc = "center left")
        elif self.location == Location.WestOutside:
            self.legend = self.legendAxes.legend(loc = "center right")
        elif self.location == Location.NorthOutside:
            self.legend = self.legendAxes.legend(loc = "lower center")
        elif self.location == Location.SouthOutside:
            self.legend = self.legendAxes.legend(loc = "upper center")
        else:
            raise Exception("Unknown legend location.")

        self.UpdateSize()

        # Recalculate legend size if the size changes
        figure.canvas.mpl_connect('resize_event', lambda event: self.UpdateSize())

    def UpdateSize(self):
        self.figure.canvas.draw() # draw everything once in order to get correct legend size

        # Extract legend size in percentage of the figure width
        legendSize = self.legend.get_window_extent().inverse_transformed(self.figure.transFigure)
        legendWidth = legendSize.width
        legendHeight = legendSize.height

        # Update subplot such that it is only as large as the legend
        if self.location == Location.EastOutside:
            gridspec = GridSpec(1, 2, width_ratios = [1 - legendWidth, legendWidth])
            legendLocation = 1
            plotLocation = 0
        elif self.location == Location.WestOutside:
            gridspec = GridSpec(1, 2, width_ratios = [legendWidth, 1 - legendWidth])
            legendLocation = 0
            plotLocation = 1
        elif self.location == Location.NorthOutside:
            gridspec = GridSpec(2, 1, height_ratios = [legendHeight, 1 - legendHeight])
            legendLocation = 0
            plotLocation = 1
        elif self.location == Location.SouthOutside:
            gridspec = GridSpec(2, 1, height_ratios = [1 - legendHeight, legendHeight])
            legendLocation = 1
            plotLocation = 0
        else:
            raise Exception("Unknown legend location.")

        self.legendAxes.set_position(gridspec[legendLocation].get_position(self.figure))
        self.legendAxes.set_subplotspec(gridspec[legendLocation]) # to make figure.tight_layout() work if that's desired

        self.plotAxes.set_position(gridspec[plotLocation].get_position(self.figure))
        self.plotAxes.set_subplotspec(gridspec[plotLocation]) # to make figure.tight_layout() work if that's desired
这使得我到目前为止测试的案例中的图例或多或少是正确的。用法是

import matplotlib.pyplot as plt

plt.ion()

figure = plt.figure()

plotAxes = figure.gca()

plotAxes.plot([1, 2, 3], [4, 5, 6], "b-", label = "Testaaaaaaaaaaaaaa 1")
plotAxes.plot([1, 2, 3], [6, 5, 4], "r-", label = "Test 2")

legend = Legend(figure, plotAxes, Location.EastOutside)

让我问一下我已经在评论中发布的问题。。。我将如何向matplotlib开发人员推荐此功能作为附加功能?(不是我的黑客,而是将图例置于图形之外的一种自然方式)

刚刚偶然发现了这个问题,我认为您可以使用图形的图例方法:

import matplotlib.pyplot as plt

# sample data
x1 = [1,2,3,4,5,6]
y1 = [0,1,0,1,0,1]
x2 = [1,2,3,4,5,6]
y2 = [9,8,8,7,8,6]

fig = plt.figure(figsize=(4, 2), dpi=100)
ax = fig.add_subplot(111)

ax.plot(x1,y1,x2,y2)

fig.legend(loc=1, mode='expand', numpoints=1, ncol=4, fancybox = True,
           fontsize='small', labels=['d1', 'd2'])
# loc=1 means at the top-right of the figure

plt.show()
尽管图例位于轴的外部,但如果图例太大,它仍可能与轴重叠


图例的放置可以使用参数进行控制 locbbox\U至\U锚定。 下面是一个代码示例:

import matplotlib.pyplot as plt

#sample data
import numpy as np
x = np.linspace(0, 2, 101)

#create figure and its axes
fig = plt.figure()
ax = fig.add_axes([0,0,1,1])

#plot 3 lines and define their labels
ax.plot(x, x**2, label="square")
ax.plot(x, x**3, label="cubic")
ax.plot(x, np.sin(x), label="sinus")

#place the legend
ax.legend(loc='lower center', bbox_to_anchor=(0.5, 1.0), ncol=3)
  # The center of the lower edge of the rectangle containing the legend
  # is placed at coordinates (x,y)=(0.5,1.0) of ax. 
  # Thus, figure and legend should not overlap.

plt.show()
您现在应该看到下图:

我不知道有什么类似于Matlab的东西。总figsize对您有多重要?如果不重要,请尝试以下操作:如果重要,请尝试创建两个子图:一个子图用于绘图,另一个子图用于图例。然后,您可以在空子地块中创建虚拟艺术家(基本上为不存在的图形设置相同的线/标记样式),并在图例中显示这些虚拟艺术家:感谢您的回复!总体形大小并没有那么重要。显然,我想让它填满任何大小的窗口。。。因此,如果我的窗口是全屏的,则图形应该更大,如果我的窗口仅覆盖一半屏幕,则图形应该适当缩小以适应图例,依此类推。(续)您建议的第一个解决方案的问题是,它只在保存图形后才起作用。为了提供更多背景,我有一个(单独的)tkinter窗口,我在其中控制显示哪些行,等等。这也是我不知道图例大小的原因。(续)我将看看第二个解决方案。我以前考虑过这一点,但这将是非常乏味的,因为(a)有很多行,(b)行根据tkinter窗口中的设置而变化,所以我不能只硬编码样式。这真是太讨厌了。。。您/其他人是否知道向matplotlib团队建议此功能是否有意义?对我来说,这似乎是一件非常基本的事情,以至于我真的很惊讶它竟然没有出现。对于散点图这样的东西,在散点图中,图例项看起来就像是实际数据,如果它们在图表中的话。