Skip to main content
 首页 » 编程设计

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

2025年05月04日23linjiqin

假设我正在绘制一个包含五个项目的图,并且只有空间来创建一个包含 3 列的图例(比这更多的列会太宽),例如

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

最后一行中尾随的两个条目是左对齐的,在右侧留下了一个很大的空白空间,这在美学上不是很令人愉悦。当您必须标记非常多的行时,这会变得尤其成问题。

有什么方法可以让未填充行中的条目居中吗?

请您参考如下方法:

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() 

这里的缺点是图例没有边框,两个图例都需要单独定位。为了克服这个问题,需要更深入地挖掘并使用图例的一些私有(private)属性(注意,这些属性可能会因版本而异,恕不另行通知)。
然后,想法可以是在创建后删除第二个图例,并将其 _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()