Matplotlib/seaborn - 在创建带有循环的子图时设置 yticks/sharey 的问题

Matplotlib/seaborn - problem with set yticks/sharey when creating subplots figure with loop

更新:如果您在这里遇到类似的问题,我在这里找到了适合我的解决方案(请参阅下面的回复);


最好是好的敌人...

我写了一个 for 循环来创建一个两行四列的图形。 x 轴按列共享 (sharex='col'),按行共享 ya 轴 (sharey='row')。它工作得很好......直到我试图在我的循环中使用它来强制第一行的 yaxis 范围:

axes[0, i].set(yticks=range(0,70,10)) 

现在,当我 运行 代码时,只有子图 axes[0,3](即这一行中的最后一个子图)尊重 axes[0, i].set(yticks=range(0,70,10)) 行代码。同一行左侧的其他三个子图保留从数据派生的默认 ytick 值(见下图)。

这意味着,尽管 sharey='row'axes[0,0]axes[0,1]axes[0,2] 共享一个 axes[0,3] 不共享的轴.

如果我设置sharey=False,那么该行中的所有子图都遵循axes[0, i].set(yticks=range(0,70,10))代码,但不是根据相同的垂直比例,更不用说sharey=False也很乱我的第二行子图。

有谁知道为什么 axes[0, i].set(yticks=range(0,70,10)) 在循环中似乎只被应用了一次,而不是四次?谢谢。

编辑:我无法提供数据,但基本上我的完整代码如下所示:

fig, axes = plt.subplots(2, 4, figsize=(20, 12), sharex=True, sharey='row')

for i in range(4):
    sns.lineplot(ax = axes[0, i], x=x, y=y,
                    data=data)
    sns.despine()
    axes[0, i].grid(True, alpha=0.2)
    axes[0, i].tick_params(axis='both', which='major', labelsize=13)
    axes[0, i].axhline(y=50, color='k', dashes=(2, 1), zorder=0)
    axes[0, i].set(yticks=range(0,70,10))
axes[0, 0].set_ylabel('y axis row 0', fontsize=14)
    

for i in range(4):
    sns.lineplot(ax = axes[1, i], x=x1, y=y1,
                    data=data1)
    sns.despine()
    axes[1, i].grid(True, alpha=0.2)
    axes[1, i].set(xticks=[0, 1, 2, 3, 4])
    axes[1, i].axhline(y=0, color='red', dashes=(2, 1), zorder=0)
    axes[1, i].tick_params(axis='both', which='major', labelsize=13)    
axes[1, 0].set_ylabel('y axis row 1', fontsize=14)

这就是我要说的。看到右上角的子图 axes[0,3] 具有我想要的 yaxis,但同一行的其他子图没有:

这是使用 Seaborn's stock FMRI dataset to show (1) how you can leverage the awesomeness that is Seaborn by using relplot here and perhaps get rid of your loop; and (2) how you can add an axhline. Basically, you let relplot 创建你的子图,然后你在每个子图(方面)上有一个简单的小循环来查看图中的数据,然后根据你的条件添加你的线。

在此示例中,region 字段是行标识符,但看起来您会使用行号。

fmri = sns.load_dataset("fmri")
print(fmri)

g = sns.relplot(
    data=fmri, x="timepoint", y="signal", row="region",
    hue="event", style="event", kind="line",
)

for (row, col, hue_idx), data in g.facet_data():
    ax = g.facet_axis(row, col)
    myline = 0.2 if data['region'].unique() == "frontal" else -0.1
    col = 'red' if data['region'].unique() == "frontal" else 'black'
    ax.axhline(myline, c=col, dashes=(2, 1))

plt.show()

编辑:我在此处的解决方案中找到了适合我的解决方案:

通过添加 g = sns.lineplot... 然后在循环内调用 g.axes.set_ylim(0,70),我得到了想要的结果。工作代码示例:

fig, axes = plt.subplots(2, 4, figsize=(20, 12), sharex=True, sharey='row')

    for i in range(4):
        g = sns.lineplot(ax = axes[0, i], x=x, y=y,
                        data=data)
        g.axes.set_ylim(0,70)
        axes[0, i].grid(True, alpha=0.2)
        axes[0, i].tick_params(axis='both', which='major', labelsize=13)
        axes[0, i].axhline(y=50, color='k', dashes=(2, 1), zorder=0)
    axes[0, 0].set_ylabel('y0', fontsize=14)
    
    for i in range(4):
        sns.lineplot(ax = axes[1, i], x=x1, y=y1,
                        data=data1)
        sns.despine()
        axes[1, i].grid(True, alpha=0.2)
        axes[1, i].set(xticks=[0, 1, 2, 3, 4])
        axes[1, i].axhline(y=0, color='red', dashes=(2, 1), zorder=0)
        axes[1, i].tick_params(axis='both', which='major', labelsize=13)    
    axes[1, 0].set_ylabel('y1', fontsize=14)