Matplotlib Custom Hatch Patterns in 5 Steps

Matplotlib hatch is a pattern that can be used as an overlay in bar charts, shapes and are important to ensure the readability of graphs in the absence of color (when printed in a single color format). Unfortunately, there are only a few patterns available on Matplotlib.

By reading this post, you will be able to create a custom square hatch pattern that can be used in any of your Matplotlib graphs. You can extend this to create whichever shapes you fancy.

hatch-bar-chart-example

1. Create Matplotlib Hatch Pattern

Shapes provide the base class used to create different Matplotlib patterns. We will extend the class to add a square shape. We will create a Rectangle shape with starting at (-0.25, 0.25) and with the height and width equal to 0.5. By using hatch.count we can increase the density by repeating the usage.

from matplotlib.hatch import Shapes
from matplotlib.patches import Rectangle


class SquareHatch(Shapes):
    """
    Square hatch defined by a path drawn inside [-0.5, 0.5] square.
    Identifier 's'.
    """
    def __init__(self, hatch, density):
        self.filled = False
        self.size = 1
        self.path = Rectangle((-0.25, 0.25), 0.5, 0.5).get_path()
        self.num_rows = (hatch.count('s')) * density
        self.shape_vertices = self.path.vertices
        self.shape_codes = self.path.codes
        Shapes.__init__(self, hatch, density)

2. Add Custom Class to Hatch Types

You will need to import _hatch_types in order to append to the list ensuring that we can access our bar patterns within any plot method supported my Matplotlib.

from matplotlib.hatch import _hatch_types

# attach our new hatch
_hatch_types.append(SquareHatch)

3. Create a Minimal Example

import matplotlib.pyplot as plt
import numpy as np


def main():
    # attach our new hatch
    _hatch_types.append(SquareHatch)
    # plot random bars
    np.random.seed(101)
    num = 5
    y_values = np.random.rand(num)
    x_values = np.arange(num)
    fig = plt.figure(figsize=(6, 4))
    ax = fig.add_subplot(111)
    color_blue = np.asarray([0, 107, 164]) / 255
    width = 0.5
    ax.bar(x_values, y_values,
           color='w', edgecolor=color_blue, hatch='s', width=width)
    plt.show()


if __name__ == '__main__':
    main()

With this you will get the following graph.

minimal-example

4. Use Density with Grouped Bars

If you ever find that you need to plot grouped bar charts, you can repeat the hatch to increase the hatch density as follows.

import matplotlib.pyplot as plt
import numpy as np


def main():
    # attach our new hatch
    _hatch_types.append(SquareHatch)

    # plot random bars
    np.random.seed(101)
    num = 10
    y_values = np.random.rand(num)
    x_values = np.arange(num)
    fig = plt.figure(figsize=(6, 4))
    ax = fig.add_subplot(111)
    color_blue = np.asarray([0, 107, 164]) / 255
    width = 0.5

    # group bars
    ax.bar(x_values[::2] - width / 2, y_values[::2], color='w', edgecolor=color_blue, hatch='s', width=width)
    ax.bar(x_values[::2] + width / 2, y_values[1::2], color='w', edgecolor=color_blue, hatch='ss', width=width)
    
    # # set labels and ticks
    ax.set_title("Example Bar Chart")
    ax.set_xlabel("x")
    ax.set_ylabel("y")
    plt.show()
grouped-bar-chart-example

5. Reduce Data – Ink Ratio in Matplotlib Hatch Pattern

As any visualization, it’s important to remove unnecessary ink in your plot. Matplotlib is notorious to add unwanted lines, boxes and spines. Therefore, you might want to consider adding the following snippet will clean up your graph.

# clear spines and set color
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.spines['left'].set_bounds(y_ticks[0], y_ticks[-1])
ax.spines['bottom'].set_bounds(x_values[0], x_values[-2])
ax.spines['left'].set_color('darkorange')
ax.spines['bottom'].set_color('darkorange')

Here’s the full example.

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.hatch import Shapes, _hatch_types
from matplotlib.patches import Rectangle


class SquareHatch(Shapes):
    """
    Square hatch defined by a path drawn inside [-0.5, 0.5] square.
    Identifier 's'.
    """
    def __init__(self, hatch, density):
        self.filled = False
        self.size = 1
        self.path = Rectangle((-0.25, 0.25), 0.5, 0.5).get_path()
        self.num_rows = (hatch.count('s')) * density
        self.shape_vertices = self.path.vertices
        self.shape_codes = self.path.codes
        Shapes.__init__(self, hatch, density)


def main():
    # attach our new hatch
    _hatch_types.append(SquareHatch)

    # plot random bars
    np.random.seed(101)
    num = 10
    y_values = np.random.rand(num)
    x_values = np.arange(num)
    fig = plt.figure(figsize=(6, 4))
    ax = fig.add_subplot(111)
    color_blue = np.asarray([0, 107, 164]) / 255
    width = 0.5

    # group bars
    ax.bar(x_values[::2] - width / 2, y_values[::2],
           color='w', edgecolor=color_blue, hatch='s', width=width)
    ax.bar(x_values[::2] + width / 2, y_values[1::2],
           color='w', edgecolor=color_blue, hatch='ss', width=width)

    # # set labels and ticks
    ax.set_title("Example Bar Chart")
    ax.set_xlabel("x")
    ax.set_ylabel("y")
    y_ticks = np.linspace(0, np.round(max(y_values), 0), 5)
    ax.set_yticks(y_ticks)
    ax.set_xticks(x_values[::2])
    ax.set_xticklabels(['a', 'b', 'c', 'd', 'e'])

    # clear spines and set color
    ax.spines['right'].set_visible(False)
    ax.spines['top'].set_visible(False)
    ax.spines['left'].set_bounds(y_ticks[0], y_ticks[-1])
    ax.spines['bottom'].set_bounds(x_values[0], x_values[-2])
    ax.spines['left'].set_color('darkorange')
    ax.spines['bottom'].set_color('darkorange')

    plt.savefig('hatch-graph-example-minimal-2.png')
    plt.show()


if __name__ == '__main__':
    main()

That’s it. Don’t forget to like / subscribe if you enjoyed the post.[wpdiscuz-feedback id=”rjqipqpjq9″ question=”Please leave a feedback on this” opened=”0″][/wpdiscuz-feedback]

Leave a Reply

Your email address will not be published. Required fields are marked *