Customizing figures#

Deprecated

This particular guide uses the old plotting backend which is now deprecated and will be removed in the future. See Migrating to the New Backend for general migration instructions. And see the documentation of Plopp for how to customize figures with the new backend.

Sometimes, even though relatively customizeable, the plot method of Scipp is not flexible enough for one’s needs. In this section, we explore how the figures produced by the scipp.plot function can be further modified.

Modifying the returned Plot object#

There are two ways of customizing Scipp figures. The first one is to first create a default figure using the plot function, and then modifying its contents.

The plot commands returns an object which is represented in a notebook as a figure (or multiple figures) using the _ipython_display_ property. This object can subsequently be modified post-creation.

[1]:
%matplotlib widget
import matplotlib.pyplot as plt
import numpy as np
import scipp as sc
[3]:
N = 60
M = 5
d = sc.Dataset()
d['noise'] = sc.Variable(dims=['x', 'tof'], values=10.0*np.random.rand(M, N))
d.coords['tof'] = sc.Variable(dims=['tof'],
                              values=np.arange(N+1).astype(np.float64),
                              unit=sc.units.us)
d.coords['x'] = sc.Variable(dims=['x'], values=np.arange(M).astype(np.float64),
                            unit=sc.units.m)
out = sc.plot(d, projection="1d")
out
/home/runner/work/scipp/scipp/.tox/docs/lib/python3.8/site-packages/scipp/plotting/__init__.py:142: VisibleDeprecationWarning: You are using Scipp's deprecated plotting backend. This will be removed in Scipp v23.08.0 (August 2023) or after. Scipp v23.03.0 (March 2023) and all later versions will use Plopp by default. See https://scipp.github.io/visualization/plotting-overview.html#Migrating-to-the-new-backend for details and a migration guide.
  warnings.warn(
[3]:

The out object is a Plot object which is made up of several pieces: - some widgets that are used to interact with the displayed figure via buttons and sliders to control slicing of higher dimensions or flipping the axes of the plot - a view which contains a figure and is the visual interface between the user and the data - in the case of 1D and 3D plots, the Plot object also contains a panel which provides additional control widgets

Each one of these pieces can individually be displayed in the notebook. For instance, we can display the widgets of the 2D image by doing

[4]:
out.widgets
[4]:

and they are still connected to the figure above.

It is also possible to customize figures such as changing the figure title or the axes labels by accessing the underlying matplotlib axes:

[5]:
out.ax.set_title('This is a new title!')
out.ax.set_xlabel('My new Xaxis label')
out
[5]:

A line color may be modified by accessing the underlying axes and lines using the Matplotlib API (although we do recommend that changing line styles should instead be done by passing arguments to the plot command, as shown here):

[6]:
out.ax.get_lines()[0].set_color('red')
out
[6]:

Note

If the plot produces more than one figure (in the case of plotting a dataset that contains both 1d and 2d data), the out object is a dict that contains one key per figure. The keys are either a combination of dimension and unit for 1d figures, or the name of the variable (noise) in our case.

Placing figures inside existing Matplotlib axes#

Sometimes, the Scipp default graphs are not flexible enough for advanced figures. One common case is placing figures in subplots, for example. To this end, it is also possible to attach scipp plots to existing matplotlib axes.

This is achieved via the ax keyword argument (and cax for colorbar axes), and is best illustrated via a short demo.

We first create 3 subplots:

[7]:
figs, axs = plt.subplots(1, 3, figsize=(12, 3), dpi=96)

Then a Dataset with some 2D data:

[8]:
N = 100
M = 50
xx = np.arange(N, dtype=np.float64)
yy = np.arange(M, dtype=np.float64)
x, y = np.meshgrid(xx[:-1], yy)
b = N/20.0
c = M/2.0
r = np.sqrt(((x-c)/b)**2 + ((y-c)/b)**2)
a = 10.0 * np.sin(r)
d1 = sc.Dataset()
d1['Signal'] = sc.Variable(dims=['y', 'x'], values=a, unit=sc.units.counts)
d1.coords['x'] = sc.Variable(dims=['x'], values=xx, unit=sc.units.m)
d1.coords['y'] = sc.Variable(dims=['y'], values=yy, unit=sc.units.m)

Next, we attach the 2D image plot to the first subplot, and display the colorbar in the third subplot:

[9]:
out = sc.plot(d1, ax=axs[0], cax=axs[2])
/home/runner/work/scipp/scipp/.tox/docs/lib/python3.8/site-packages/scipp/plotting/__init__.py:142: VisibleDeprecationWarning: You are using Scipp's deprecated plotting backend. This will be removed in Scipp v23.08.0 (August 2023) or after. Scipp v23.03.0 (March 2023) and all later versions will use Plopp by default. See https://scipp.github.io/visualization/plotting-overview.html#Migrating-to-the-new-backend for details and a migration guide.
  warnings.warn(

This has just returned a Plot object, but then we can check that our original figure has been updated:

[10]:
figs
[10]:
../../_images/visualization_plotting_customizing-figures_18_0.png

We can add a 1D plot of a slice through the 2D data in the middle panel, and check once again the original figure:

[11]:
out1 = sc.plot(d1['Signal']['x', 1], ax=axs[1])
figs
/home/runner/work/scipp/scipp/.tox/docs/lib/python3.8/site-packages/scipp/plotting/__init__.py:142: VisibleDeprecationWarning: You are using Scipp's deprecated plotting backend. This will be removed in Scipp v23.08.0 (August 2023) or after. Scipp v23.03.0 (March 2023) and all later versions will use Plopp by default. See https://scipp.github.io/visualization/plotting-overview.html#Migrating-to-the-new-backend for details and a migration guide.
  warnings.warn(
[11]:
../../_images/visualization_plotting_customizing-figures_20_1.png

Next we create a second dataset with some more 1D data and add it to the middle panel:

[12]:
d2 = sc.Dataset()
N = 100
d2["Sample"] = sc.Variable(dims=['tof'],
                           values=10.0 * np.random.rand(N),
                           variances=np.random.rand(N),
                           unit=sc.units.counts)
d2["Background"] = sc.Variable(dims=['tof'],
                               values=2.0 * np.random.rand(N),
                               unit=sc.units.counts)
d2.coords['tof'] = sc.Variable(dims=['tof'],
                               values=np.arange(N+1).astype(np.float64),
                               unit=sc.units.us)
out2 = sc.plot(d2, ax=axs[1], color=['r', 'g'])
figs
/home/runner/work/scipp/scipp/.tox/docs/lib/python3.8/site-packages/scipp/plotting/__init__.py:142: VisibleDeprecationWarning: You are using Scipp's deprecated plotting backend. This will be removed in Scipp v23.08.0 (August 2023) or after. Scipp v23.03.0 (March 2023) and all later versions will use Plopp by default. See https://scipp.github.io/visualization/plotting-overview.html#Migrating-to-the-new-backend for details and a migration guide.
  warnings.warn(
[12]:
../../_images/visualization_plotting_customizing-figures_22_1.png

We can now for example modify the axes labels:

[13]:
axs[0].set_xlabel('This is my new label!')
figs
[13]:
../../_images/visualization_plotting_customizing-figures_24_0.png

You can then also access the individual plot objects and change their properties using the Matplotlib API. For example, if we wish to change the line color of the 'Sample' from green to purple, we can do:

[14]:
axs[1].get_lines()[2].set_color('purple')
figs
[14]:
../../_images/visualization_plotting_customizing-figures_26_0.png