mwaskom / seaborn

Statistical data visualization in Python
https://seaborn.pydata.org
BSD 3-Clause "New" or "Revised" License
12.41k stars 1.91k forks source link

How to move the legend of sns.jointplot() with kind='kde'? #3253

Closed sorenwacker closed 1 year ago

sorenwacker commented 1 year ago

Hi,

I am trying to move the legend of a jointplot, but none of the methods works. I tired


import seaborn as sns

penguins = sns.load_dataset("penguins")

sns.jointplot(data=penguins, x="bill_length_mm", y="bill_depth_mm", hue="species")

image

sns.jointplot(data=penguins, x="bill_length_mm", y="bill_depth_mm", hue="species", legend_out=True)
>>> AttributeError: 'PathCollection' object has no property 'legend_out'

g = sns.jointplot(data=penguins, x="bill_length_mm", y="bill_depth_mm", hue="species")
g._legend.set_bbox_to_anchor((1.1, 1.1))
>>> AttributeError: 'JointGrid' object has no attribute '_legend'

g = sns.jointplot(data=penguins, x="bill_length_mm", y="bill_depth_mm", hue="species")
sns.move_legend(g, "upper left", bbox_to_anchor=(.55, .45), title='Species')
>>> TypeError: `obj` must be a seaborn Grid or matplotlib Axes or Figure instance.

This works for 0.11.2, but not for 0.12.2:


g = sns.jointplot(data=penguins, x="bill_length_mm", y="bill_depth_mm", hue="species")
g.ax_joint.legend(bbox_to_anchor=(1.5, 1.1))

In seaborn 0.12.2 the legend is moved, but also cleared of any labels.


Also penguins = sns.load_dataset("penguins") does not work with 0.12.2. Just ran into this while generating this issue.

Cell In[2], line 3
      1 import seaborn as sns
----> 3 penguins = sns.load_dataset("penguins")
      5 sns.jointplot(data=penguins, x="bill_length_mm", y="bill_depth_mm", hue="species")

File /bulk/LSARP/envs/conda/pycaret/lib/python3.8/site-packages/seaborn/utils.py:584, in load_dataset(name, cache, data_home, **kws)
    581 url = f"[https://raw.githubusercontent.com/mwaskom/seaborn-data/master/{name}.csv](https://raw.githubusercontent.com/mwaskom/seaborn-data/master/%7Bname%7D.csv)"
    583 if cache:
--> 584     cache_path = os.path.join(get_data_home(data_home), os.path.basename(url))
    585     if not os.path.exists(cache_path):
    586         if name not in get_dataset_names():

File /bulk/LSARP/envs/conda/pycaret/lib/python3.8/site-packages/seaborn/utils.py:534, in get_data_home(data_home)
    532 data_home = os.path.expanduser(data_home)
    533 if not os.path.exists(data_home):
--> 534     os.makedirs(data_home)
    535 return data_home

File /bulk/LSARP/envs/conda/pycaret/lib/python3.8/os.py:223, in makedirs(name, mode, exist_ok)
    221         return
    222 try:
--> 223     mkdir(name, mode)
    224 except OSError:
    225     # Cannot rely on checking for EEXIST, since the operating system
    226     # could give priority to other errors like EACCES or EROFS
    227     if not exist_ok or not path.isdir(name):
>>>OSError: [Errno 38] Function not implemented: '/home/jovyan/.cache/seaborn'

Note: This is probably because I have to use Python 3.8.16.

sorenwacker commented 1 year ago

I am using kind='kde':


g = sns.jointplot(data=penguins, x="bill_length_mm", y="bill_depth_mm", hue="species", kind='kde')
g.ax_joint.legend(bbox_to_anchor=(1.5, 1.1))

image

thuiop commented 1 year ago

JointGrid is a bit peculiar because unlike the other Grid objects, the legend is not attached to the figure but directly to the central ax. sns.move_legend(g.figure.axes[0],loc=...) will do the trick.

sorenwacker commented 1 year ago

This works:

  g = sns.jointplot(data=penguins, x="bill_length_mm", y="bill_depth_mm", hue="species", kind='kde')
  sns.move_legend(g.ax_joint, "upper left", bbox_to_anchor=(.55, .45), title='Species')
mwaskom commented 1 year ago

That's right; either approach will work, though I'd recommend the second as both shorter and more explicit.