繁体   English   中英

防止 Matplotlib Legend 在垂直子图上涂抹 h-padding?

[英]Prevent Matplotlib Legend from smushing h-padding on vertical subplots?

我有一个脚本,我在一个 5x1 的网格中绘制了几个变量。 我注意到,当我有数据使我的图例更短时,子图本身具有可接受的高度和水平填充。 当我有数据使我的图例更大(垂直)时,子图被压扁,在图之间留下额外的水平填充。

有没有办法防止这种情况? 要将图例分配与轴对象分开并独立于图例间距绘制每个图?

下面是一个最小的可重现示例来说明我的意思:

#!/usr/bin/env python3
from pathlib import Path
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt


def plotter(df, var_cols):
    dfa = df.query("accepted == 'accepted'")
    dfr = df.query("accepted != 'accepted'")
    colors = {v: c for v, c in zip(['accepted', 'rejected', 'rerun'],
                                   ['darkgreen', 'firebrick', 'steelblue'])}
    fig, axes = plt.subplots(nrows=len(var_cols), sharex=True)

    for var, ax in zip(var_cols, axes):
        for k, d in dfr.groupby('accepted'):
            ax.scatter(d.iteration, d[var], label=k, alpha=0.8, c=d.accepted.map(colors))
        ax.plot(dfa.iteration, dfa[var], '-o', label='accepted', color=colors['accepted'])

    # Grab 3rd axes because I want the legend to be towards the center
    handles, labels = axes[2].get_legend_handles_labels()
    # Sort legend labels to put 'accepted' on top
    labels, handles = zip(*sorted(zip(labels, handles), key=lambda t: t[0]))
    axes[2].legend(handles, labels, markerscale=1.2, bbox_to_anchor=(1, 0.5))
    fig.tight_layout()


def main():
    states = {1: 'accepted', 2: 'rejected', 3: 'rerun'}
    np.random.seed(666)
    dat1 = pd.DataFrame({
        'iteration': [0, 1, 2],
        'accepted': ['accepted']*3,
        'h_cap': [10.1, 6.5, 12.2],
        'h_stor': [500, 410, 0],
        'h_mark': [10, 6, 1],
        'bid': [500, 100, 50],
        'npv': [2.278, 2.6, 2.85]
    })

    dat2 = pd.DataFrame({
        'iteration': range(10),
        'accepted': [states[num] for num in np.random.randint(1, 4, size=10)],
        'h_cap': np.random.rand(10),
        'h_stor': np.random.rand(10),
        'h_mark': np.random.rand(10),
        'bid': np.random.rand(10),
        'npv': np.random.rand(10)
    })

    var_cols = ['h_cap', 'h_stor', 'h_mark', 'bid', 'npv']
    plotter(dat1, var_cols)
    plt.savefig(Path('~/Desktop/nonsmushed.png').expanduser())

    plotter(dat2, var_cols)
    plt.savefig(Path('~/Desktop/smushed.png').expanduser())


if __name__ == '__main__':
    main()

未涂抹.png

在此处输入图片说明

被弄脏的.png

在此处输入图片说明

因为您的图例“属于” axes[2]tight_layout()调整间距,以便相邻轴不覆盖图例。

我认为最简单的解决方案是创建一个“图形级”图例( fig.legend() ),但问题在于tight_layout()不考虑该图例,您必须调整正确的手动保证金(如果需要,可能有一种方法可以自动计算,但这可能会变得混乱)

(...)
labels, handles = zip(*sorted(zip(labels, handles), key=lambda t: t[0]))
fig.legend(handles, labels, markerscale=1.2, bbox_to_anchor=(1, 0.5))
fig.tight_layout()
fig.subplots_adjust(right=0.75)  # adjust value as needed

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM