99

I've created this plot using Seaborn and a pandas dataframe (data):

enter image description here

My code:

import seaborn as sns

g = sns.lmplot('credibility', 'percentWatched', data=data, hue='millennial', markers=["+", "."], x_jitter=True,
               y_jitter=True, size=5)
g.set(xlabel='Credibility Ranking\n ← Low       High  →', ylabel='Percent of Video Watched [%]')

You may notice the plot's legend title is simply the variable name ('millennial') and the legend items are the variable's values (0, 1). How can I edit the legend's title and labels? Ideally, the legend's title would be 'Generation' and the labels would be "Millennial" and "Older Generations"

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
Oliver G
  • 1,021
  • 2
  • 9
  • 20
  • 12
    `data.assign(Generation=data.millenial.map({0: "Older Generations", 1: "Millenial"}))` – mwaskom Jul 20 '17 at 14:43
  • The comment from @mwaskom (the creator of seaborn) is the simplest option, or rename and map to the original column. To move the legend, see [Move seaborn plot legend to a different position](https://stackoverflow.com/a/68849891/7758804) – Trenton McKinney Sep 14 '21 at 00:06

2 Answers2

134

Took me a while to read through the above. This was the answer for me:

import seaborn as sns
import matplotlib.pyplot as plt
tips = sns.load_dataset("tips")

g = sns.lmplot(
    x="total_bill", 
    y="tip", 
    hue="smoker", 
    data=tips,  
    legend=False
)

plt.legend(title='Smoker', loc='upper left', labels=['Hell Yeh', 'Nah Bruh'])
plt.show(g)

Reference this for more arguments: matplotlib.pyplot.legend

enter image description here

Glen Thompson
  • 9,071
  • 4
  • 54
  • 50
  • 15
    This option does not edit the existing legend, it creates a new legend for the last axes in the figure-level (plots without the `ax` parameter) plot. This is not a good solution for most figure-level plots because it is unlikely to match all markers / colors for all axes. It should be fine for a single axes-level plot (plots with the `ax` parameter) – Trenton McKinney Sep 14 '21 at 00:20
  • 8
    Be aware, when you swap the legend labels `labels=['Hell Yeh', 'Nah Bruh']` the `Hell Yeh` datapoints become the `Nah Bruh` data points. – TMOTTM Dec 01 '21 at 19:46
  • I have been skeptical of swapping legend labels too. I consider it too dangerous. Hence I was considering direct mapper code like. {oldText1: newText1 , oldText2: newText2 } – Walker Nov 27 '22 at 04:41
  • Given changes to `matplotlib` and `seaborn`, this option no longer works. `plt.show(g)` results in `TypeError: Show.__call__() takes 1 positional argument but 2 were given`. If `g` is removed, [this plot](https://i.stack.imgur.com/GKIMd.png) is created, but the legend is not correct. – Trenton McKinney Aug 03 '23 at 14:26
122
  • If legend_out is set to True then legend is available through the g._legend property and it is a part of a figure. Seaborn legend is standard matplotlib legend object. Therefore you may change legend texts.
  • Tested in python 3.8.11, matplotlib 3.4.3, seaborn 0.11.2
import seaborn as sns

# load the tips dataset
tips = sns.load_dataset("tips")

# plot
g = sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips, markers=["o", "x"], facet_kws={'legend_out': True})

# title
new_title = 'My title'
g._legend.set_title(new_title)
# replace labels
new_labels = ['label 1', 'label 2']
for t, l in zip(g._legend.texts, new_labels):
    t.set_text(l)

enter image description here

Another situation if legend_out is set to False. You have to define which axes has a legend (in below example this is axis number 0):

g = sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips, markers=["o", "x"], facet_kws={'legend_out': False})

# check axes and find which is have legend
leg = g.axes.flat[0].get_legend()
new_title = 'My title'
leg.set_title(new_title)
new_labels = ['label 1', 'label 2']
for t, l in zip(leg.texts, new_labels):
    t.set_text(l)

enter image description here

Moreover you may combine both situations and use this code:

g = sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips, markers=["o", "x"], facet_kws={'legend_out': True})

# check axes and find which is have legend
for ax in g.axes.flat:
    leg = g.axes.flat[0].get_legend()
    if not leg is None: break
# or legend may be on a figure
if leg is None: leg = g._legend

# change legend texts
new_title = 'My title'
leg.set_title(new_title)
new_labels = ['label 1', 'label 2']
for t, l in zip(leg.texts, new_labels):
    t.set_text(l)

enter image description here

This code works for any seaborn plot which is based on Grid class.

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
Serenity
  • 35,289
  • 20
  • 120
  • 115
  • 8
    Are you sure it's `g._legend.set_title(new_title)` and not `g.legend_.set_title(new_title)`? – Rylan Schaeffer Oct 06 '21 at 03:13
  • 2
    When I generate a plot using `sns.kdeplot`, there is no `_legend` attribute but there is a `legend_` attribute on the resulting axes object. – Steven C. Howell Oct 30 '21 at 05:14
  • 2
    The difference is you should use `_legend` for any plot which is based on the Grid class as noted in the original answer. `sns.kdeplot` (and some others) will instead generate an `matplotlib.axes._subplots.AxesSubplot` object, for which you should use the `legend_` property. In practice you can try both and use the one that works. – Srikiran Nov 02 '21 at 00:42