Migration and Deprecation Warning

Scipp is migrating to use Plopp instead of a built-in plotting solution. Scipp v23.03.0 (March 2023) and all later versions will use Plopp by default. The built-in plotting solution is deprecated and will be removed (at the earliest) in Scipp v23.08.0 (August 2023) without further warning. See Migrating to the new backend for details.

Plotting Overview#

Getting started#

Scipp offers a number of different ways to plot data from a Variable, DataArray, or a Dataset. It uses the matplotlib graphing library to do so, as well as the pythreejs project for 3D visualizations.

Making plots interactive

Matplotlib makes static plots by default in Jupyter notebooks. To enable interactive plots, use

%matplotlib widget

at the start of your notebook (see here for more details on Matplotlib backends).

Plotting functionality is available in two different ways:

  • using the plot() free function

  • using the .plot() method on a Scipp object (variable, data array or dataset)

The difference between the two possible plot functions is that the free function can accept more input types than just the Scipp objects. It can also plot raw NumPy arrays, as well as Python dicts of Scipp variables or data arrays. For Scipp objects, the produced plot will be the same with either approach: Internally, the .plot() method just forwards the Scipp object to the free function plot().

Note

Plot settings are cached in a scipp config.yaml file. See runtime-configuration for more information

Consider two data arrays storing 1-D data:

[1]:
import numpy as np

import scipp as sc

size = 50
rng = np.random.default_rng(seed=0)
x = sc.linspace('x', 0.0, 2.0, num=size, unit='m')
y = sc.linspace('y', 0.0, 1.0, num=5, unit='us')
temp1 = sc.array(dims=['x'], values=rng.random(size), unit='K')
temp1 += sc.linspace('x', 100, 105, num=size, unit='K')
da1 = sc.DataArray(temp1, coords={'x': x})
da1.name = 'temp1'  # Data array name is optional and will be used as a label
temp2 = sc.array(dims=['x'], values=rng.random(size), unit='K')
temp2.variances = temp2.values + 1
temp2 += sc.linspace('x', 99, 102, num=size, unit='K')
da2 = sc.DataArray(temp2, coords={'x': x})

The information in a data array or dataset is typically enough to create meaningful plots:

[2]:
da1.plot()
[2]:
../_images/visualization_plotting-overview_6_0.svg

In the plot above, the x dimension has an associated x coordinate and its values are used to label the ticks on the horizontal axis. The coordinate name and the unit (here: m) are used as a label for the horizontal axis. In a 1-D plot the unit of the data values (here K) labels the vertical axis.

Multiple data arrays can be plotted by passing a Python dict to the plot function. This example also illustrates how a data array with uncertainties results in a plot with error bars:

[3]:
sc.plot({'temp1': da1, 'temp2': da2})
[3]:
../_images/visualization_plotting-overview_8_0.svg

When the data arrays are part of a dataset, we can plot this directly:

[4]:
ds = sc.Dataset({'temp1': da1, 'temp2': da2})
ds.plot()
[4]:
../_images/visualization_plotting-overview_10_0.svg

Plotting slices or items of a dataset#

The usual indexing and slicing can be used to create plots of slices of data, or plots of individual items from a dataset.

Plot a single entry of a dataset#

[5]:
ds['temp2'].plot()
[5]:
../_images/visualization_plotting-overview_12_0.svg

Plot a slice range#

[6]:
ds['x', 10:30].plot()
[6]:
../_images/visualization_plotting-overview_14_0.svg

2-D data#

Creating a 2-D plot#

2-D data arrays can be plotted directly, just like 1-D data:

[7]:
size = 50
rng = np.random.default_rng(seed=0)
x = sc.linspace('x', 1.0, 3.0, num=size, unit='m')
time = sc.linspace('time', 1.0, 2.0, num=2 * size, unit='us')
temp = sc.array(dims=['x', 'time'], values=rng.random((size, 2 * size)), unit='K')
temp += sc.linspace('x', 100, 105, num=size, unit='K')
da = sc.DataArray(temp, coords={'x': x, 'time': time})
da.name = 'temperature'  # Data array name is optional and will be used as a label

This result in the following 2-D plot:

[8]:
da.plot()
[8]:
../_images/visualization_plotting-overview_18_0.svg

Plot a 1-D slice of 2-D data#

When slicing without a range, the dimensionality reduces. This can be used to, e.g., plot a 1-D slice through 2-D data:

[9]:
da['time', 4].plot()
[9]:
../_images/visualization_plotting-overview_20_0.svg

Transpose axes#

To control which dimensions are shown along which axes of the matplotlib figure transpose data before calling plot:

[10]:
da.transpose().plot()
[10]:
../_images/visualization_plotting-overview_22_0.svg

When the Matplotlib “widget” backend is activate it is also possible to transpose the axes via a button in the toolbar.

Logarithmic scale#

Data can be plotted on a logarithmic scale on one or both axes. For the independent axis (the coordinate axis, i.e., the horizontal axis) this can be set using the scale option:

[11]:
da1.plot(scale={'x': 'log'})
[11]:
../_images/visualization_plotting-overview_25_0.svg
[12]:
da.plot(scale={'x': 'log', 'time': 'log'})
[12]:
../_images/visualization_plotting-overview_26_0.svg

Note the the keys in the scale dict are dimension labels and not “x” and “y” as Matplotlib would refer to its axes.

For the dependent axis (the data axis, i.e., vetical axis) use the norm option:

[13]:
(100 * (da1 - da1.min())).plot(norm='log')
[13]:
../_images/visualization_plotting-overview_29_0.svg

Migrating to the new backend#

Since version 0.17, Scipp can use either the old builtin plotting backend or Plopp. Scipp v23.03.0 (March 2023) and all later versions will use Plopp by default. The old backend is deprecated and will be removed at the earliest in Scipp v23.08.0 (August 2023) without further warning. It is recommended to switch to the new backend already now to avoid breaking your code through an update later. There are 3 ways of doing so:

Set a configuration value#

Set the plot/use_plopp configuration value to set Plopp as the default backend. See Runtime Configuration.

In your config file:

plot:
  use_plopp: True

And then Scipp uses Plopp for all plotting:

sc.plot(da)  # or da.plot

Patch Scipp via Plopp#

Plopp provides patch_scipp to set Plopp as the default plotting backend in Scipp (and unpatch_scipp can be used to reset to the old backend).

import plopp as pp
pp.patch_scipp()
# Uses Plopp as backend:
sc.plot(da)  # or da.plot

Use Plopp directly#

Instead of using sc.plot(da) or da.plot(), use

import plopp as pp
pp.plot(da)

Using the old backend#

There are some changes to functionality in Plopp compared to the old backend. If the old backend is required, it is possible to use scipp.legacy_plot instead of sc.plot to use the old backend regardless of any configuration settings or patching through Plopp. But use this at your own risk as scipp.legacy_plot will be removed in the future.