Author avatar

Vivek Kumar

Creating Path Effects and Transformations

Vivek Kumar

  • Apr 10, 2019
  • 9 Min read
  • 42 Views
  • Apr 10, 2019
  • 9 Min read
  • 42 Views
Data
matplotlib

Introduction

The objective of this guide covers the creation of path effects using shadows and outlines along with the necessary transformations to handle figure and axes coordinate systems.

After completing this guide, you will be able to do the following: 1. Make your visualizations stand out using several path effects. 2. Gain more control over your visualizations by controlling data, axes, figures, and display coordinates.

The Baseline

Importing the necessary library to be used in the guide:

Syntax:

1
2
3
import matplotlib.pyplot as plt
import matplotlib.patheffects as path_effects
import matplotlib.patches as mpatches
python

Implementation of Path Effects

The path effects provide a succinct way to define paths that objects follow on a canvas. Such effects can be applied on Path artists including Patch, Line2D, Collection, and even Text.

The effects are achievable using the patheffects module from the Matplotlib package and controlled via the set_path_effects method.

Let us discuss the three most commonly used path effects:

  1. Normal
  2. Drop-shadow
  3. Outline

1. Normal Path Effect

This path effect brings no changes to the artist. In the given figure, we pass the normal path effect to the plot and, as can be observed, no new changes have been observed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Initializing x and y coordinates
x = [1, 1, 3, 5, 5]
y = [1, 5, 1, 5, 1]

# Defining the path effect
pe = [path_effects.Normal()]

# Defining the figure size
plt.figure(figsize=(8, 5))

# Plotting
plt.plot(x, y, path_effects=pe, linewidth=8, color='Orange')

# Labelling the title
plt.title('Normal Path Effect', weight='bold', fontsize=18)

# Displaying the results
plt.show()
python

Normal Path Effect

2. Drop-Shadow Path Effect

The next effect, the drop-shadow effect, applies to all artists and draws either a filled patch or a lined patch below the original artist. It uses two classes to perform the changes which are:

  • SimplePatchShadow
  • SimpleLineShadow

Let us learn to implement these classes in the following figure:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# Initializing x and y coordinates
x = [1, 1, 3, 5, 5]
y = [1, 5, 1, 5, 1]

# Defining the path effects
pe1 = [path_effects.SimplePatchShadow(), path_effects.Normal()]
pe2 = [path_effects.SimpleLineShadow(), path_effects.Normal()]

# To avoid writing path_effects.Normal() after the SimplePatchShadow() class, 
# you can also use 'with' as shown:
# pe1 = [path_effects.withSimplePatchShadow()]

# Defining the figure size
plt.figure(figsize=(8, 5))

# Using SimplePatchShadow
plt.text(2.2, 4, 'Hello, world!', path_effects=pe1, fontsize=25, color='Green')

# Using SimpleLineShadow
plt.plot(x, y, path_effects=pe2, linewidth=8, color='Orange')

# Labelling title
plt.title('Drop-Shadow Path Effect', weight='bold', fontsize=18)

# Displaying the results
plt.show()
python

Drop-Shadow

3. Outline Path Effect

This effect helps to draw an outline around the text to make it stand out. This is achieved using the Stroke class as shown:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Defining the path effect
pe = [path_effects.SimplePatchShadow(), path_effects.Normal()]

# Defining the figure size
plt.figure(figsize=(8, 5))

# Plotting text
statement = plt.text(0.2, 0.5, 'Hello, world!', fontsize=50, color='Green')

statement.set_path_effects([path_effects.Stroke(linewidth=3, foreground='Orange'), path_effects.Normal()])

# Labelling title
plt.title('Outline Path Effect', weight='bold', fontsize=18)

# Displaying the results
plt.show()
python

Outline

Handling Transformations

To take control of internal features available within Matplotlib, we can utilize various transformations, like data, axes, figure, display, etc, which helps in moving between the coordinate systems. Following is the list of coordinate systems along with their objects (ax stands for Axis and fig stands for Figure) which we are going to discuss:

Coordinate systemObject
dataax.transData
axesax.transAxes
figurefig.transFigure
figure-inchesfig.dpi_scale_trans
displayNone or IdentityTransform()
xaxis, yaxisax.get_xaxis_transform(), ax.get_yaxis_transform()

All the transformations present in the above table convert their input to the display coordinate system and that is the reason that the display coordinate system has None object because it is already in the display coordinate system.

Let us briefly take a look at data, axes, and figure-inches coordinate systems through an example.

1. Data Coordinate System

When the data is added to a plot, Matplotlib automatically updates the data limits. We can also change the data limits using set_xlim() and set_ylim() methods. Once the data limits are set then we can find the corresponding display coordinates using ax.transData object.

Let us take an example of a triangle, we set the data limits of x and y axes and look for the display coordinates on the data coordinates (3, 3).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Initializing x and y coordinates
x = [1, 3, 5, 1]
y = [1, 3, 1, 1]

# Plotting basic figure with manual x and y limits
fig, ax = plt.subplots(figsize=(8, 5))
ax.plot(x, y, c='Orange', linewidth=5)
ax.set_xlim(0, 6)
ax.set_ylim(0, 4)

# Finding display coordinates
xdata, ydata = 3, 3
xdisplay, ydisplay = ax.transData.transform_point((xdata, ydata))

# Labelling data and display coordinates
plt.text(2, 3.1, 'Data Coordinate: (3, 3)\nDisplay coordinate: ('+ str(round(xdisplay)) + ',' + str(round(ydisplay)) + ')')

# Labelling title
plt.title('Data to Display Coordinate', weight='bold', fontsize=18)

# Displaying the results
plt.show()
python

Data

As you may observe, for the given figure, the size provided is (8, 5). The display coordinate is dependent upon the size of a figure. If you change the figure size, the display coordinate changes too. To retrieve the results back from the display coordinate system to data coordinate system, we can use inverted as shown:

1
2
3
4
5
# Fetching data coordinates from the display coordinates 
ax.transData.inverted().transform((xdisplay, ydisplay))

# Output
# array([3., 3.])
python

2. Axes Coordinate System

After the data coordinate system, the next most helpful coordinate system is the axes coordinate system. With the help of this system, we get better control over placing text in the figure. The table given below lists a few such instances, considering a figure with x-limit (0, 1) and y-limit (0, 1):

CoordinateLocation
(0, 0)Bottom Left
(1, 0)Bottom Right
(1, 1)Top Right
(0, 1)Top Left
(0.5, 0.5)Center

Let us learn to label text in each of these five listed locations:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Initializing the text coordinates and the text labels
x = [0, 0.75, 0.82, 0, 0.45]
y = [0.01, 0.01, 0.95, 0.95, 0.5]
labels = ['Bottom Left', 'Bottom Right', 'Top Right', 'Top Left', 'Center']

# Initializing the figure
fig, ax = plt.subplots(figsize=(8, 5))

# Labelling the text
for i in range(5):
    ax.text(x[i], y[i], labels[i], transform=ax.transAxes, fontsize=16)

# Labelling title
plt.title('Axes Coordinate', weight='bold', fontsize=18)

# Displaying the results
plt.show()
python

Axes

3. Figure Coordinate System in Inches

While constructing a figure, most of the time, we may need a particular piece of figure intact which doesn't change in its size and shape even if the figure size is modified. Figure coordinate system provides fig.dpi_scale_trans object to provide support under such circumstances.

Let us take a circle and plot it at the center of a figure with shape (5, 5) as shown:

1
2
3
4
5
6
7
8
9
10
11
12
13
# Setting figure size to 5, 5
fig, ax = plt.subplots(figsize=(5, 5))

# Adding a circle in fixed-units
circ = mpatches.Circle((2.3, 2.3), 1.0, transform=fig.dpi_scale_trans,
                       facecolor='Orange')
ax.add_patch(circ)

# Labelling title
plt.title('Figure Coordinate', weight='bold', fontsize=18)

# Displaying the results
plt.show()
python

Figure_1

Now, let us change the figure size to (5, 2) as shown:

1
2
3
4
5
6
7
8
9
10
11
12
13
# Setting figure size to 5, 2
fig, ax = plt.subplots(figsize=(5, 2))

# Adding a circle in fixed-units
circ = mpatches.Circle((2.3, 2.3), 1.0, transform=fig.dpi_scale_trans,
                       facecolor='Orange')
ax.add_patch(circ)

# Labelling title
plt.title('Figure shape: (5, 2)', weight='bold', fontsize=18)

# Displaying the results
plt.show()
python

Figure_2

As you can observe, the circle remains intact without any change to its original shape and location.

Conclusion

In this guide, you have learned how to make your visualizations stand out using several path effects and how to get more control of your visualizations by controlling data, axes, figures, and display coordinates. To learn more on Matplotlib you can refer the following guides:

0