Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make patches bigger used as legend inside matplotlib

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?

like image 900
Ravi Joshi Avatar asked Dec 19 '18 11:12

Ravi Joshi


4 Answers

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

like image 126
tmdavison Avatar answered Nov 15 '22 05:11

tmdavison


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

like image 23
rileyx Avatar answered Nov 15 '22 04:11

rileyx


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

like image 36
raunak rathi Avatar answered Nov 15 '22 03:11

raunak rathi


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

like image 35
Pepe Mandioca Avatar answered Nov 15 '22 04:11

Pepe Mandioca