协慌网

登录 贡献 社区

如何将传说从情节中删除

我有一系列 20 个图(不是子图)可以在一个图中制作。我希望传说能够在盒子之外。同时,我不想改变轴,因为图形的大小减少了。请帮助我以下查询:

  1. 我想将情节框保留在情节区域之外。 (我希望传说位于情节区域的右侧)。
  2. 无论如何,我减少了图例框内文本的字体大小,因此图例框的大小会很小。

答案

有很多方法可以做你想做的事。要添加 @inalis 和 @Navi 已经说过的内容,您可以使用bbox_to_anchor关键字参数将图例部分放置在轴外和 / 或减小字体大小。

在考虑减小字体大小(这可能会使事情难以阅读)之前,请尝试将图例放在不同的位置:

那么,让我们从一个通用的例子开始:

import matplotlib.pyplot as plt
import numpy as np

x = np.arange(10)

fig = plt.figure()
ax = plt.subplot(111)

for i in xrange(5):
    ax.plot(x, i * x, label='$y = %ix$' % i)

ax.legend()

plt.show()

替代文字

如果我们做同样的事情,但使用bbox_to_anchor关键字参数,我们可以稍微将图例bbox_to_anchor轴边界之外:

import matplotlib.pyplot as plt
import numpy as np

x = np.arange(10)

fig = plt.figure()
ax = plt.subplot(111)

for i in xrange(5):
    ax.plot(x, i * x, label='$y = %ix$' % i)

ax.legend(bbox_to_anchor=(1.1, 1.05))

plt.show()

替代文字

同样,你可以使图例更加水平和 / 或将它放在图的顶部(我也打开圆角和一个简单的阴影):

import matplotlib.pyplot as plt
import numpy as np

x = np.arange(10)

fig = plt.figure()
ax = plt.subplot(111)

for i in xrange(5):
    line, = ax.plot(x, i * x, label='$y = %ix$'%i)

ax.legend(loc='upper center', bbox_to_anchor=(0.5, 1.05),
          ncol=3, fancybox=True, shadow=True)
plt.show()

替代文字

或者,您可以缩小当前图的宽度,并将图例完全放在图的轴外:

import matplotlib.pyplot as plt
import numpy as np

x = np.arange(10)

fig = plt.figure()
ax = plt.subplot(111)

for i in xrange(5):
    ax.plot(x, i * x, label='$y = %ix$'%i)

# Shrink current axis by 20%
box = ax.get_position()
ax.set_position([box.x0, box.y0, box.width * 0.8, box.height])

# Put a legend to the right of the current axis
ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))

plt.show()

替代文字

以类似的方式,您可以垂直缩小绘图,并将水平图例放在底部:

import matplotlib.pyplot as plt
import numpy as np

x = np.arange(10)

fig = plt.figure()
ax = plt.subplot(111)

for i in xrange(5):
    line, = ax.plot(x, i * x, label='$y = %ix$'%i)

# Shrink current axis's height by 10% on the bottom
box = ax.get_position()
ax.set_position([box.x0, box.y0 + box.height * 0.1,
                 box.width, box.height * 0.9])

# Put a legend below current axis
ax.legend(loc='upper center', bbox_to_anchor=(0.5, -0.05),
          fancybox=True, shadow=True, ncol=5)

plt.show()

替代文字

看看matplotlib 图例指南 。你也可以看一下plt.figlegend() 。无论如何,希望有所帮助!

放置图例( bbox_to_anchor

使用plt.legendloc参数将图例定位在轴的边界框内。
例如loc="upper right"将图例放置在边界框的右上角,默认情况下,在轴坐标(或边界框符号(x0,y0, width, height)=(0,0,1,1)(0,0)(1,1)范围(0,0) (x0,y0, width, height)=(0,0,1,1) )。

要将图例放置在轴边界框之外,可以指定图例左下角的轴坐标的元组(x0,y0)

plt.legend(loc=(1.04,0))

但是,更通用的方法是使用bbox_to_anchor参数手动指定应放置图例的边界框。可以限制自己仅提供 bbox 的(x0,y0)部分。这将创建一个零跨度框,其中图例将按loc参数指定的方向展开。例如

plt.legend(bbox_to_anchor=(1.04,1), loc="upper left")

将图例放在轴外,使图例的左上角位于轴坐标的位置(1.04,1)

下面给出了进一步的示例,其中还示出了诸如modencols类的不同参数之间的相互作用。

在此输入图像描述

l1 = plt.legend(bbox_to_anchor=(1.04,1), borderaxespad=0)
l2 = plt.legend(bbox_to_anchor=(1.04,0), loc="lower left", borderaxespad=0)
l3 = plt.legend(bbox_to_anchor=(1.04,0.5), loc="center left", borderaxespad=0)
l4 = plt.legend(bbox_to_anchor=(0,1.02,1,0.2), loc="lower left",
                mode="expand", borderaxespad=0, ncol=3)
l5 = plt.legend(bbox_to_anchor=(1,0), loc="lower right", 
                bbox_transform=fig.transFigure, ncol=3)
l6 = plt.legend(bbox_to_anchor=(0.4,0.8), loc="upper right")

有关如何将 4 元组参数解释为bbox_to_anchor ,如l4 ,可以在此问题中找到。 mode="expand"在 4 元组给出的边界框内水平扩展图例。对于垂直展开的图例,请参阅此问题

有时,在图形坐标中指定边界框而不是轴坐标可能很有用。这在上面的示例l5显示,其中bbox_transform参数用于将图例放在图的左下角。

后期处理

将图例放置在轴外部通常会导致不希望的情况,即它完全或部分位于图形画布之外。

这个问题的解决方案是:

  • 调整子图参数
    可以通过使用plt.subplots_adjust调整子图参数,使得轴在图中占用较少的空间(从而为图例留出更多空间)。例如

    plt.subplots_adjust(right=0.7)

    在图的右侧留下 30%的空间,可以放置图例。

  • 布局紧张
    使用plt.tight_layout允许自动调整子图参数,使图中的元素紧贴图形边缘。不幸的是,在这种自动化中没有考虑图例,但我们可以提供一个矩形框,整个子图区域(包括标签)将适合。

    plt.tight_layout(rect=[0,0,0.75,1])
  • bbox_inches = "tight"保存数字
    这个论点bbox_inches = "tight"plt.savefig可以用来保存数字使得画布(包括图例)上的所有艺术家被装配到已保存的区域。如果需要,可自动调整图形大小。

    plt.savefig("output.png", bbox_inches="tight")
  • 自动调整 subplot params
    在这个答案中可以找到一种自动调整子图位置以使图例适合画布而不改变图形大小的方法: 创建具有精确尺寸且没有填充的图形(以及轴外的图例)

上述案例之间的比较:

在此输入图像描述

备择方案

图传说
人们可以使用图例而不是轴, matplotlib.figure.Figure.legend 。这对于 matplotlib 版本 > = 2.1 特别有用,它不需要特殊的参数

fig.legend(loc=7)

为图中不同轴的所有艺术家创建一个图例。使用loc参数放置图例,类似于它放置在轴内的方式,但是参考整个图形 - 因此它会稍微自动地在轴外。剩下的就是调整子图,使图例和轴之间没有重叠。从上面的“调整子图参数”这一点将会很有帮助。一个例子:

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0,2*np.pi)
colors=["#7aa0c4","#ca82e1" ,"#8bcd50","#e18882"]
fig, axes = plt.subplots(ncols=2)
for i in range(4):
    axes[i//2].plot(x,np.sin(x+i), color=colors[i],label="y=sin(x+{})".format(i))

fig.legend(loc=7)
fig.tight_layout()
fig.subplots_adjust(right=0.75)   
plt.show()

在此输入图像描述

专用子图轴内的图例
使用bbox_to_anchor的另一种方法是将图例放在其专用的子图轴( lax )中。由于图gridspec_kw={"width_ratios":[4,1]}图应小于图,我们可以在创建轴时使用gridspec_kw={"width_ratios":[4,1]} 。我们可以隐藏轴lax.axis("off")但仍然放入图例。图例句柄和标签需要通过h,l = ax.get_legend_handles_labels()从真实图中获得,然后可以提供给lax子图中的图例, lax.legend(h,l) 。下面是一个完整的例子。

import matplotlib.pyplot as plt
plt.rcParams["figure.figsize"] = 6,2

fig, (ax,lax) = plt.subplots(ncols=2, gridspec_kw={"width_ratios":[4,1]})
ax.plot(x,y, label="y=sin(x)")
....

h,l = ax.get_legend_handles_labels()
lax.legend(h,l, borderaxespad=0)
lax.axis("off")

plt.tight_layout()
plt.show()

这会产生一个视觉上非常类似于上图的情节:

在此输入图像描述

我们也可以使用第一个轴来放置图例,但是使用图例轴的bbox_transform

ax.legend(bbox_to_anchor=(0,0,1,1), bbox_transform=lax.transAxes)
lax.axis("off")

在这种方法中,我们不需要从外部获取图例句柄,但我们需要指定bbox_to_anchor参数。

进一步阅读和说明:

  • 考虑一下 matplotlib 图例指南,以及一些你想用传说做的其他东西的例子。
  • 在回答这个问题时可以直接找到一些用于为饼图放置图例的示例代码: Python - Legend 与饼图重叠
  • loc参数可以使用数字而不是字符串,这会使调用更短,但是,它们不是非常直观地相互映射。以下是参考的映射:

在此输入图像描述

只需在plot()调用后调用legend()调用,如下所示:

# matplotlib
plt.plot(...)
plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))

# Pandas
df.myCol.plot().legend(loc='center left', bbox_to_anchor=(1, 0.5))

结果看起来像这样:

在此输入图像描述