Plotting N-D data#

Data with any number of dimensions can also be plotted in Scipp.

import numpy as np
import scipp as sc

Default representation#

Data with 3 or more dimensions are by default represented by a 2-D image, accompanied by sliders to navigate the extra dimensions (one slider per dimension above 2).

N = 20
M = 30
L = 20
K = 10
xx = np.arange(N, dtype=np.float64)
yy = np.arange(M, dtype=np.float64)
zz = np.arange(L, dtype=np.float64)
qq = np.arange(K, dtype=np.float64)
x, y, z, q = np.meshgrid(xx, yy, zz, qq, indexing='ij')
b = N/20.0
c = M/2.0
d = L/2.0
r = np.sqrt(((x-c)/b)**2 + ((y-c)/b)**2 + ((z-d)/b)**2  + ((q-d)/b)**2)
a = np.sin(r)
d = sc.Dataset()
d['Some4Ddata'] = sc.Variable(dims=['x', 'y', 'z', 'Q_x'], values=a)
d.coords['x'] = sc.Variable(dims=['x'], values=xx)
d.coords['y'] = sc.Variable(dims=['y'], values=yy)
d.coords['z'] = sc.Variable(dims=['z'], values=zz)
d.coords['Q_x'] = sc.Variable(dims=['Q_x'], values=qq)

Slider controls#

  • Each dimension comes with two sliders to control the position of the slice and its thickness.

  • Upon figure creation, the thickness is set to the first bin width. Only the data contained in that bin is displayed. The thickness can be increased by an integer number of bins, and the data inside those bins will either be summed or averaged (see note below).

  • Changing the slice thickness will also change the color range, and the Rescale button can be used to automatically rescale the colorbar to the limits of the currently displayed data.

  • Each dimension control comes with a Continuous Update checkbox, which is applied by default. If this is unselected, the plot will only update once the slider has been released.


You can automatically recalculate the intensity range using the Rescale button on the left of the plot. When zooming or changing thickness, the enclosed viewed region is used to calculate the new intensities. In the general case the intensities are calculated as the mean of the values within.

In the special case of your variable having units of counts, the intensities are summed.

Changing axes dimensions#

By default, the two innermost dimensions are used for the image, and the rest will be allocated to a slider. This can be changed, either interactively using the buttons, or by changing the dimension order using transpose:

d['Some4Ddata'].transpose(dims=['z', 'y', 'Q_x', 'x']).plot()

Profile picking#

Finally, each dimension also comes with a Profile button which allows to display one of the additional dimensions as a profile underneath the main plot.

  • When hovering the mouse over the top image, the profile below is updated according to the mouse position.

  • Clicking on the image will save the current profile with a random color.

  • Clicking on an existing marker on the image will delete the corresponding saved profile.

3-D scatter plots#


3-D visualization requires pythreejs to be installed. Use either pip or conda:

- conda install -c conda-forge pythreejs
- pip install pythreejs

3-D scatter plots can be created using plot(projection='3d', positions='xyz'), where the mandatory positions keyword argument is used to set the name of the position coord (here 'xyz') to use as position vectors:

N = 1000
M = 100
theta = np.random.random(N) * np.pi
phi = np.random.random(N) * 2.0 * np.pi
r = 10.0 + (np.random.random(N) - 0.5)
x = r * np.sin(theta) * np.sin(phi)
y = r * np.sin(theta) * np.cos(phi)
z = r * np.cos(theta)

a = np.arange(M*N).reshape([M, N]) * np.sin(y)
da = sc.DataArray(
    data=sc.array(dims=['time', 'xyz'], values=a),
        'xyz':sc.vectors(dims=['xyz'], unit='m', values=np.array([x, y, z]).T),
        'time':sc.array(dims=['time'], unit='s', values=np.arange(M).astype(float))})
da.plot(projection='3d', positions='xyz')

Cut surfaces to slice data in 3-D can be enabled using buttons below the scene. When using a cut surface, the upper value of the opacity slider controls the opacity of the slice, while the lower value of the slider controls the opacity of the background.

The scatter-plot functionality can also be used to create 3-D plots of dense data with slicing functionality. In this case we must first create a coordinate with positions:

d2 = d['y', :10].copy()
d2.coords['dummy-pos'] = sc.geometry.position(*[d2.coords[dim] for dim in ['x', 'y', 'z']])
sc.plot(d2, projection='3d', positions='dummy-pos')

In the above example creating the coordinate was simple, since all three coords (x, y, and z) had the same unit. In general you may need to: - Set a consistent fake unit before using before calling sc.geometry.position. - Convert bin-edge coordinates to normal coordinates.

It may be simpler to use dummy ranges as coordinates in that case:

ranges = [sc.arange(dim=dim, start=0.0, stop=d2.sizes[dim]) for dim in ['x', 'y', 'z']]
d2.coords['dummy-pos'] = sc.geometry.position(*ranges)
sc.plot(d2, projection='3d', positions='dummy-pos')

LAMP’s Superplot#

A 1d projection is also available for multi-dimensional data, with the possibility to keep/remove lines that are plotted, a behavior we copied from LAMP’s Superplot which was very popular in the neutron physics community.

sc.plot(d, projection='1d')