To be able to distinguish one particular plot line in the figure or just to fit in the overall style of the output our figure is in, we sometimes need to add a shadow effect to the chart line (or histogram, for that matter). In this recipe, you will be learning how to add a shadow effect to the plot's chart lines.
To add shadows to the lines or rectangles in our charts, we need to use the transformation framework built in matplotlib and located in matplotlib.transforms
.
To understand how it all works, we need to explain what transformations are available in matplotlib and how they work.
Transformations know how to convert the given coordinates from their coordinate system into display. They also know how to convert them from display coordinates into their own coordinate system.
The following table summarizes the existing coordinate systems and what they represent:
Note how the display does not have a value in the column. This is because the default coordinate system is Display
, so coordinates are always in pixels relative to your display coordinate systems. This is not very useful, and most often we want them normalized into Figure
or Axes
or a Data
coordinate system.
This framework enables us to transform the current object into an offset object, that is, to place that object shifted a certain distance from the original object.
We will use this framework to create our desired effect on the plotted sine wave.
Here is the code recipe to add shadowing to the plotted chart. The code is explained in the section that follows.
import numpy as np import matplotlib.pyplot as plt import matplotlib.transforms as transforms def setup(layout=None): assert layout is not None fig = plt.figure() ax = fig.add_subplot(layout) return fig, ax def get_signal(): t = np.arange(0., 2.5, 0.01) s = np.sin(5 * np.pi * t) return t, s def plot_signal(t, s): line, = axes.plot(t, s, linewidth=5, color='magenta') return line def make_shadow(fig, axes, line, t, s): delta = 2 / 72. # how many points to move the shadow offset = transforms.ScaledTranslation(delta, -delta, fig.dpi_scale_trans) offset_transform = axes.transData + offset # We plot the same data, but now using offset transform # zorder -- to render it below the line axes.plot(t, s, linewidth=5, color='gray', transform=offset_transform, zorder=0.5 * line.get_zorder()) if __name__ == "__main__": fig, axes = setup(111) t, s = get_signal() line = plot_signal(t, s) make_shadow(fig, axes, line, t, s) axes.set_title('Shadow effect using an offset transform') plt.show()
We start reading the code from the bottom, after the if __name__
check. First, we create the figure and axes in setup()
; after that, we obtain a signal (or generate data—sine wave). We plot the basic signal in plot_signal()
. Then, we make the shadow transformation and plot the shadow in make_shadow()
.
We use the offset effect to create an offset object underneath and just a few points away from the original object.
The original object is a simple sine wave that we plot using the standard function plot()
.
To add to this offset transformation, matplotlib contains helper transformation—matplotlib.transforms.ScaledTranslation
.
The values for dx
and dy
are defined in points, and as the point is 1/72 inches, we move the offset object 2 pt right and 2pt down.
If you want to learn more about how we converted the point to 1/72 inches, read more in this Wikipedia article: http://en.wikipedia.org/wiki/Point_%28typography%29.
We can use matplotlib.transforms.ScaledTransformation(xtr, ytr, scaletr)
; here, xtr
and ytr
are translation offsets and scaletr
is a transformation callable to scale xtr
and ytr
at transformation time and before display. The most common use case for this is transforming from points to display space—for example—to DPI so that the offset always stays at the same place no matter what the actual output—be it the monitor or printed material. The callable we use for this is already built in, and is available at Figure.dpi_scale_trans
.
We then plot the same data with the applied transformation.
Using transforms to add shadows is just one and not the most popular use case of this framework. To be able to do more with the transformation framework, you will need to learn the details of how the transformation pipeline works and what the extension points are (what classes to inherit and how). This easy enough because matplotlib is open source, and even if some code is not well documented, there is a source you can read from and use or change, thus contributing to the overall quality and usefulness of matplotlib.