pythonmatplotlibseabornlegendhistplot

How to add hatches to histplot bars and legend


I created a bar plot with hatches using seaborn. I was also able to add a legend that included the hatch styles, as shown in the MWE below:

import matplotlib.pyplot as plt
import seaborn as sns

tips = sns.load_dataset("tips")

hatches = ['\\\\', '//']

fig, ax = plt.subplots(figsize=(6,3))
sns.barplot(data=tips, x="day", y="total_bill", hue="time")

# loop through days
for hues, hatch in zip(ax.containers, hatches):
    # set a different hatch for each time
    for hue in hues:
        hue.set_hatch(hatch)

# add legend with hatches
plt.legend().loc='best'

plt.show()

enter image description here

However, when I try to create a histogram on seaborn with a legend, the same code does not work; I get the following error: No artists with labels found to put in legend. Note that artists whose label start with an underscore are ignored when legend() is called with no argument.

I've looked for an answer online, but I haven't been successful at finding one.

How can I add the hatches to the legend of a histogram for the MWE below?

import matplotlib.pyplot as plt
import seaborn as sns

tips = sns.load_dataset("tips")

hatches = ['\\\\', '//']

fig, ax = plt.subplots(figsize=(6,3))
sns.histplot(data=tips, x="total_bill", hue="time", multiple='stack')

# loop through days
for hues, hatch in zip(ax.containers, hatches):
    # set a different hatch for each time
    for hue in hues:
        hue.set_hatch(hatch)

# add legend with hatches
plt.legend().loc='best' # this does not work

plt.show()

enter image description here


Solution

  • The issue is ax1.get_legend_handles_labels() returns empty lists for seaborn.histplot. Refer to this answer. Use the explicit interface by adding ax=ax to seaborn.histplot(...). Use ax.get_legend().legend_handles (.legendHandles is deprecate) to get the handles for the legend, and add hatches with set_hatch().

    ax.get_legend().legend_handles returns the handles in the reverse order compared to the container, so the order can be reversed with [::-1].

    Tested in python 3.11.2, matplotlib 3.7.1, seaborn 0.12.2

    import matplotlib.pyplot as plt
    import seaborn as sns
    
    tips = sns.load_dataset("tips")
    
    hatches = ['\\\\', '//']
    
    fig, ax = plt.subplots(figsize=(6, 3))
    sns.histplot(data=tips, x="total_bill", hue="time", multiple='stack', ax=ax)  # added ax=ax
    
    # iterate through each container, hatch, and legend handle
    for container, hatch, handle in zip(ax.containers, hatches, ax.get_legend().legend_handles[::-1]):
        
        # update the hatching in the legend handle
        handle.set_hatch(hatch)
        
        # iterate through each rectangle in the container
        for rectangle in container:
    
            # set the rectangle hatch
            rectangle.set_hatch(hatch)
    
    plt.show()
    

    enter image description here