如何移动图形图例的标记和颜色部分

use*_*775 7 python matplotlib seaborn seaborn-0.12.x

我正在创建一个图形(带有seaborn.objects界面),需要带有颜色和标记的图例。我正在尝试移动生成的图例,但在移动标记部分时遇到问题。请参阅下面的示例:

该示例旨在在笔记本环境中运行。添加plt.show()以在作为 Python 文件运行时显示图形。

import matplotlib.pyplot as plt
import seaborn as sns
import seaborn.objects as so

mpg = sns.load_dataset("mpg")
f, axs = plt.subplots(1, 2, figsize=(8, 4))
_ = (
    so.Plot(mpg, "weight", "acceleration", color="cylinders")
    .add(so.Dot(), marker="origin")
    .scale(
        color=["#49b", "#a6a", "#5b8", "#128", "#332"],
        marker={"japan": ".", "europe": "+", "usa": "*"},
    )
    .on(axs[0])
    .plot()
)
Run Code Online (Sandbox Code Playgroud)

图例包括颜色和标记部分,其中一半在图的右侧被切除。

我试图将图例从情节的最右侧移动到子情节的右侧。如何使用 Seaborn 0.12 对象自定义图例在这种情况下只是部分解决方案。仅部分图例移动成功。我想知道为什么没有看到传说的第二部分......legend_handles

l1 = f.legends.pop(0)
axs[0].legend(l1.legend_handles, [t.get_text() for t in l1.texts])
Run Code Online (Sandbox Code Playgroud)

只有图例的颜色部分可见,标记部分不可见。

传奇的所有部分如何移动?

Jos*_*hew 0

免责声明:这是一个黑客解决方案,可能不适用于所有情况。我开发它时只考虑到了这个问题,所以我不能保证它会有效地推广。如果这不涵盖您的特定用例,请发表评论。

该解决方案有两个关键要素:

  1. 使用以下命令访问两个图形部分plotter._legend_contents
  2. 采用seaborn图例创建过程中使用的实用方法,adjust_legend_subtitles以重新创建带有样式字幕的图例seaborn

我已调整问题中的示例以在笔记本环境中运行,并且我已包含该问题的解决方案。见下文:

import matplotlib as mpl
import matplotlib.pyplot as plt
import seaborn as sns
import seaborn.objects as so

sns.set_theme(style="white")
mpg = sns.load_dataset("mpg")
fig, axs = plt.subplots(1, 2, figsize=(8, 4))
ax = axs[0]
plotter = (
    so.Plot(mpg, "weight", "acceleration", color="cylinders")
    .add(so.Dot(), marker="origin")
    .scale(
        color=["#49b", "#a6a", "#5b8", "#128", "#332"],
        marker={"japan": ".", "europe": "+", "usa": "*"},
    )
    .on(ax)
    .plot()
)

# --- Solution begins here --- #

legend_contents = plotter._legend_contents
blank_handle = mpl.patches.Patch(alpha=0, linewidth=0, visible=False)
handles = []
labels = []
for legend_content in legend_contents:
    handles.append(blank_handle)
    handles.extend(legend_content[1])
    labels.append(legend_content[0][0])
    labels.extend(legend_content[2])
axs[0].legend(handles, labels)
def adjust_legend_subtitles(legend):
    font_size = plt.rcParams.get("legend.title_fontsize", None)
    hpackers = legend.findobj(mpl.offsetbox.VPacker)[0].get_children()
    for hpack in hpackers:
        draw_area, text_area = hpack.get_children()
        handles = draw_area.get_children()
        handle = handles[0]
        if not handles[0]._visible:
            draw_area.set_width(0)
            for text in text_area.get_children():
                if font_size is not None:
                    text.set_size(font_size)
adjust_legend_subtitles(ax.get_legend())
fig.legends.pop(0)

# --- Optional! --- #
# After this move, you can use sns.move_legend to move the legend where
# you want it. However, you will have to call adjust_legend_subtitles
# again to get the "subtitle" formatting back.

sns.move_legend(ax, "best")
adjust_legend_subtitles(axs[0].get_legend())
Run Code Online (Sandbox Code Playgroud)

结果包括图例的两个方面,甚至在移动后将字幕添加回图例。

该解决方案在以下版本上进行了测试:

  • 海博恩==0.12.2
  • matplotlib==3.7.2