在 matplotlib 图下显示图例数量不同的图例 [英] Showing legend under matplotlib plot with varying number of plots

查看:101
本文介绍了在 matplotlib 图下显示图例数量不同的图例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个程序,该程序允许用户将不同数量的图放在一个轴上.我需要显示图例,并且情节标签很长,所以我认为最好在情节下显示这一点.当我以前这样做时,我总是把情节缩小一点,把图例放在情节下面.现在有不同数量的图,这并不那么简单,因为我找不到一个很好的公式来确定将图缩小多少以及将图例向下放置多远,因此它不会被切断或与轴重叠.

我编写了一个示例代码来演示我目前正在做的事情,这很丑陋.我目前正在检查图中有多少项,并尝试手动优化轴收缩和图例偏移参数,然后进行了很大的 if 循环以使用手动优化的值.它们没有针对此示例代码进行优化,但我认为它展示了我在做什么.

 将matplotlib.pyplot导入为plt将numpy导入为npdef find_scales(legendData):leg_len = len(legendData)如果leg_len == 0:高度_比例 = 1Legend_offset = 0elif leg_len == 1:高度_比例 = 0.96legend_offset = -0.18elif leg_len == 2:height_scale = 0.95Legend_offset = -0.25elif leg_len == 3:height_scale = 0.94legend_offset = -0.35elif leg_len == 4:高度_比例 = 0.93Legend_offset = -0.45elif leg_len == 5:高度比例= 0.93legend_offset = -0.57elif leg_len == 6:高度比例= 0.93Legend_offset = -0.68elif leg_len == 7:高度_比例 = 0.93legend_offset = -0.82elif leg_len == 8:高度_比例 = 0.93legend_offset = -0.98elif leg_len == 9:height_scale = 0.92Legend_offset = -1.3elif leg_len == 10:height_scale = 0.92legend_offset = -1.53别的:高度_比例 = 0.92Legend_offset = -1.8返回 height_scale,legend_offsetnum_plots = 3x_range = np.arange(10)fig,ax = plt.subplots()对于我在范围内(num_plots):ax.plot(x_range,np.random.rand(10))legend_labels = ['a','b','c','d','e','f','g','h','i','j'] [:num_plots]盒子= ax.get_position()height_scale,legend_offset = find_scales(legend_labels)ax.set_position([box.x0,box.y0 + box.height *(1-height_scale),#left,bottom,width,heightbox.width, box.height * height_scale])ax.legend(legend_labels,loc = 3,bbox_to_anchor =(0,legend_offset),borderaxespad = 0.)plt.show()

我希望有更好的方法来做到这一点.我希望图例位于轴下方.我不能使图例与轴或x标签重叠.我不能让传说因为太低和超出形象而被切断.有没有办法做到这一点,以便轴和图例会自动调整大小以适应图形?

解决方案

此问题的答案中显示了一种校正轴位置的方法,以使图例具有足够的空间:

I'm working on a program that allows users to put a different number of plots onto an axis. I need to show the legend, and the plot labels are long so I think it is best to show this under the plot. When I've done this before, I've always just shrunk the plot a bit and put the legend under the plot. Now that there are a varying number of plots this isn't as simple since I cannot find a nice formula for determining how much to shrink the plot and how far down to put the legend so it is not being cut off or overlapping the axis.

I've written a an example code to demonstrate what I currently am doing, which is ugly. I currently am checking how many items are in the plot and tried to manually optimize the axis shrink and legend offset parameters then did a big if loop to use the manually optimized values. They are not optimized for this example code, but I think it demonstrates what I am doing.

import matplotlib.pyplot as plt
import numpy as np

def find_scales(legendData):
        leg_len = len(legendData)
        if leg_len == 0:
            height_scale = 1
            legend_offset = 0
        elif leg_len == 1:
            height_scale = 0.96
            legend_offset = -0.18
        elif leg_len == 2:
            height_scale = 0.95
            legend_offset = -0.25
        elif leg_len == 3:
            height_scale = 0.94
            legend_offset = -0.35
        elif leg_len == 4:
            height_scale = 0.93
            legend_offset = -0.45
        elif leg_len == 5:
            height_scale = 0.93
            legend_offset = -0.57
        elif leg_len == 6:
            height_scale = 0.93
            legend_offset = -0.68
        elif leg_len == 7:
            height_scale = 0.93
            legend_offset = -0.82
        elif leg_len == 8:
            height_scale = 0.93
            legend_offset = -0.98
        elif leg_len == 9:
            height_scale = 0.92
            legend_offset = -1.3
        elif leg_len == 10:
            height_scale = 0.92
            legend_offset = -1.53
        else:
            height_scale = 0.92
            legend_offset = -1.8
        return height_scale, legend_offset

num_plots = 3
x_range = np.arange(10)

fig,ax = plt.subplots()

for i in range(num_plots):
    ax.plot(x_range, np.random.rand(10))

legend_labels = ['a','b','c','d','e','f','g','h','i','j'][:num_plots]

box = ax.get_position()

height_scale, legend_offset = find_scales(legend_labels)

ax.set_position([box.x0, box.y0 + box.height * (1-height_scale), #left, bottom, width, height
             box.width, box.height * height_scale])

ax.legend(legend_labels, loc=3, bbox_to_anchor=(0,legend_offset), borderaxespad=0.)

plt.show()

I'm hoping there's a better way to do this. I want the legend to be under the axis. I cannot have the legend be overlapping the axis or x-label. I cannot have the legend being cut off by being too low and out of the figure. Is there a way to do this so the axis and legend will automatically size themselves to fit in the figure?

解决方案

A way to correct the axes position, such that the legend has enough space is shown in this question's answer: Creating figure with exact size and no padding (and legend outside the axes)

For the legend to sit on the bottom the solution is much simpler. Essentially you only need to subtract the legend height from the axes height and move the axes by the amount of the legend height towards the top.

import matplotlib.pyplot as plt 

fig = plt.figure(figsize = [3.5,2]) 
ax = fig.add_subplot(111)
ax.set_title('title')
ax.set_ylabel('y label')
ax.set_xlabel('x label')
ax.plot([1,2,3], marker="o", label="quantity 1")
ax.plot([2,1.7,1.2], marker="s", label="quantity 2")

def legend(ax, x0=0.5,y0=0, pad=0.5,**kwargs):
    otrans = ax.figure.transFigure
    t = ax.legend(bbox_to_anchor=(x0,y0), loc=8, bbox_transform=otrans,**kwargs)
    ax.figure.tight_layout(pad=pad)
    ax.figure.canvas.draw()
    tbox = t.get_window_extent().transformed( ax.figure.transFigure.inverted() )
    bbox = ax.get_position()
    ax.set_position([bbox.x0, bbox.y0+tbox.height,bbox.width, bbox.height-tbox.height]) 

legend(ax,y0=0, borderaxespad=0.2)

plt.savefig(__file__+'.pdf')
plt.show()

这篇关于在 matplotlib 图下显示图例数量不同的图例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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