-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Open
Description
Hi!
I'm making a figure with three subpanels. The bottom panel is a scatterplot which needs a legend. I'm using subplot_mosaic to arrange the panels, then combining the objects interface with matplotlib to have finer control and to also build the plots. One thing I've encountered is that the legend created by the objects interface does not pass to the matplotlib axis. As a result, I can't move it using either ax.legend() or sns.move_legend():
fig, axs = plt.subplot_mosaic([['hist1', 'hist2'],
['scatter1', 'scatter1']],
figsize=(6.5, 6),
height_ratios=(1, 4),
layout='constrained')
(
so.Plot(x='m2', y='Plot_treat', data=full_df)
# .add(so.Dot(alpha=0.10), so.Dodge(), so.Jitter())
.add(so.Bar(artist_kws={'zorder': 10}), so.Est(), legend=False)
.add(so.Range(artist_kws={'zorder': 10}), so.Est(errorbar='se'), legend=False)
.label(y='', x='Total ANPP (g m$^{\mathregular{2}}$)', legend=False)
.scale(y=so.Nominal(order=['Ambient', 'Drought']))
.theme(theme)
.on(axs['hist1'])
.plot()
)
axs['hist1'].xaxis.set_label_position('top')
axs['hist1'].xaxis.tick_top()
axs['hist1'].set_xscale('log')
axs['hist1'].set_xticks([1, 10, 50, 100])
axs['hist1'].set_xticklabels([1, 10, 50, 100])
axs['hist1'].text(75, 0.5, '*', ha='center', va='center', size=18)
(
so.Plot(x='m2', y='Herb_treat', data=full_df)
# .add(so.Dot(alpha=0.10), so.Dodge(), so.Jitter())
.add(so.Bar(artist_kws={'zorder': 10}), so.Est(), legend=False)
.add(so.Range(artist_kws={'zorder': 10}), so.Est(errorbar='se'), legend=False)
.label(y='', x='Total ANPP (g m$^{\mathregular{2}}$)', legend=False)
.scale(y=so.Nominal(order=['No Consumers', 'Consumers']))
.theme(theme)
.on(axs['hist2'])
.plot()
)
axs['hist2'].xaxis.set_label_position('top')
axs['hist2'].xaxis.tick_top()
axs['hist2'].yaxis.tick_right()
axs['hist2'].set_xscale('log')
axs['hist2'].set_xticks([1, 10, 50, 100])
axs['hist2'].set_xticklabels([1, 10, 50, 100])
axs['hist2'].set_yticklabels(['No\nConsumers', 'Consumers'])
axs['hist2'].text(75, 0.5, '*', ha='center', va='center', size=18)
rmod = smf.ols('np.log(m2) ~ Plot_treat*np.log(Total_N)', full_df).fit()
xpred_a = np.linspace(full_df.query('Plot_treat == "Ambient"')['Total_N'].min(),
full_df.query('Plot_treat == "Ambient"')['Total_N'].max(),
100)
xpred_d = np.linspace(full_df.query('Plot_treat == "Drought"')['Total_N'].min(),
full_df.query('Plot_treat == "Drought"')['Total_N'].max(),
100)
dfa = pd.DataFrame({'Total_N': xpred_a, 'Plot_treat': 'Ambient'})
dfb = pd.DataFrame({'Total_N': xpred_d, 'Plot_treat': 'Drought'})
preddf = pd.concat((dfa, dfb))
preds = rmod.get_prediction(preddf)
preddf = (preddf
.assign(m2 = np.exp(preds.predicted))
.assign(upper = np.exp(preds.predicted + preds.se))
.assign(lower = np.exp(preds.predicted - preds.se))
.reset_index()
)
(
so.Plot(data=full_df)
.add(so.Dot(alpha=0.35), x='Total_N', y='m2', color='Plot_treat', data=full_df, legend=False)
.add(so.Line(), x='Total_N', y='m2', color='Plot_treat', data=preddf)
.add(so.Band(), x='Total_N', ymin='lower', ymax='upper', color='Plot_treat', data=preddf)
.scale(color=so.Nominal(order=['Ambient', 'Drought']))
.label(x='Total Plant Available N', y='Total ANPP (g m$^{\mathregular{2}}$)', color='')
.theme(theme)
.on(axs['scatter1'])
.plot()
)
sns.move_legend(axs['scatter1'], 'lower left')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/nate/miniforge3/lib/python3.12/site-packages/seaborn/utils.py", line 432, in move_legend
raise ValueError(err)
ValueError: Axes(0.103889,0.0726394;0.762606x0.669495) has no legend attached.
Same here:
axs['scatter1'].legend(loc='lower left')
<stdin>:1: UserWarning: No artists with labels found to put in legend. Note that artists whose label start with an underscore are ignored when legend() is called with no argument.
<matplotlib.legend.Legend object at 0x7c4e4ebf7b60>
Ultimately, the figure looks amazing, but I have to manually move the legend in Inkscape afterwards.

Metadata
Metadata
Assignees
Labels
No labels