matplotlib中的矩阵图例(Python)

DIN*_*970 5 python matplotlib legend

我尝试在Matplotlib中绘制具有以下特征的数据:

数据可以细分为10个不同的组.我想用独特的标记形状绘制每个组.

每组可以再次细分为两种类型.我想区分我的情节中的两种类型与填充的经文相同形状的空标记.每个组细分为相同的两种类型.

为了使我的传奇更优雅,而不仅仅是所有不同标记类型的列表,我希望它看起来像:

|------------|Type 1-------| Type 2------|
|Group 1     | fil. mark 1 | empty mark 1|
|Group 2     | fil. mark 2 | empty mark 2|
Run Code Online (Sandbox Code Playgroud)

...

Matplotlib可以实现吗?

小智 4

我无法找到一个优雅的解决方案来解决你的问题,但知道如何通过一些技巧来做到这一点。

方法一

显而易见的选择是制作一个包含 3 列的图例。如果我们假设图例是一个表格,那么我们需要一个标题 - 第一行,我们在其中添加列名称。图例是逐列填充补丁,而不是逐行填充。为了处理这种行为,我们将为每个图例的列创建三个单独的列表并向它们添加补丁。然后连接所有三个列表并将最终的补丁列表添加到图例中。

结果你应该得到一个像这样的图例: 使用方法1生成的图像

生成该图像的代码如下

from random import random
import matplotlib.patches as m_patches
import matplotlib.pyplot as plt


groups = [{'Type 1': [i+random() for _ in range(25)], 'Type 2': [i+random() for _ in range(25)]} for i in range(10)]
markers = ['o', 'v', '^', '<', '>', '8', 's', 'p', '*', 'h']
colors = ['r', 'g', 'b', 'salmon', 'lime', 'purple', 'cyan', 'gold', 'peru', 'gray']


def main():
    # Make legend with 3 columns:
    # First column for group number, seconds on for Type 1 lines, and third for Type 2 lines
    n_cols = 3

    # Figure size
    fig_width = 13
    fig_height = 7

    # Axis location
    # Note that width must be lower than 1 so we could add legend to the right from a plot
    ax_left, ax_bottom = 0.1, 0.1
    ax_width, ax_height = 0.6, 0.8

    # Make a figure
    plt.figure(figsize=(fig_width, fig_height))

    # Create axes
    ax = plt.axes([ax_left, ax_bottom, ax_width, ax_height])

    # Since we are going to create a custom legend, create a list of patches for each column
    patches_column1 = [m_patches.Patch(color="w", label=f'')]
    patches_column2 = [m_patches.Patch(color="none", label=f'Type 1')]
    patches_column3 = [m_patches.Patch(color="none", label=f'Type 2')]

    for i, (group, color, marker) in enumerate(zip(groups, colors, markers)):

        l1, = ax.plot(group['Type 1'], f"{marker}-", color=color, label=f"Fill")
        l2, = ax.plot(group['Type 2'], f"{marker}-", color=color, label=f"Empty", markerfacecolor='none')

        # First column contains a 'group' info patch
        patches_column1.append(m_patches.Patch(color='none', label=f'Group {i}'))

        # Second column contains patches with lines of Type 1
        patches_column2.append(l1)

        # Third column contains patches with lines of Type 2
        patches_column3.append(l2)

    # Now we need to merge all three lists together and add the final list to a legend
    patches = list()
    patches.extend(patches_column1)
    patches.extend(patches_column2)
    patches.extend(patches_column3)

    ax.legend(loc='upper left',
              bbox_to_anchor=(1.0, 0.0, 1, 1),
              ncol=n_cols, handles=patches)

    plt.savefig("fig1", dpi=200, facecolor='w', edgecolor='w',
                orientation='portrait', papertype=None, format=None,
                transparent=False, bbox_inches=None, pad_inches=0.1,
                frameon=None, metadata=None)

    plt.show()


if __name__ == '__main__':
    main()
Run Code Online (Sandbox Code Playgroud)

但是,您将在图例上看到Fill和标签。Empty我找不到一种简单的方法来去除它们,但找到了一种丑陋的方法。

方法二

这与方法 1相同,只是现在我们要对图例上的标记和文本进行一些操作。

首先,我们需要向legend()构造函数添加一个附加参数: handletextpad=-2.5。此参数将移动标记并将其放置在大约列的中间。现在我们需要隐藏与每个行标记(“填充”和“空”)相对应的文本。为此,我们将从图例中获取所有文本对象,并根据我们想要隐藏的内容将其颜色更新为'black'或。'none'结果你应该得到这样的结果:

方法 2 的图像

完整代码如下

from random import random
import matplotlib.patches as m_patches
import matplotlib.pyplot as plt


groups = [{'Type 1': [i+random() for _ in range(25)], 'Type 2': [i+random() for _ in range(25)]} for i in range(10)]
markers = ['o', 'v', '^', '<', '>', '8', 's', 'p', '*', 'h']
colors = ['r', 'g', 'b', 'salmon', 'lime', 'purple', 'cyan', 'gold', 'peru', 'gray']


def main():
# Make legend with 3 columns:
    # First column for group number, seconds on for Type 1 lines, and third for Type 2 lines
    n_cols = 3

    # Figure size
    fig_width = 10
    fig_height = 7

    # Axis location
    # Note that width must be lower than 1 so we could add legend to the right from a plot
    ax_left, ax_bottom = 0.1, 0.1
    ax_width, ax_height = 0.65, 0.8

    # Make a figure
    plt.figure(figsize=(fig_width, fig_height))

    # Create axes
    ax = plt.axes([ax_left, ax_bottom, ax_width, ax_height])

    # Since we are going to create a custom legend, create a list of patches for each column
    patches_column1 = [m_patches.Patch(color="w", label=f'')]
    patches_column2 = [m_patches.Patch(color="none", label=f'Type 1')]
    patches_column3 = [m_patches.Patch(color="none", label=f'Type 2')]

    for i, (group, color, marker) in enumerate(zip(groups, colors, markers)):

        l1, = ax.plot(group['Type 1'], f"{marker}-", color=color, label="Filled")
        l2, = ax.plot(group['Type 2'], f"{marker}-", color=color, markerfacecolor='none', label="Empty")

        # First column contains a 'group' info patch
        patches_column1.append(m_patches.Patch(color='none', label=f'Group {i}'))

        # Second column contains patches with lines of Type 1
        patches_column2.append(l1)

        # Third column contains patches with lines of Type 2
        patches_column3.append(l2)

    # Now we need to merge all three lists together and add the final list to a legend
    patches = list()
    patches.extend(patches_column1)
    patches.extend(patches_column2)
    patches.extend(patches_column3)

    # If you plan to
    lg = ax.legend(loc='upper left',
                   bbox_to_anchor=(1.0, 0.0, 1, 1),
                   ncol=n_cols,
                   handles=patches,
                   handletextpad=-2.5, 
                   borderpad=1.0)

    for i, text in enumerate(lg.get_texts()):
        if i < 11:
            pass
        elif i == 11:
            text.set_color('black')
        elif 12 <= i < 22:
            text.set_color('none')
        elif i == 22:
            text.set_color('black')
        else:
            text.set_color('none')

    plt.savefig("fig2", dpi=200, facecolor='w', edgecolor='w',
                orientation='portrait', papertype=None, format=None,
                transparent=False, bbox_inches=None, pad_inches=0.1,
                frameon=None, metadata=None)

    plt.show()


if __name__ == '__main__':
    main()
Run Code Online (Sandbox Code Playgroud)