Search code examples
loopsmatplotlibseabornsubplotaxis-labels

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


Update: If you find yourself here with a similar problem, I found a solution that worked for me (see my response below) here; Adjust y-axis in Seaborn multiplot


The best is the enemy of the good...

I wrote a for loop to create a figure with two rows and four columns. The xaxis is shared by column (sharex='col'), the yaxis by row (sharey='row'). And it worked great...until I tried to use this inside my loop to force the yaxis range of the first row:

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

Now when I run the code, only the subplot axes[0,3] (i.e. the final subplot in this row) respects the axes[0, i].set(yticks=range(0,70,10)) line of code. The other three subplots to the left of it on the same row keep their default ytick values derived from the data (see image below).

This means that, despite the fact that sharey='row', axes[0,0], axes[0,1], and axes[0,2] share a yaxis that is not shared by axes[0,3].

If I set sharey=False, then all the subplots in the row respect the axes[0, i].set(yticks=range(0,70,10)) code, but not according to the same vertical scale, not to mention that sharey=False also messes up my second row of subplots.

Does anyone understand why axes[0, i].set(yticks=range(0,70,10)) is seemingly being applied only once in the loop, instead of four times? Thank you.

Edit: I can't provide data, but here's basically what my full code looks like:

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)

And here's what I'm getting out. See that the top right subplot, axes[0,3], has the yaxis the way I want, but not the others on the same row: enter image description here


Solution

  • Edit: I found a solution that worked for me in this solution here: https://stackoverflow.com/a/57101557/13917918

    By adding g = sns.lineplot... and then placing a call to g.axes.set_ylim(0,70) inside the loop, I got the desired result. Example of working code:

    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) 
        
         
    

    enter image description here