Blake Alexander
Blake Alexander

Reputation: 27

Using "hue" for a Seaborn visual: how to get legend in one graph?

I created a scatter plot in seaborn using seaborn.relplot, but am having trouble putting the legend all in one graph.

When I do this simple way, everything works fine:

import pandas as pd
import numpy as np
from scipy import stats
import matplotlib.pyplot as plt
import seaborn as sns

df2 = df[df.ln_amt_000s < 700]
sns.relplot(x='ln_amt_000s', y='hud_med_fm_inc', hue='outcome', size='outcome', legend='brief', ax=ax, data=df2)

The result is a scatter plot as desired, with the legend on the right hand side.

enter image description here

However, when I try to generate a matplotlib figure and axes objects ahead of time to specify the figure dimensions I run into problems:

a4_dims = (10, 10) # generating a matplotlib figure and axes objects ahead of time to specify figure dimensions
df2 = df[df.ln_amt_000s < 700]
fig, ax = plt.subplots(figsize = a4_dims)
sns.relplot(x='ln_amt_000s', y='hud_med_fm_inc', hue='outcome', size='outcome', legend='brief', ax=ax, data=df2)

The result is two graphs -- one that has the scatter plots as expected but missing the legend, and another one below it that is all blank except for the legend on the right hand side. enter image description here

How do I fix this such? My desired result is one graph where I can specify the figure dimensions and have the legend at the bottom in two rows, below the x-axis (if that is too difficult, or not supported, then the default legend position to the right on the same graph would work too)? I know the problem lies with "ax=ax", and in the way I am specifying the dimensions as matplotlib figure, but I'd like to know specifically why this causes a problem so I can learn from this.

Thank you for your time.

Upvotes: 1

Views: 3860

Answers (1)

Brendan
Brendan

Reputation: 4011

The issue is that sns.relplot is a "Figure-level interface for drawing relational plots onto a FacetGrid" (see the API page). With a simple sns.scatterplot (the default type of plot used by sns.relplot), your code works (changed to use reproducible data):

df = pd.read_csv("https://vincentarelbundock.github.io/Rdatasets/csv/datasets/iris.csv", index_col=0)
fig, ax = plt.subplots(figsize = (5,5))
sns.scatterplot(x = 'Sepal.Length', y = 'Sepal.Width', 
            hue = 'Species', legend = 'brief',
            ax=ax, data = df)
plt.show()

enter image description here


Further edits to legend

Seaborn's legends are a bit finicky. Some tweaks you may want to employ:

  • Remove the default seaborn title, which is actually a legend entry, by getting and slicing the handles and labels
  • Set a new title that is actually a title
  • Move the location and make use of bbox_to_anchor to move outside the plot area (note that the bbox parameters need some tweaking depending on your plot size)
  • Specify the number of columns
fig, ax = plt.subplots(figsize = (5,5))
sns.scatterplot(x = 'Sepal.Length', y = 'Sepal.Width', 
            hue = 'Species', legend = 'brief',
            ax=ax, data = df)
handles, labels = ax.get_legend_handles_labels()
ax.legend(handles=handles[1:], labels=labels[1:], loc=8, 
          ncol=2, bbox_to_anchor=[0.5,-.3,0,0])

plt.show()

enter image description here

Upvotes: 3

Related Questions