重叠补丁(又名艺术家)的问题在于您在创建图例时如何定义手柄和标签。引用matplotlib图例指南 https://matplotlib.org/tutorials/intermediate/legend_guide.html:
默认的 handler_map 有一个特殊的元组处理程序(legend_handler.HandlerTuple),它简单地为给定元组中的每一项绘制句柄。
让我们首先检查一下您作为示例给出的图例的结构。任何可迭代对象都可以用于句柄和标签,因此我选择将它们存储为列表,与图例指南中给出的一些示例一致并使代码更清晰:
ax2.legend([(expt_r, expt_a), (thry_r, thry_a)], ['Experiment', 'Theory'])
handles_list = [(expt_r, expt_a), (thry_r, thry_a)]
handles1 = (expt_r, expt_a) # tuple of 2 handles (aka legend keys) representing the markers
handles2 = (thry_r, thry_a) # same, these represent the lines
labels_list = ['Experiment', 'Theory']
label1 = 'Experiment'
label2 = 'Theory'
无论包含多少个句柄handles1
or in handles2
,它们都会被相应的绘制在彼此之上label1
and label2
,因为它们包含在单个元组中。要解决此问题并单独绘制键/符号,您必须将它们从元组中取出,如下所示:
handles_list = [expt_r, expt_a, thry_r, thry_a]
但现在你面临的问题是,只有expt_r, expt_a
由于标签列表仅包含两个标签,因此将绘制句柄。然而这里的目标是避免不必要地重复这些标签。以下是如何解决此问题的示例。它是基于您提供的代码示例构建的,并利用图例参数 https://matplotlib.org/api/_as_gen/matplotlib.pyplot.legend.html:
import numpy as np # v 1.19.2
import matplotlib.pyplot as plt # v 3.3.2
# Set data parameters
rng = np.random.default_rng(seed=1)
data_points = 10
error_scale = 0.2
# Create variables
rho = np.arange(0, data_points)
g_r = rho**2
g_r_error = rng.uniform(-error_scale, error_scale, size=g_r.size)*g_r
g_a = rho**2 + 50
g_a_error = rng.uniform(-error_scale, error_scale, size=g_a.size)*g_a
X_r = rho
Y_r = g_r + g_r_error
X_a = rho
Y_a = g_a + g_a_error
# Define two colors, one for 'r' data, one for 'a' data
rcolor = [69./255 , 115./255, 50.8/255 ]
acolor = [202./255, 115./255, 50.8/255 ]
# Create figure with single axes
fig, ax = plt.subplots(figsize=(9,5))
# Plot theory: notice the comma after each variable to unpack the list
# containing one Line2D object returned by the plotting function
# (because it is possible to plot several lines in one function call)
thry_r, = ax.plot(rho, g_r, '-', color=rcolor, lw=2)
thry_a, = ax.plot(rho, g_a, '-', color=acolor, lw=2)
# Plot experiment: no need for a comma as the PathCollection object
# returned by the plotting function is not contained in a list
expt_r = ax.scatter(X_r, Y_r, s=100, marker='s', facecolors='none', edgecolors=rcolor)
expt_a = ax.scatter(X_a, Y_a, s=100, marker='^', facecolors='none', edgecolors=acolor)
# Create custom legend: input handles and labels in desired order and
# set ncol=2 to line up the legend keys of the same type.
# Note that seeing as the labels are added here with explicitly defined
# handles, it is not necessary to define the labels in the plotting functions.
ax.legend(handles=[thry_r, expt_r, thry_a, expt_a],
labels=['', '', 'Theory','Experiment'],
loc='upper left', ncol=2, handlelength=3, edgecolor='black',
borderpad=0.7, handletextpad=1.5, columnspacing=0)
plt.show()
问题已解决,但可以简化代码以自动创建图例。通过使用以下命令,可以避免将每个绘图函数的输出存储为新变量:get_legend_handles_labels https://matplotlib.org/api/_as_gen/matplotlib.axes.Axes.get_legend_handles_labels.html功能。这是基于相同数据构建的示例。请注意,添加了第三种类型的图(误差带),以使句柄和标签的处理更加清晰:
# Define parameters used to process handles and labels
nb_plot_types = 3 # theory, experiment, error band
nb_experiments = 2 # r and a
# Create figure with single axes
fig, ax = plt.subplots(figsize=(9,5))
# Note that contrary to the previous example, here it is necessary to
# define a label in the plotting functions seeing as the returned
# handles/artists are this time not stored as variables. No labels means
# no handles in the handles list returned by the
# ax.get_legend_handles_labels() function.
# Plot theory
ax.plot(rho, g_r, '-', color=rcolor, lw=2, label='Theory')
ax.plot(rho, g_a, '-', color=acolor, lw=2, label='Theory')
# Plot experiment
ax.scatter(X_r, Y_r, s=100, marker='s', facecolors='none',
edgecolors=rcolor, label='Experiment')
ax.scatter(X_a, Y_a, s=100, marker='^', facecolors='none',
edgecolors=acolor, label='Experiment')
# Plot error band
g_r_lower = g_r - error_scale*g_r
g_r_upper = g_r + error_scale*g_r
ax.fill_between(X_r, g_r_lower, g_r_upper,
color=rcolor, alpha=0.2, label='Uncertainty')
g_a_lower = g_a - error_scale*g_a
g_a_upper = g_a + error_scale*g_a
ax.fill_between(X_a, g_a_lower, g_a_upper,
color=acolor, alpha=0.2, label='Uncertainty')
# Extract handles and labels and reorder/process them for the custom legend,
# based on the number of types of plots and the number of experiments.
# The handles list returned by ax.get_legend_handles_labels() appears to be
# always ordered the same way with lines placed first, followed by collection
# objects in alphabetical order, regardless of the order of the plotting
# functions calls. So if you want to display the legend keys in a different
# order (e.g. put lines on the bottom line) you will have to process the
# handles list in another way.
handles, labels = ax.get_legend_handles_labels()
handles_ordered_arr = np.array(handles).reshape(nb_plot_types, nb_experiments).T
handles_ordered = handles_ordered_arr.flatten()
# Note the use of asterisks to unpack the lists of strings
labels_trimmed = *nb_plot_types*[''], *labels[::nb_experiments]
# Create custom legend with the same format as in the previous example
ax.legend(handles_ordered, labels_trimmed,
loc='upper left', ncol=nb_experiments, handlelength=3, edgecolor='black',
borderpad=0.7, handletextpad=1.5, columnspacing=0)
plt.show()
附加文档:传奇级 https://matplotlib.org/api/legend_api.html, 艺术家班 https://matplotlib.org/api/artist_api.html