Share secondary y axis in looped seaborn plots

Viewed 3221

I am trying to plot a few plots with secondary y axis in the same row in the loop. I would like them to have only one primary y axis on the left side of the first plot and only one secondary y on the right side of the last plot. So far I managed to accomplish the first thing by sharey = True attribute of subplots but I have trouble with the secondary axis.

for r in df.Category1.sort_values().unique():
    dfx = df[df['Category1'] == r]
    fig, axes = plt.subplots(1,3, figsize = (14,6), sharey=True)
    for (n, dfxx), ax in zip(dfx.groupby("Category2"), axes.flat): 
        ax1 = sns.barplot(x = dfxx['Month'], y = dfxx['value1'], hue = dfxx['Category3'], ci = None, palette = palette1, ax=ax)
        ax2 = ax1.twinx()
        ax2 = sns.pointplot(x = dfxx['Month'], y=dfxx['value2'], hue = dfxx['Category3'], ci = None, sort = False, legend = None, palette = palette2) 

plt.tight_layout()
plt.show()

So as you can see one iteration of the loop, it has only one primary y axis on the left but secondary appears on each of the plots and I would like it to be consistent for all the plots and appear only once for the most right plot.

2 Answers

One easy trick to get what you want is to keep the tick-labels and ticks only on the right most axis by turning off the ticks of the first and the second subplot. This can be done using an index i as follows:

for r in df.Category1.sort_values().unique():
    dfx = df[df['Category1'] == r]
    fig, axes = plt.subplots(1,3, figsize = (14,6), sharey=True)
    i = 0 # <--- Initialize a counter
    for (n, dfxx), ax in zip(dfx.groupby("Category2"), axes.flat): 
        ax1 = sns.barplot(x = dfxx['Month'], y = dfxx['value1'], hue = dfxx['Category3'], ci = None, palette = palette1, ax=ax)
        ax2 = ax1.twinx()
        ax2 = sns.pointplot(x = dfxx['Month'], y=dfxx['value2'], hue = dfxx['Category3'], ci = None, sort = False, legend = None, palette = palette2) 
        if i < 2: # <-- Only turn off the ticks for the first two subplots
            ax2.get_yaxis().set_ticks([]) # <-- Hiding the ticks
        i += 1  # <-- Counter for the subplot
plt.tight_layout()

But you should be careful that your 3 subplots have different y-limits on the secondary axis. So better to make the axis limits equal before hiding the ticks. To do so, you can use ax2.set_ylim(minimum, maximum) where minimum, maximum are the values you want the axis to be restricted to.

according to this answer to a similar question, you can use the get_shared_y_axes() function of axes together with its join() method:

fig, axes = plt.subplots(1,3, figsize = (14,6), sharey=True)

secaxes = []                            # list for collecting all secondary y-axes
for i, ax in enumerate(axes):
    ax.plot(range(10))
    secaxes.append(ax.twinx())          # put current secondary y-axis into list
    secaxes[-1].plot(range(10, 0, -1))
secaxes[0].get_shared_y_axes().join(*secaxes) # share all y-axes

for s in secaxes[:-1]:                  # make all secondary y-axes invisible
    s.get_yaxis().set_visible(False)    # except the last one

enter image description here

test for shared scaling:

secaxes[1].plot(range(20, 10, -1))

enter image description here

Related