1

I want to combine multiple plot legends with a catplot legend and place the single result in the upper right. I also want to replace the labels on both. Examples - {"avg tip":"average", "Yes":"Smoker","No":"Non smoker"}

My actual work is more complex but here's a nice example of the point stolen from someone else's answer elsewhere.

enter image description here

Example:

tips = sns.load_dataset("tips")
g = sns.catplot(x="day", y="total_bill", hue="smoker", kind="violin", inner='quartile', split=True, data=tips)
legend=g._legend
legend.set(title='')

l1 = plt.axhline(tips.total_bill.mean(), ls='--', color='k', alpha=0.3, zorder=0, label='Avg tip')
l2 = plt.axhline(tips.total_bill.mean()+tips.total_bill.std(), ls='--', color='r', alpha=0.1, zorder=0, label='+1 std')
l2 = plt.axhline(tips.total_bill.mean()-tips.total_bill.std(), ls='--', color='b', alpha=0.1, zorder=0, label='1 std')

legend2 = plt.legend(title='', loc='upper right', handles=[l1,l2,l3])
2
  • You could extract the handles and labels from g._legend and add them to the handles and labels used by plt.legend(...). And remove g._legend.
    – JohanC
    Commented Jan 23 at 19:49
  • I don't understand/can't find how to access the handles and labels from g._legend. It seems daft but I'm going round in circles. I tried a few stabs in the dark... g.get_legend_handles_labels(), g.handles, g._legend.handles etc Commented Jan 23 at 20:12

1 Answer 1

1

Figured out the answer myself eventually. Answer for posterity so please edit or suggest edits to improve....

catplot & lineplot legends merged

Catplot's legend appears not to be the same as the legend of other plots, presumably because catplot is already a combination of other plots from a facetplot call, or some figure level magic or something. I could not add other handles directly to the legend for catplot, but must have the default parameter legend=True for catplot or it doesn't return any legend artist at all. Instead, I took the following approach:

  • output an artist g = sns.catplot(x="day", y="total_bill", hue="smoker", kind="violin", inner='quartile', split=True, data=tips)

  • extract the legend handles catplot_handles = g._legend.legendHandles

  • set the legend visibility to False g._legend.set(visible=False)

  • add the catplot handles to a list of other artists in an externally created legend, optionally adding a list of all labels in this call. Note, I've explicitly shown this as 2 concatenated lists for clarity only plt.legend(handles=[l1,l2,l3]+catplot_handles, labels=['label1',label2','label3']+['cat_label1',cat_label2'])

Full code

import matplotlib.pyplot as plt

# load data
tips = sns.load_dataset("tips")

# run my primary cateegory plot, assigning the output to 'g'
g = sns.catplot(x="day", y="total_bill", hue="smoker", kind="violin", inner='quartile', split=True, data=tips)

# Run my other assigned plots
l1 = plt.axhline(tips.total_bill.mean(), ls='--', color='k', alpha=0.5, zorder=0)
l2 = plt.axhline(tips.total_bill.mean()+tips.total_bill.std(), ls='--', color='r', alpha=0.3, zorder=0)
l3 = plt.axhline(tips.total_bill.mean()-tips.total_bill.std(), ls='--', color='b', alpha=0.3, zorder=0)

# extract the catplot artist legend handles
catplot_handles = g._legend.legendHandles
# make the in built catplot legend invisible
g._legend.set(visible=False)
# merge all the artist legend handles
legend2 = plt.legend(title='', loc='upper right', handles=[l1,l2,l3]+catplot_handles, labels=['Average', '+1 std', '-1 std', 'Smoker', 'Non-smoker'])

plt.show()

Not the answer you're looking for? Browse other questions tagged or ask your own question.