Georg Heiler
Georg Heiler

Reputation: 17676

Seaborn plot with multiple subplots and multiple y axis for each one

When plotting with twinx how can multiple subplots be used?

%pylab inline
import pandas as pd
import seaborn as sns; sns.set()

df = pd.DataFrame({'dt':['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04'], 'category':['a', 'b', 'a', 'b'], 'foo':[10, 15, 8, 13], 'bar':[12, 8, 5, 18]})
df['dt'] = pd.to_datetime(df['dt'])

ax = sns.lineplot(x='dt', y='foo', data=df, hue='category')
ax.set_ylabel('asdf', fontsize=28)
ax.plot([], '-g', label = 'other axis in legend')
plt.legend(fontsize='x-large')
handles, labels = ax.get_legend_handles_labels()
ax.legend(handles=handles[1:], labels=labels[1:], fontsize='large', loc='lower left')
plt.xticks(rotation=90, horizontalalignment='center', fontsize=28)
plt.xlabel('')
plt.yticks(fontsize=16)

ax2 = ax.twinx()
ax2 = sns.lineplot(x='dt', y='bar', data=df, ax=ax2, color='green')
plt.yticks(fontsize=16)
ax2.plot([], '-g', label = 'other axis in legend')
ax2.set_ylabel('ratio', fontsize=28)

plt.axvline(x=np.datetime64('2020-01-02'),color='k', linestyle='--', lw=4)
plt.text(x=np.datetime64('2020-01-02'), y=10, s=' foo-the-bar  ', fontsize=28, horizontalalignment='left')
plt.show()


d2 = pd.DataFrame({'dt':['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04'], 'category':['a', 'b', 'a', 'b'],'foo':[11, 16, 8, 14], 'bar':[11, 7, 4, 17]})
d2['dt'] = pd.to_datetime(d2['dt'])

ax = sns.lineplot(x='dt', y='foo', data=d2, hue='category')
ax.set_ylabel('something else', fontsize=28)
ax.plot([], '-g', label = 'other axis in legend')
plt.legend(fontsize='x-large')
handles, labels = ax.get_legend_handles_labels()
ax.legend(handles=handles[1:], labels=labels[1:], fontsize='large', loc='lower left')
plt.xticks(rotation=90, horizontalalignment='center', fontsize=28)
plt.xlabel('')
plt.yticks(fontsize=16)
plt.axvline(x=np.datetime64('2020-01-02'),color='k', linestyle='--', lw=4)
plt.text(x=np.datetime64('2020-01-02'), y=10, s=' foo-the-bar  ', fontsize=28, horizontalalignment='left')


ax2 = ax.twinx()
ax2 = sns.lineplot(x='dt', y='bar', data=d2, ax=ax2, color='green')
plt.yticks(fontsize=16)
ax2.plot([], '-g', label = 'other axis in legend')
ax2.set_ylabel('ratio', fontsize=28)
plt.show()

It is more or less nice. But when adding in subplots to combine multiple measurements into a single figure sharing the x-axis (i.e. saving space and writing the dates over and over) the following fails to work and completely distorts the plot

ax0 = plt.subplot(211)
ax2 = ax0.twinx()
ax3 = plt.subplot(212)
ax4 = ax3.twinx()

ax = sns.lineplot(x='dt', y='foo', data=df, hue='category', ax=ax0)
ax.set_ylabel('asdf', fontsize=28)
ax.plot([], '-g', label = 'other axis in legend')
plt.legend(fontsize='x-large')
handles, labels = ax.get_legend_handles_labels()
ax.legend(handles=handles[1:], labels=labels[1:], fontsize='large', loc='lower left')
plt.xticks(rotation=90, horizontalalignment='center', fontsize=28)
plt.xlabel('')
plt.yticks(fontsize=16)
plt.axvline(x=np.datetime64('2020-01-02'),color='k', linestyle='--', lw=4)
plt.text(x=np.datetime64('2020-01-02'), y=10, s=' foo-the-bar  ', fontsize=28, horizontalalignment='left')


#ax2 = ax.twinx()
ax2 = sns.lineplot(x='dt', y='bar', data=df, ax=ax2, color='green')
plt.yticks(fontsize=16)
ax2.plot([], '-g', label = 'other axis in legend')
ax2.set_ylabel('ratio', fontsize=28)
plt.show()


# TODO second plot is missing

Upvotes: 0

Views: 4006

Answers (1)

Diziet Asahi
Diziet Asahi

Reputation: 40697

Problems always arise when trying to mix the object-oriented syntax and the pyplot interface.

pyplot functions (plt.XXX) only affect the current axes (generally the latest created, in your case ax4). When you have several axes, it is generally much better to use the OO functions so that there is no ambiguity about which axes you are working on.

Additionally, you might want to through a tight_layout() at the end of your code to automatically adjust the position of your axes to give enough room for your labels

plt.figure()
ax0 = plt.subplot(211)
ax2 = ax0.twinx()
ax3 = plt.subplot(212)
ax4 = ax3.twinx()

ax = sns.lineplot(x='dt', y='foo', data=df, hue='category', ax=ax0)
ax.set_ylabel('asdf', fontsize=28)
ax.plot([], '-g', label = 'other axis in legend')
handles, labels = ax.get_legend_handles_labels()
ax.legend(handles=handles[1:], labels=labels[1:], fontsize='large', loc='lower left')
ax.set_xticklabels(ax.get_xticklabels(), rotation=90, horizontalalignment='center', fontsize=28)
ax.set_xlabel('')
ax.tick_params(axis='y', labelsize=16)
ax.axvline(x=np.datetime64('2020-01-02'),color='k', linestyle='--', lw=4)
ax.text(x=np.datetime64('2020-01-02'), y=10, s=' foo-the-bar  ', fontsize=28, horizontalalignment='left')


#ax2 = ax.twinx()
ax2 = sns.lineplot(x='dt', y='bar', data=df, ax=ax2, color='green')
ax2.tick_params(axis='y', labelsize=16)
ax2.plot([], '-g', label = 'other axis in legend')
ax2.set_ylabel('ratio', fontsize=28)

plt.tight_layout()
plt.show()

enter image description here

Upvotes: 1

Related Questions