Search code examples
pythonpandasseabornlegendscatter-plot

How to create separate legend sections for colors and markers


I would like to plot two features in the same plot. My problem is with the legend. I would like to have one legend for colors(species) and one for the marker (Label defined by me). And I don't need to repeat the colors in the legend, as is happening in this example.

this is what I'm trying:

import seaborn as sns
import matplotlib.pyplot as plt
import random
import matplotlib.colors as mcolors

random.seed(5)
iris = sns.load_dataset("iris")

species_list = list(iris.species.unique())
colors_name = list(mcolors.CSS4_COLORS.keys())
color_species = random.sample(colors_name,len(species_list))


fig,ax = plt.subplots(1,1,figsize=(10,6))

sns.scatterplot(data=iris,edgecolor="black", x=iris.index.values, y='sepal_length',s=50,alpha=0.8, hue='species',palette=color_species,ax=ax,label='Feat. 1')
sns.scatterplot(data=iris,edgecolor="black",marker='*', x=iris.index.values, y='sepal_width',s=90,alpha=0.8, hue='species',palette=color_species,ax=ax, label='Feat. 2')
ax.legend(loc='upper right',bbox_to_anchor=(1.5,1))

ax.set_xlabel('Sample')
ax.set_ylabel('Feature 1 and 2')

enter image description here


Solution

  • To manipulate the legend, you need to get the legend handles using get_legend_handles_labels(), then pick half the entries (3 in your case) and plot them as a legend. Then use matplotlib.lines to create the kind of icon you need (color, shape, etc.) and pass that as the handles for second legend along with the label text you want. You can use title to add titles to the legends. The code is shown below. Hope this is what you are looking for.

    import seaborn as sns
    import matplotlib.pyplot as plt
    import random
    import matplotlib.colors as mcolors
    
    random.seed(5)
    iris = sns.load_dataset("iris")
    
    species_list = list(iris.species.unique())
    colors_name = list(mcolors.CSS4_COLORS.keys())
    color_species = random.sample(colors_name,len(species_list))
    
    fig,ax = plt.subplots(1,1,figsize=(10,6))
    
    sns.scatterplot(data=iris,edgecolor="black", x=iris.index.values, y='sepal_length',s=50,alpha=0.8, hue='species',
                    palette=color_species,ax=ax)#,label='Feat. 1')
    sns.scatterplot(data=iris,edgecolor="black",marker='*', x=iris.index.values, y='sepal_width',s=90,alpha=0.8, 
                    hue='species',palette=color_species,ax=ax)#, label='Feat. 2')
    
    h,l = ax.get_legend_handles_labels() ##Get the legend handles and lables
    l1 = ax.legend(h[:int(len(h)/2)],l[:int(len(l)/2)], loc='upper right',bbox_to_anchor=(1.2,1), title='Title 1') ##Plot half as the first legend
    
    from matplotlib.lines import Line2D
    myHandle = [Line2D([], [], marker='.', color='red', markersize=10, linestyle='None'),
              Line2D([], [], marker='*', color='blue', markersize=10, linestyle='None')] ##Create custom handles for 2nd legend
    
    l2 = ax.legend(handles=myHandle, labels = ['MyLabel1', 'MyLabel2'], loc='upper right',bbox_to_anchor=(1.2,0.8), title='Title 2') ##Add 2nd legend
    ax.add_artist(l1) # 2nd legend will erases the first, so need to add it
    
    ax.set_xlabel('Sample')
    ax.set_ylabel('Feature 1 and 2')
    

    enter image description here