在不完整/未填充的行中将matplotlib图例条目居中?

0qx6xfy6  于 2023-10-24  发布在  其他
关注(0)|答案(1)|浏览(120)

假设我正在制作一个有五个项目的图,并且只有空间创建一个有三列的图例(比这更多的列太宽了),例如。

import matplotlib.pyplot as plt
fig, ax = plt.subplots()
for i in range(5):
    ax.plot(np.arange(10), np.random.rand(10), label='Item #%d' % i)
ax.legend(ncol=3)

最后一行的两个条目是左对齐的,在右边留下了一个很大的空白,这不是很美观。当你必须标记大量的行时,这就变得特别有问题。
有没有办法将未填充行中的条目居中?

zfciruhq

zfciruhq1#

matplotlib图例是基于列的。您不能让图例条目跨越多个列。也就是说,当然可以在奇数行的图例中“居中”奇数个条目,同样也可以在偶数行的图例中“居中”偶数个条目。这可以通过在外部位置使用空的艺术家来完成。

import matplotlib.pyplot as plt
import numpy as np

ncols = 3  # or 4
nlines = 7 # or 10

x = np.linspace(0,19)
f = lambda x,p: np.sin(x*p)
h = [plt.plot(x,f(x,i/10.), label="Line {}".format(i))[0] for i in range(nlines)]
h.insert(nlines//ncols, plt.plot([],[],color=(0,0,0,0), label=" ")[0])
plt.legend(handles=h, ncol=ncols, framealpha=1)

plt.show()

如果列数为奇数,图例条目数为偶数,或者相反,则无法执行上述操作。可以选择使用两个不同的图例,并将它们放置在彼此的下方,这样看起来最后一行的条目居中。

import matplotlib.pyplot as plt
import numpy as np

ncols = 3
nlines = 8

x = np.linspace(0,19)
f = lambda x,p: np.sin(x*p)
h = [plt.plot(x,f(x,i/10.), label="Line {}".format(i))[0] for i in range(nlines)]

kw = dict(framealpha=1, borderaxespad=0, bbox_to_anchor=(0.5,0.2), edgecolor="w")
leg1 = plt.legend(handles=h[:nlines//ncols*ncols], ncol=ncols, loc="lower center", **kw)
plt.gca().add_artist(leg1)
leg2 = plt.legend(handles=h[nlines//ncols*ncols:], ncol=nlines-nlines//ncols*ncols, 
                   loc="upper center", **kw)

plt.show()

这里的缺点是图例没有边框,两个图例都需要单独定位。为了克服这个问题,需要深入挖掘并使用图例的一些私有属性(注意,这些属性可能会在不通知的情况下随版本而变化)。
然后,可以在创建后删除第二个图例,并将其_legend_handle_box放置到第一个图例中。

import matplotlib.pyplot as plt
import numpy as np

ncols = 3
nlines = 8

x = np.linspace(0,19)
f = lambda x,p: np.sin(x*p)
h = [plt.plot(x,f(x,i/10.), label="Line {}".format(i))[0] for i in range(nlines)]

kw = dict(framealpha=1, bbox_to_anchor=(0.5,0.2))
leg1 = plt.legend(handles=h[:nlines//ncols*ncols], ncol=ncols, loc="lower center", **kw)
plt.gca().add_artist(leg1)
leg2 = plt.legend(handles=h[nlines//ncols*ncols:], ncol=nlines-nlines//ncols*ncols)

leg2.remove()
leg1._legend_box._children.append(leg2._legend_handle_box)
leg1._legend_box.stale = True

plt.show()

相关问题