matplotlib 图例中的两个相邻符号
Two adjacent symbols in matplotlib legend
我想在图例的同一行上识别两个不同的符号(具有不同的颜色)。下面,我尝试使用代理艺术家来执行此操作,但结果是它们在图例中堆叠在一起。我希望它们彼此相邻或一个在另一个之上 - 所以它们都可见。
from pylab import *
import matplotlib.lines as mlines
#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 ]
#Plot theory:
ax2.plot(rho, g_r, '-',color=rcolor,lw=2)
ax2.plot(rho, g_a, '-',color=acolor,lw=2)
#Plot experiment:
ax2.scatter(X_r, Y_r,s=200, marker='s', facecolors='none', edgecolors=rcolor);
ax2.scatter(X_a, Y_a,s=200, marker='^', facecolors='none', edgecolors=acolor);
#Create Proxy Artists for legend
expt_r = mlines.Line2D([], [], fillstyle='none', color=rcolor, marker='s', linestyle='', markersize=15)
expt_a = mlines.Line2D([], [], fillstyle='none', color=acolor, marker='^', linestyle='', markersize=15)
thry_r = mlines.Line2D([], [], fillstyle='none', color=rcolor, marker='', markersize=15)
thry_a = mlines.Line2D([], [], fillstyle='none', color=acolor, marker='', markersize=15)
#Add legend
ax2.legend(((expt_r,expt_a),(thry_r,thry_a)), ('Experiment','Theory'))
我认为我的问题几乎与这个问题完全一样:(Matplotlib, legend with multiple different markers with one label),但似乎问题没有解决,因为那里的答案只是在另一个之上绘制了一个补丁,这正是也发生在我身上。我觉得也许我需要以某种方式制作一个复合补丁,但我很难找到如何做到这一点。谢谢!
此外,我还没有找到如何使图例符号看起来(线条粗细、大小)与散点符号相同。再次感谢。
我不回答你的主要问题,抱歉。
但是,关于你的最后一点
how to make the legend symbols look the same (line thickness, size) as the scatter symbols
可以使用legend
命令的关键字markerscale
。所以对于相同的大小
ax2.legend( ...<handles_and_labels>... , markerscale=1)
或在 rcParams
中更改 legend.markerscale
应该可以。
重叠补丁(又名艺术家)的问题在于您在创建图例时如何定义手柄和标签。引用 matplotlib legend guide:
the default handler_map has a special tuple handler (legend_handler.HandlerTuple) which simply plots the handles on top of one another for each item in the given tuple
让我们首先检查一下您作为示例给出的图例的结构。任何可迭代对象都可以用于句柄和标签,所以我选择将它们存储为列表,以符合图例指南中给出的一些示例并使代码更清晰:
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
或handles2
中包含多少句柄,它们都会被相应的label1
和label2
绘制在彼此之上,看到它们包含在一个元组中。要解决此问题并单独绘制 keys/symbols,您必须像这样将它们从元组中取出:
handles_list = [expt_r, expt_a, thry_r, thry_a]
但是现在您面临的问题是只有 expt_r, expt_a
个图柄会被绘制,因为标签列表只包含两个标签。然而,这里的目标是避免不必要地重复这些标签。以下是如何解决此问题的示例。它建立在您提供的代码示例之上,并使用 legend parameters:
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
函数可以避免将每个绘图函数的输出存储为新变量。这是一个基于相同数据的示例。请注意,添加了第三种类型的图(错误带)以使句柄和标签的处理更加清晰:
# 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()
其他文档:legend class, artist class
我想在图例的同一行上识别两个不同的符号(具有不同的颜色)。下面,我尝试使用代理艺术家来执行此操作,但结果是它们在图例中堆叠在一起。我希望它们彼此相邻或一个在另一个之上 - 所以它们都可见。
from pylab import *
import matplotlib.lines as mlines
#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 ]
#Plot theory:
ax2.plot(rho, g_r, '-',color=rcolor,lw=2)
ax2.plot(rho, g_a, '-',color=acolor,lw=2)
#Plot experiment:
ax2.scatter(X_r, Y_r,s=200, marker='s', facecolors='none', edgecolors=rcolor);
ax2.scatter(X_a, Y_a,s=200, marker='^', facecolors='none', edgecolors=acolor);
#Create Proxy Artists for legend
expt_r = mlines.Line2D([], [], fillstyle='none', color=rcolor, marker='s', linestyle='', markersize=15)
expt_a = mlines.Line2D([], [], fillstyle='none', color=acolor, marker='^', linestyle='', markersize=15)
thry_r = mlines.Line2D([], [], fillstyle='none', color=rcolor, marker='', markersize=15)
thry_a = mlines.Line2D([], [], fillstyle='none', color=acolor, marker='', markersize=15)
#Add legend
ax2.legend(((expt_r,expt_a),(thry_r,thry_a)), ('Experiment','Theory'))
我认为我的问题几乎与这个问题完全一样:(Matplotlib, legend with multiple different markers with one label),但似乎问题没有解决,因为那里的答案只是在另一个之上绘制了一个补丁,这正是也发生在我身上。我觉得也许我需要以某种方式制作一个复合补丁,但我很难找到如何做到这一点。谢谢!
此外,我还没有找到如何使图例符号看起来(线条粗细、大小)与散点符号相同。再次感谢。
我不回答你的主要问题,抱歉。 但是,关于你的最后一点
how to make the legend symbols look the same (line thickness, size) as the scatter symbols
可以使用legend
命令的关键字markerscale
。所以对于相同的大小
ax2.legend( ...<handles_and_labels>... , markerscale=1)
或在 rcParams
中更改 legend.markerscale
应该可以。
重叠补丁(又名艺术家)的问题在于您在创建图例时如何定义手柄和标签。引用 matplotlib legend guide:
the default handler_map has a special tuple handler (legend_handler.HandlerTuple) which simply plots the handles on top of one another for each item in the given tuple
让我们首先检查一下您作为示例给出的图例的结构。任何可迭代对象都可以用于句柄和标签,所以我选择将它们存储为列表,以符合图例指南中给出的一些示例并使代码更清晰:
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
或handles2
中包含多少句柄,它们都会被相应的label1
和label2
绘制在彼此之上,看到它们包含在一个元组中。要解决此问题并单独绘制 keys/symbols,您必须像这样将它们从元组中取出:
handles_list = [expt_r, expt_a, thry_r, thry_a]
但是现在您面临的问题是只有 expt_r, expt_a
个图柄会被绘制,因为标签列表只包含两个标签。然而,这里的目标是避免不必要地重复这些标签。以下是如何解决此问题的示例。它建立在您提供的代码示例之上,并使用 legend parameters:
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
函数可以避免将每个绘图函数的输出存储为新变量。这是一个基于相同数据的示例。请注意,添加了第三种类型的图(错误带)以使句柄和标签的处理更加清晰:
# 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()
其他文档:legend class, artist class