I am trying to generate a figure with 4 subplots, each of which is a Seaborn histplot. The figure definition lines are:
fig,axes=plt.subplots(2,2,figsize=(6.3,7),sharex=True,sharey=True)
(ax1,ax2),(ax3,ax4)=axes
fig.subplots_adjust(wspace=0.1,hspace=0.2)
I would like to define strings for legend entries in each of the subplots. As an example, I am using the following code for the first subplot:
sp1=sns.histplot(df_dn,x="ktau",hue="statind",element="step", stat="density",common_norm=True,fill=False,palette=colvec,ax=ax1)
ax1.set_title(r'$d_n$')
ax1.set_xlabel(r'max($F_{a,max}$)')
ax1.set_ylabel(r'$\tau_{ken}$')
legend_labels,_=ax1.get_legend_handles_labels()
ax1.legend(legend_labels,['dep-','ind-','ind+','dep+'],title='Stat.ind.')
The legend is not showing correctly (legend entries are not plotted and the legend title is the name of the hue variable ("statind"). Please note I have successfully used the same code for other figures in which I used Seaborn relplots instead of histplots.
The main problem is that ax1.get_legend_handles_labels()
returns empty lists (note that the first return value are the handles, the second would be the labels).
Basically, there are two ways to change the legend labels. One is to reuse the "handles" of the existing legend. Another approach is to rename the values.
To get the handles, you can do
legend = ax1.get_legend()
handles = legend.legend_handles
(In older matplotlib versions, the name was legend.legendHandles
)
A new legend can be created starting from some handles of the existing legend. Creating a new legend automatically removes an existing one.
Also note that to be sure of the order of the labels, it helps to set hue_order
. Here is some example code to show the ideas:
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
df_dn = pd.DataFrame({'ktau': np.random.randn(4000).cumsum(),
'statind': np.repeat([*'abcd'], 1000)})
fig, ax1 = plt.subplots()
sns.histplot(df_dn, x="ktau", hue="statind", hue_order=['a', 'b', 'c', 'd'],
element="step", stat="density", common_norm=True, fill=False, ax=ax1)
ax1.set_title(r'$d_n$')
ax1.set_xlabel(r'max($F_{a,max}$)')
ax1.set_ylabel(r'$\tau_{ken}$')
legend = ax1.get_legend()
handles = legend.legend_handles
ax1.legend(handles, ['dep-', 'ind-', 'ind+', 'dep+'], title='Stat.ind.')
plt.show()
Renaming the column values, will put these in the legend. The title can either be changed by changing the column name, or by using sns.movelegend(..., title=...)
. Its name may be a bit misleading, as sns.movelegend()
not only serves to move the legend's location, but also other properties (font size, number of columns, ...).
fig, ax1 = plt.subplots()
df_renamed = df_dn.replace({'statind': {'a': 'dep-', 'b': 'ind-', 'c': 'ind+', 'd': 'dep+'}})
sns.histplot(df_renamed, x="ktau", hue="statind",
element="step", stat="density", common_norm=True, fill=False, ax=ax1)
ax1.set_title(r'$d_n$')
ax1.set_xlabel(r'max($F_{a,max}$)')
ax1.set_ylabel(r'$\tau_{ken}$')
sns.move_legend(ax1, loc='best', title='Stat.ind.')
plt.show()