9

I am adding patches in a plot inside matplotlib using from matplotlib.patches import Patch class. Please see the sample code below-

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

n = 5
hatch_1 = 'o'
hatch_2 = '.'
opacity = 0.4
bar_width = 0.4

y = np.random.randint(low=0, high=10, size=n)
x = np.arange(n)

bars = plt.bar(x, y, bar_width, align='center',  alpha=opacity, fill=False)
for bar in bars:
    bar.set_hatch(hatch_1)

y = np.random.randint(low=0, high=10, size=n)
bars = plt.bar(x + bar_width, y, bar_width,
               align='center',  alpha=opacity, fill=False)
for bar in bars:
    bar.set_hatch(hatch_2)

patch_1 = Patch(fill=False, label='Hatch 1', hatch=hatch_1, alpha=opacity)
patch_2 = Patch(fill=False, label='Hatch 2', hatch=hatch_2, alpha=opacity)

# add legends
plt.legend(handles=[patch_1, patch_2], loc='upper right')

plt.show()

Below is the generated plot- screenshot of the plot

The hatches used for legends aren't visisble properly. I guess if I make the patches bigger, it will be visible.

How to make patches bigger?

ravi
  • 6,140
  • 18
  • 77
  • 154
  • 1
    I don't know if there is an easy way to change the size of the hatch pattern (that is, without creating a new hatch from scratch). In the mean time, you could increase the hatch density in the legend: `patch_1 = Patch(fill=False, label='Hatch 1', hatch=2*hatch_1, alpha=opacity)` – Diziet Asahi Dec 19 '18 at 11:28

5 Answers5

7

You can change the size of the legend patches in a couple of ways.

First, you can increase the width using the handlelength option to plt.legend.

However, there is no way to increase their height using kwargs. So we need to loop over the patches after creating the legend. If we keep a reference to the legend as we create it leg = plt.legend(...), then we can loop over the patches using for patch in leg.get_patches():.

Then you can change the height of the patch using patch.set_height().

However, all this tinkering means they won't be aligned quite right. So we also need to change their vertical position slightly (using patch.set_y()).

I also found it helped to increase the vertical spacing of the labels in the legend to fit things in nicely (use the labelspacing kwarg).

And finally, I added a new line at the beginning of the legend labels to make it all look nice (label='\nHatch 1').

A complete script is below. You may wish to play around with the values of labelspacing, handlelength, patch.set_height() and patch.set_y() to suit your needs.

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

n = 5
hatch_1 = 'o'
hatch_2 = '.'
opacity = 0.4
bar_width = 0.4

y = np.random.randint(low=0, high=10, size=n)
x = np.arange(n)

bars = plt.bar(x, y, bar_width, align='center',  alpha=opacity, fill=False)
for bar in bars:
    bar.set_hatch(hatch_1)

y = np.random.randint(low=0, high=10, size=n)
bars = plt.bar(x + bar_width, y, bar_width,
               align='center',  alpha=opacity, fill=False)
for bar in bars:
    bar.set_hatch(hatch_2)

patch_1 = Patch(fill=False, label='\nHatch 1', hatch=hatch_1, alpha=opacity)
patch_2 = Patch(fill=False, label='\nHatch 2', hatch=hatch_2, alpha=opacity)

# add legends
leg = plt.legend(handles=[patch_1, patch_2], loc='upper right', labelspacing=1.5, handlelength=4)

for patch in leg.get_patches():
    patch.set_height(22)
    patch.set_y(-6)

plt.show()

enter image description here

tmdavison
  • 64,360
  • 12
  • 187
  • 165
  • 1
    Awesome. This is a cool workaround. It shows the power of customization which can be done in `matplotlib`. Thank you very much! – ravi Dec 21 '18 at 03:16
  • @tmdavison's workaround is really nice (and I'm using it). However, this does _not_ show the "power" of matplotlib customization but rather the lack of good defaults for hatch sizes in legends. – Pepe Mandioca Feb 02 '20 at 15:13
  • 1
    `handleheight` is now an argument to `plt.legend()` (as of 3.5 but maybe earlier versions). See also my answer below. – rileyx Apr 12 '22 at 17:58
  • Nice, good to know @rileyx – tmdavison Apr 13 '22 at 14:42
2

As of 3.5.0 (and maybe earlier), handleheight and handlelength are now options to plt.legend(). Using your code but replacing the call to plt.legend() with

plt.legend(handles=[patch_1, patch_2], loc='upper right', handleheight=3, handlelength=4)

gives the following: enter image description here

rileyx
  • 322
  • 4
  • 11
  • 1
    A quick look at the repository suggests `handleheight` has been there as an option to `plt.legend` for a long time... but was only added to the documentation in v3.5.0 ! – tmdavison Apr 13 '22 at 14:52
1
n = 5
hatch_1 = 'O'
hatch_2 = '.'
opacity = 0.4
bar_width = 0.4

y = np.random.randint(low=0, high=10, size=n)
x = np.arange(n)

bars = plt.bar(x, y, bar_width, align='center',  alpha=opacity, fill=False)
for bar in bars:
    bar.set_hatch(hatch_1)

y = np.random.randint(low=0, high=10, size=n)
bars = plt.bar(x + bar_width, y, bar_width,
               align='center',  alpha=opacity, fill=False)
for bar in bars:
    bar.set_hatch(hatch_2)

patch_1 = Patch(fill=False, label='Hatch 1', hatch=hatch_1, alpha=opacity)
patch_2 = Patch(fill=False, label='Hatch 2', hatch=hatch_2, alpha=opacity)
plt.rcParams['figure.figsize'] = (25,15)
# add legends
plt.legend(handles=[patch_1, patch_2], loc='upper right')

plt.show()

Made your small o as O and increase the size of figure that will make sense i guess

Diziet Asahi
  • 38,379
  • 7
  • 60
  • 75
raunak rathi
  • 95
  • 1
  • 9
  • The main issue here is the legend showing hatch. The size of the box is so small that it is difficult to understand in a quick look. – ravi Dec 21 '18 at 03:13
1

The easiest way offered by matplotlib in my opinion is to define the properties of the legend using prop size to increase/decrease the legend size. You just need to do the following where you can choose the 'size' as preferred.

plt.legend(handles=[patch_1, patch_2], loc='best', prop={'size': 24})

enter image description here

Sheldore
  • 37,862
  • 7
  • 57
  • 71
  • 1
    `prop` is used to modify the font properties (in this case just the font size) in the legend. This will modify the patch size too, but it comes at the cost of changing the font size to be much larger than the font for the axis labels, etc. – tmdavison Dec 19 '18 at 14:00
  • @tmdavison: True that. Good catch – Sheldore Dec 19 '18 at 14:07
  • Thank you very much. As @tmdavison mentioned, the drawback of using `prop` is that the font size is getting much larger. – ravi Dec 21 '18 at 03:15
1

While @tmdavison's solution works great, it is a bit involved.

Since the main issue here is that hatches are not easy to recognize in legends, a less ideal but much simpler workaround is to increase the hatch density, which is achieved simply by repeating the desired hatch character, ie, replacing o with ooo:

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

n = 5
hatch_1 = 'o'
hatch_2 = 'ooo'
opacity = 0.4
bar_width = 0.4

y = np.random.randint(low=0, high=10, size=n)
x = np.arange(n)

bars = plt.bar(x, y, bar_width, align='center',  alpha=opacity, fill=False)
for bar in bars:
    bar.set_hatch(hatch_1)

y = np.random.randint(low=0, high=10, size=n)
bars = plt.bar(x + bar_width, y, bar_width,
               align='center',  alpha=opacity, fill=False)
for bar in bars:
    bar.set_hatch(hatch_2)

patch_1 = Patch(fill=False, label='Hatch 1', hatch=hatch_1, alpha=opacity)
patch_2 = Patch(fill=False, label='Hatch 2', hatch=hatch_2, alpha=opacity)

# add legends
plt.legend(handles=[patch_1, patch_2], loc='upper right')

plt.show()

bar plot with different hatch densities

Pepe Mandioca
  • 334
  • 2
  • 10