python - matplotlib 图例中的两个相邻符号

标签 python matplotlib plot 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 ),但似乎问题未解决,因为那里的答案只是将一个补丁绘制在另一个补丁之上,这也正是我遇到的情况.我觉得也许我需要以某种方式制作一个复合补丁,但我很难找到如何做到这一点。谢谢!

此外,我还没有找到如何使图例符号看起来与散点符号相同(线条粗细、大小)。再次感谢。

最佳答案

重叠补丁(又名艺术家)的问题在于您在创建图例时如何定义 handle 和标签。引用 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'

无论 handles1handles2 中包含多少个句柄,它们都将被相应的 label1 绘制在另一个之上和 label2,因为它们包含在单个元组中。要解决此问题并单独绘制键/符号,您必须像这样将它们从元组中取出:

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

legend_unique_labels1

问题已解决,但可以简化代码以自动创建图例。通过使用 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_unique_labels2

附加文档:legend class , artist class

关于python - matplotlib 图例中的两个相邻符号,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29452535/

相关文章:

r - 如何在R中绘制多条线

python - 未找到 Virtualenv 命令

python - Python获取多个Button点击事件的方法

python - 使用 python 中的 for 循环仅在一个轴上创建多个绘图

matlab - 具有 2 个变量的函数曲线下的阴影面积

python - 如何在具有大量不同计数且分布不均匀的 Pandas 中绘制 value_counts

python - 如何使用 CBV 在所有 Django 模板中创建侧边栏?

Python:在 tarfile 中使用过滤器

python - 如何将整数转换为位列表?

python - 使用适用于 Linux 的 Windows 子系统时设置 Matplotlib 后端