如何在图例中的同一标签下添加条形图和线形? [英] How to add bar and line under the same label in a legend?

查看:67
本文介绍了如何在图例中的同一标签下添加条形图和线形?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我绘制了一个带有2个Y轴的图,其中一个带有条形图,另一条带有线,我想为两个图添加一个共同的图例.

如果我添加以下内容:

  axis1.legend([(bar1,line1),(bar2,line2)],['解决方案1','解决方案2'],loc ='左上',numpoints = 1) 

我几乎得到了我想要的东西,除了标记/艺术家(不确定哪个术语在这里合适)之外,这两种解决方案在这里都重叠了:

有可能首先让酒吧艺术家和线条艺术家并排,然后是标签吗?

根据官方

最后一点,知道下图中有两个轴可能很有用,就像下面的示例一样,通过重叠两个轴的图例来绘制单个图例在技术上是可行的,尽管显然需要大量手动调整才能根据需要设置句柄和标签的格式.

 #通过调整两个轴的图例重叠来创建图例#bbox的位置和大小,并删除第一个图例的标签leg1 = axis1.legend(loc='左上', bbox_to_anchor=(0., 0.5, 0.25, 0.5),edgecolor =黑色",模式=展开")对于leg1.texts中的文本:text.set_visible(False)axis2.legend(bbox_to_anchor =(0.05,0.5,0.2,0.5),frameon = False) 

可以通过 legend类中的文档中列出的图例方法访问其他图例功能.>.

I made a plot with 2 Y axes, where one has a bar chart and the other one lines and I would like to add a common legend for both plots.

If I add the following:

axis1.legend([(bar1, line1), (bar2, line2)], ['Solution 1', 'Solution 2'], 
             loc='upper left', numpoints=1)

I almost get what I want, except for the markers / artists (not sure which term is suitable here), which are overlapping for both solutions as seen here:

Is it possible to have the bar artist first and the line artist second, side by side, and then the label?

I suppose this has to do with the legend handler, according to the official Legend guide the tuple handler "simply plots the handles on top of one another for each item in the given tuple". Could anyone help me in writing a new custom handler which plots the handles side by side?

Edit: This question seems quite similar to this.

解决方案

As you have quoted from the paragraph on legend handlers in the legend guide, the handles of the items in each tuple contained in your list of tuples are being plotted on top of one another. So it would seem that the solution should be to take out the handles from the tuples: changing this [(bar1, line1), (bar2, line2)] to this [bar1, bar2, line1, line2]. But then you would face the issue that you need to have a corresponding number of labels, because with ['Solution 1', 'Solution 2'] the legend will only show the bar1, bar2 keys.

To avoid this issue, you could use the legend_handler_HandlerTuple class presented in the last example shown in the aforementioned paragraph on legend handlers. Your code for the legend would then look something like this (note that I rename bar1, bar2 to bars1, bars2 in all the following code snippets; the legend parameters are set so that the format is like in the image shown further below):

import matplotlib.pyplot as plt
from matplotlib.legend_handler import HandlerTuple
...

# Import data, draw plots
...
axis1.legend([(bar1, line1), (bar2, line2)], ['Solution 1', 'Solution 2'],
             loc='upper left', edgecolor='black', borderpad=0.7, handlelength=7,
             handletextpad=1.5, handler_map={tuple: HandlerTuple(ndivide=None, pad=1.5)})

This does the job but there is a more elegant alternative to this. Let me illustrate with a complete example based on the image you have provided and on this example for the code for the bars. From the code snippet you have shared, I imagine that your code is structured somewhat like this:

import numpy as np                 # v 1.19.2
import matplotlib.pyplot as plt    # v 3.3.2

# Compute variables to plot
var_bars1 = np.arange(1, 8)
var_bars2 = var_bars1/2
var_line1 = 2+var_bars1**1.3
var_line2 = 5+var_bars2**1.6
x = np.arange(len(var_bars1))

# Create grouped bar chart
bar_width = 0.3
fig, axis1 = plt.subplots(figsize=(9, 6))
# notice that the bar plot functions return a BarContainer object which contains
# the colored rectangles (stored as matplotlib.patches.Rectangle objects)
bars1 = axis1.bar(x-bar_width/2, var_bars1, bar_width, label='Solution 1')
bars2 = axis1.bar(x+bar_width/2, var_bars2, bar_width, label='Solution 2')
axis1.set_xlabel('x both', labelpad=15, fontsize=12)
axis1.set_ylabel('y bars', labelpad=15, fontsize=12)

# Create line chart over bar chart using same x axis
axis2 = axis1.twinx()
# notice that the plot functions return a list containing the single line (with
# markers), the comma after the variable name unpacks the list
line1, = axis2.plot(x, var_line1, 'kx', linestyle='-', ms=8, label='Solution 1')
line2, = axis2.plot(x, var_line2, 'ko', linestyle='-', ms=8, label='Solution 2')
axis2.set_ylabel('y lines', labelpad=15, fontsize=12)

Here is how to create the legend without using the HandlerTuple class:

# Create custom legend using handles extracted from plots and manually
# inserting the corresponding labels, leaving strings empty for the first
# column of legend handles (aka legend keys) and adding columnspacing=0 to
# remove the extra whitespace created by the empty strings
axis1.legend((bars1, bars2, line1, line2), ('', '', 'Solution 1', 'Solution 2'),
             loc='upper left', ncol=2, handlelength=3, edgecolor='black',
             borderpad=0.7, handletextpad=1.5, columnspacing=0)

The parameters for the legend are detailed in the documentation here. Before showing what the output looks like, it is worth noting that this code can be simplified in a way that automates the legend creation. It can be rewritten in a way to avoid having to store the output of each plotting function as a new variable by making use of the get_legend_handles_labels function, like this:

fig, axis1 = plt.subplots(figsize=(9, 6))

# Create grouped bar chart
axis1.bar(x-bar_width/2, var_bars1, bar_width, label='Solution 1')
axis1.bar(x+bar_width/2, var_bars2, bar_width, label='Solution 2')
axis1.set_xlabel('x both', labelpad=15, fontsize=12)
axis1.set_ylabel('y bars', labelpad=15, fontsize=12)

# Create line chart over bar chart using same x axis
axis2 = axis1.twinx()
axis2.plot(x, var_line1, 'kx', linestyle='-', ms=8, label='Solution 1')
axis2.plot(x, var_line2, 'ko', linestyle='-', ms=8, label='Solution 2')
axis2.set_ylabel('y lines', labelpad=15, fontsize=12)

handles1, labels1 = axis1.get_legend_handles_labels()
handles2, labels2 = axis2.get_legend_handles_labels()

# Create custom legend by unpacking tuples containing handles and using 
# only one set of unpacked labels along with set of unpacked empty strings
# (using None instead of empty strings gives the same result)
axis1.legend((*handles1, *handles2), (*len(labels1)*[''], *labels2),
             loc='upper left', ncol=2, handlelength=3, edgecolor='black',
             borderpad=0.7, handletextpad=1.5, columnspacing=0)

plt.show()

On a final note, it can be useful to know that seeing as there are two axes in this figure, it is technically possible to draw a single legend by overlapping the legends of both axes like in the following example, though this obviously requires a lot of manual tweaking to format the handles and labels as wanted.

# Create legend by overlapping the legends from both axes by adjusting the 
# bbox location and size, and by removing labels of the first legend
leg1 = axis1.legend(loc='upper left', bbox_to_anchor=(0., 0.5, 0.25, 0.5),
                    edgecolor='black', mode="expand")
for text in leg1.texts:
    text.set_visible(False)
axis2.legend(bbox_to_anchor=(0.05, 0.5, 0.2, 0.5), frameon=False)

Further legend functionalities can be accessed through the legend methods listed in the documentation on the legend class.

这篇关于如何在图例中的同一标签下添加条形图和线形?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆