How to add hatch to custom legend in matplotlib?

Viewed 2112

Edit: FWIW I am fixing the legend in Illustrator, but would prefer to do it automatically because I have about 20 of these plots to make

I followed this very helpful answer for adding a custom legend to a matplotlib violinplot, which doesn't support its own legend. It worked great except when I tried to add hatches.

Here's my code for the labels (I tried adding patch two different ways):

    labels = [ 'Low entropy bin', 'Medium entropy bin', 'High entropy bin' ]
    legend_patches = 3*[matplotlib.patches.Patch( color='#DCDCDC', hatch='//' )]
    for i in legend_patches:
       i.set_hatch( '//' )

The code for hatching the violins themselves works fine:

parts = plt.violinplot( data, showmeans=False, showextrema=True, showmedians=True )

hatch_dict = { 0:'', 1:'///', 2:'xx' }

for t in range(0, 3):
    third = range( 0, len( labels ) )[ t*(int(len(labels)/3)):(((t+1)*int(len(labels)/3))) ]
    for i in third:
        face = parts['bodies'][i]
        face.set_hatch( hatch_dict[t] )

enter image description here

The data the bins relate to (not shown) is already color coded for other categories so I'd really like to show the bins in different hatches.

1 Answers

You are almost there - just need to be careful with the color argument with patches. There are two sub-components: an edge (edgecolor) and a face (facecolor); with this patch artist setting the color= defines both of these colors. The hatch and the background then come out the same color and you can't see one from the other.

Bottom line: use something like this for your patch constructor:

p = matplotlib.patches.Patch(facecolor='#DCDCDC', hatch=hatch_dict[i])

hatches

Full code for this plot:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches

# generate some data
n = 50
sigmas = np.array([0.1, 0.05, 0.15])
means  = np.array([0.2, 0.5, 0.75])
data = sigmas * np.random.randn(n, 3) + means

labels = [ 'Low entropy bin', 'Medium entropy bin', 'High entropy bin' ]

parts = plt.violinplot( data, showmeans=False, showextrema=True, showmedians=True)

# set up color and hatching on the violins
hatch_dict = { 0:'', 1:'///', 2:'xx' }
for i, face in enumerate(parts['bodies']):
    face.set_hatch(hatch_dict[i])
    face.set_facecolor('#DCDCDC')

# for completeness update all the lines (you already had this styling applied)
for elem in ['cbars', 'cmedians', 'cmaxes', 'cmins']:
    parts[elem].set_edgecolor('0.5')

# construct proxy artist patches
leg_artists = []
for i in range(len(hatch_dict)):
    p = matplotlib.patches.Patch(facecolor='#DCDCDC', hatch=hatch_dict[i])
    # can also explicitly declare 2nd color like this
    #p = matplotlib.patches.Patch(facecolor='#DCDCDC', hatch=hatch_dict[i], edgecolor='0.5')

    leg_artists.append(p)

# and add them to legend.
ax = plt.gca()
ax.legend(leg_artists, labels, loc='upper left')
Related