Seaborn jointplot link x 轴到 Matplotlib 子图

Seaborn jointplot link x-axis to Matplotlib subplots

有没有办法将使用 vanilla Matplotlib 创建的额外子图添加到(下方)Seaborn 联合图,共享 x 轴?理想情况下,我想控制联合图和附加图之间的比率(类似于 gridspec_kw={'height_ratios':[3, 1, 1]}

我试图通过在 Matplotlib 子图中调整 figsize 来伪造它,但是当边缘图中的 KDE 曲线发生变化时,它显然不能很好地工作。虽然我可以手动将输出 PNG 调整为 shrink/grow 其中一个数字,但我希望自动对齐所有内容。

我知道联合网格的设置方式很棘手,但对于精通 Seaborn 基础的人来说,这可能相当简单。

这是一个最小的工作示例,但有两个独立的数字:

import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline

图一

diamonds = sns.load_dataset('diamonds')
g = sns.jointplot(
    data=diamonds,
    x="carat",
    y="price",
    hue="cut",
    xlim=(1, 2),
)
g.ax_marg_x.remove()

图2

fig, (ax1, ax2) = plt.subplots(2,1,sharex=True)
ax1.scatter(x=diamonds["carat"], y=diamonds["depth"], color="gray", edgecolor="black")
ax1.set_xlim([1, 2])
ax1.set_ylabel("depth")
ax2.scatter(x=diamonds["carat"], y=diamonds["table"], color="gray", edgecolor="black")
ax2.set_xlabel("carat")
ax2.set_ylabel("table")

期望输出:

您可以采用 jointplot() 创建的图形,移动其填充(使用 subplots_adjust())并添加 2 个额外的轴。

示例代码需要针对每种特定情况进行一些调整。

import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
import seaborn as sns

diamonds = sns.load_dataset('diamonds')
g = sns.jointplot(data=diamonds, x="carat", y="price", hue="cut",
                  xlim=(1, 2), height=12)
g.ax_marg_x.remove()
g.fig.subplots_adjust(left=0.08, right=0.97, top=1.05, bottom=0.45)

axins1 = inset_axes(g.ax_joint, width="100%", height="30%",
                    bbox_to_anchor=(0, -0.4, 1, 1),
                    bbox_transform=g.ax_joint.transAxes, loc=3, borderpad=0)
axins2 = inset_axes(g.ax_joint, width="100%", height="30%",
                    bbox_to_anchor=(0, -0.75, 1, 1),
                    bbox_transform=g.ax_joint.transAxes, loc=3, borderpad=0)
shared_x_group = g.ax_joint.get_shared_x_axes()
shared_x_group.remove(g.ax_marg_x)
shared_x_group.join(g.ax_joint, axins1)
shared_x_group.join(g.ax_joint, axins2)

axins1.scatter(x=diamonds["carat"], y=diamonds["depth"], color="grey", edgecolor="black")
axins1.set_ylabel("depth")
axins2.scatter(x=diamonds["carat"], y=diamonds["table"], color="grey", edgecolor="black")
axins2.set_xlabel("carat")
axins2.set_ylabel("table")
g.ax_joint.set_xlim(1, 2)
plt.setp(axins1.get_xticklabels(), visible=False)
plt.show()

PS: 包含一些关于共享坐标轴的信息(尽管在这里您可以通过为每个子图设置 xlims 来获得相同的效果)。

定位新轴的代码已改编自此tutorial example

我认为在这种情况下,使用 matplotlib 函数设置图形会比从与 use-case 不匹配的 seaborn 图形布局向后工作更好。

如果你有一个 non-full 子图网格,你必须决定是否要 (A) 设置所有子图然后删除你不想要的,或者 (B) 明确添加您想要的每个子图。让我们在这里选择选项 A。

figsize = (6, 8)
gridspec_kw = dict(
    nrows=3, ncols=2,
    width_ratios=[5, 1],
    height_ratios=[4, 1, 1],
)
subplot_kw = dict(sharex="col", sharey="row")
fig = plt.figure(figsize=figsize, constrained_layout=True)
axs = fig.add_gridspec(**gridspec_kw).subplots(**subplot_kw)

sns.kdeplot(data=df, y="price", hue="cut", legend=False, ax=axs[0, 1])
sns.scatterplot(data=df, x="carat", y="price", hue="cut", ax=axs[0, 0])
sns.scatterplot(data=df, x="carat", y="depth", color=".2", ax=axs[1, 0])
sns.scatterplot(data=df, x="carat", y="table", color=".2", ax=axs[2, 0])

axs[0, 0].set(xlim=(1, 2))

axs[1, 1].remove()
axs[2, 1].remove()

顺便说一句,几乎 plt.subplot_mosaic 更容易一些,但它还不支持轴共享。