How to make an ax.twinx() for a split y-axis?

Viewed 338

I have the following plot with a split y axis:

df = pd.DataFrame({
          'x': ["a","b","c","d"], 
          'y': [10,20,30,400],
          'z':[50, 60, 70, 80]})

f, (ax_top, ax_bottom) = plt.subplots(ncols=1, nrows=2, sharex=True, gridspec_kw={'hspace':0.2})

sns.barplot(x='x', y='y', data = df, palette='summer', ax=ax_top)
ax_top.tick_params(top=False, bottom=False, left=False, right=False, labelleft=True,labelbottom=False)

sns.barplot(x='x', y='y', data = df, palette='summer', ax=ax_bottom)

ax_top.set_ylim(bottom=300) 
ax_bottom.set_ylim(0,50)
ax_top.set_xlabel(""), ax_bottom.set_ylabel(""), ax_top.set_ylabel(""), ax_bottom.set_xlabel("")

sns.despine(ax=ax_bottom)
sns.despine(ax=ax_top, bottom=True)

ax = ax_top
d = .015  
kwargs = dict(transform=ax.transAxes, color='k', clip_on=False)
ax.plot((-d, +d), (-d, +d), **kwargs)     
ax2 = ax_bottom
kwargs.update(transform=ax2.transAxes)  
ax2.plot((-d, +d), (1 - d, 1 + d), **kwargs)  

ax3 = ax_top.twinx()
sns.lineplot(x = 'x', y = 'z', data = df, c='red', ax=ax3)
sns.despine()
plt.show()

Which yields the following image:

enter image description here

Is there a way to extend the twin ax where z is plotted up until the bottom ax x axis? I've been trying to add an extra ax using the bbox of ax_top and ax_bottom, but no luck... Note that ideally the line will not break between the ax_top and the ax_bottom.

Other solutions to split the y axis are welcome.

Many thanks!

EDIT

Why @jylls answer won't work with the lineplot:

code:

import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns

df = pd.DataFrame({
      'x': ["a","b","c","d"], 
      'y': [10,20,30,400],
      'z':[50, 60, 70, 80]})

f, (ax_top, ax_bottom) = plt.subplots(ncols=1, nrows=2, sharex=True, gridspec_kw={'hspace':0.2})

#Creating top plot
sns.barplot(x='x', y='y', data = df, palette='summer', ax=ax_top)

ax_top.tick_params(top=False, bottom=False, left=False, right=False, 
labelleft=True,labelbottom=False)
ax_top.set_ylim(bottom=300)
ax_top.set_xlabel(""), ax_bottom.set_ylabel(""), ax_top.set_ylabel(""), 
ax_bottom.set_xlabel("")
sns.despine(ax = ax_top)
ax_top.spines['bottom'].set_visible(False)

sns.barplot(x='x', y='y', data = df, palette='summer', ax=ax_bottom)

ax_bottom.set_ylim(0,50)
sns.despine(ax=ax_bottom)

ax = ax_top
d = .015  
kwargs = dict(transform=ax.transAxes, color='k', clip_on=False)
ax.plot((-d, +d), (-d, +d), **kwargs)        # top-left diagonal

ax2 = ax_bottom
kwargs.update(transform=ax2.transAxes)  
ax2.plot((-d, +d), (1 - d, 1 + d), **kwargs)  # bottom-left diagonal
sns.despine(ax = ax2)



#Creating top twin axis
ax_top_2= ax_top.twinx()
sns.lineplot(x='x', y='z', data = df, color='red', ax=ax_top_2)

#Setting z-limits
ax_top_2.set_ylim(60,100)
sns.despine(ax = ax_top_2)
ax_top_2.spines['bottom'].set_visible(False)

#Creating bottom twin axis
ax_bottom_2 = ax_bottom.twinx()
sns.lineplot(x='x', y='z', data = df, color='red', ax=ax_bottom_2)
ax_bottom_2.set_ylim(0,55)
ax_bottom_2.set_ylabel('')

sns.despine(ax=ax_bottom_2)

Yields the following plot:

enter image description here

Is it possible to connect that line somehow? This is why I think an ax on top of the plt figure would work better... And that's where I am stuck

EDIT2 The desired output is given in orange below:

enter image description here

1 Answers

Your figure is composed of two axes (ax_top and ax_bottom). You created a twin axis for the top one (ax_top_2 in my code) in order to plot your stripplot which means that you need to create a twin axis for your bottom plot if you want the z axis to extend to the bottom of the bottom x axis. Once, you create that twin axis (ax_bottom_2), you will need to think what limits you want to set for your z axis. Do you want all z values to be plotted in the top subplot or in the bottom subplot or both? Below, I opted to spread the stripplot across the two subplots (bottom and tot) but there are many different ways to do that depending on what you want to show.

You can see more details in the code below:

import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns

df = pd.DataFrame({
          'x': ["a","b","c","d"], 
          'y': [10,20,30,400],
          'z':[50, 60, 70, 80]})

f, (ax_top, ax_bottom) = plt.subplots(ncols=1, nrows=2, sharex=True, gridspec_kw={'hspace':0.2})

#Creating top plot
sns.barplot(x='x', y='y', data = df, palette='summer', ax=ax_top)
ax_top.tick_params(top=False, bottom=False, left=False, right=False, labelleft=True,labelbottom=False)
ax_top.set_ylim(bottom=300) 
ax_top.set_xlabel("")
ax_top.set_ylabel("")

#Creating bottom plot
sns.barplot(x='x', y='y', data = df, palette='summer', ax=ax_bottom)
ax_bottom.set_ylim(0,50)
ax_bottom.set_ylabel("")
ax_bottom.set_xlabel("")


sns.despine(ax=ax_top,bottom=True)
sns.despine(ax=ax_bottom)

d = .015  
kwargs = dict(transform=ax_top.transAxes, color='k', clip_on=False)
ax_top.plot((-d, +d), (-d, +d), **kwargs) 
kwargs.update(transform=ax_bottom.transAxes)  
ax_bottom.plot((-d, +d), (1 - d, 1 + d), **kwargs)  

#Creating top twin axis
ax_top_2= ax_top.twinx()
sns.stripplot(x = 'x', y = 'z', data = df, c='red', ax=ax_top_2)
#Setting z-limits
ax_top_2.set_ylim(55,110)
sns.despine()

#Creating bottom twin axis
ax_bottom_2 = ax_bottom.twinx()
sns.stripplot(x = 'x', y = 'z', data = df, c='red', ax=ax_bottom_2)

#Setting z-limits
ax_bottom_2.set_ylim(0,55)
ax_bottom_2.set_ylabel('')
sns.despine()
plt.show()

And the output gives:

enter image description here

Related