{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Plotting 1-D data\n", "\n", "`scipp` offers a number of different ways to plot data from a `DataArray` or a `Dataset`. It uses the `matplotlib` graphing library to do so." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import scipp as sc" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Basic plot\n", "\n", "Plotting is done using the `plot` function.\n", "Generally the information in a dataset is sufficient to produce a useful plot out of the box.\n", "\n", "For example, a simple plot from a 1D dataset is produced as follows:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "d = sc.Dataset()\n", "N = 50\n", "d['Signal'] = sc.Variable(dims=['tof'], values=5.0 + 5.0*np.sin(np.arange(N, dtype=float)/5.0),\n", " unit=sc.units.counts)\n", "d.coords['tof'] = sc.Variable(dims=['tof'], values=np.arange(N).astype(float),\n", " unit=sc.units.us)\n", "sc.plot(d)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## With error bars\n", "\n", "Error bars are shown automatically if variances are present in the data:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "d['Signal'].variances = np.square(np.random.random(N))\n", "sc.plot(d)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that the length of the errors bars is the standard-deviation, i.e., the square root of the variances stored in the data.\n", "\n", "## Multiple variables on the same axes\n", "\n", "### Plotting a Dataset with multiple entries\n", "\n", "If a dataset contains more than one 1D variable with the same coordinates, they are plotted on the same axes:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "d['Background'] = sc.Variable(dims=['tof'], values=3.0*np.random.random(N),\n", " unit=sc.units.counts)\n", "sc.plot(d)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is possible to hide the error bars with" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sc.plot(d, errorbars=False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can always plot just a single item of the dataset." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sc.plot(d['Background'])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Overplotting using a dict of DataArrays\n", "\n", "One can also supply the `plot` function with a `dict` of data arrays.\n", "Compatible data arrays will be overplotted on the same axes:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sc.plot({\"My sample\": d['Signal'], \"My background\": d[\"Background\"]})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that the newly supplied names (keys) have also been adopted as the graph names.\n", "\n", "We can also overplot sections of interest with the help of slicing." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sc.plot({\"My sample\": d['Signal']['tof', 10:40], \"My background\": d[\"Background\"]})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This overplotting is useful for plotting slices of a 2D data array:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "M = 100\n", "L = 5\n", "xx = np.arange(M, dtype=float)\n", "yy = np.arange(L, dtype=float)\n", "x, y = np.meshgrid(xx, yy)\n", "b = M/20.0\n", "c = M/5.0\n", "e = L/10.0\n", "r = np.sqrt(((x-c)/b)**2 + (y/e)**2)\n", "a = np.sin(r)\n", "d2d = sc.DataArray(data=sc.Variable(dims=['y', 'x'], values=a, unit=sc.units.counts))\n", "d2d.coords['x'] = sc.Variable(dims=['x'], values=xx, unit=sc.units.m)\n", "d2d.coords['y'] = sc.Variable(dims=['y'], values=yy, unit=sc.units.m)\n", "sc.plot({f'slice-{i}': d2d['y', i] for i in range(L)})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or using the `collapse` helper function, which returns a `dict` of data arrays:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sc.plot(sc.collapse(d2d, keep='x'))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Customizing linestyles, markers and colors\n", "\n", "Linestyles can be customized following the Matplotlib syntax.\n", "For instance, it is possible to connect the dots by setting `linestyle='solid'`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sc.plot(d, linestyle='solid')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Marker colors and symbols can be changed via the `color` and `marker` keyword arguments:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sc.plot(d, color=['red', '#30D5F9'], marker=['s', 'x'])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The supplied `color` and `marker` arguments can also be a list of integers, which correspond to one of the pre-defined [colors](https://matplotlib.org/3.1.1/users/dflt_style_changes.html) or [markers](https://matplotlib.org/3.1.1/api/markers_api.html) (which were taken from matplotlib). In addition, the grid can also be displayed:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sc.plot(d, color=[6, 8], grid=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Logarithmic scales\n", "\n", "Use the keyword arguments `scale` and `norm` to apply logarithmic scales to the horizontal and vertical axes, respectively." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sc.plot(d, norm='log', scale={'tof': 'log'})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Histograms\n", "Histograms are automatically generated if the coordinate is bin edges:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "histogram = sc.DataArray(data=sc.Variable(dims=['tof'],\n", " values=20.0 + 20.0*np.cos(np.arange(N-1, dtype=float) / 3.0),\n", " unit=sc.units.counts),\n", " coords={'tof':d.coords['tof']})\n", "sc.plot(histogram)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and with error bars" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "histogram.variances = 5.0*np.random.random(N-1)\n", "sc.plot(histogram)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The histogram color can be customized:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sc.plot(histogram, color='#000000')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Multiple 1D variables with different dimensions\n", "\n", "`scipp.plot` also supports multiple 1-D variables with different dimensions (note that the data entries are grouped onto the same graph if they have the same dimension and unit):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "M = 60\n", "d['OtherSample'] = sc.Variable(dims=['x'], values=10.0*np.random.rand(M),\n", " unit=sc.units.s)\n", "d['OtherNoise'] = sc.Variable(dims=['x'], values=7.0*np.random.rand(M),\n", " variances=3.0*np.random.rand(M),\n", " unit=sc.units.s)\n", "d['SomeKgs'] = sc.Variable(dims=['x'], values=20.0*np.random.rand(M),\n", " unit=sc.units.kg)\n", "d.coords['x'] = sc.Variable(dims=['x'],\n", " values=np.arange(M).astype(float),\n", " unit=sc.units.m)\n", "sc.plot(d)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Custom labels along x axis\n", "\n", "Sometimes one wishes to have labels along the `x` axis instead of the dimension-coordinate.\n", "This can be achieved via the `labels` keyword argument by specifying which dimension should run along the `x` axis:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "d1 = sc.Dataset()\n", "N = 100\n", "x = np.arange(N, dtype=float)\n", "d1['Sample'] = sc.Variable(dims=['tof'],\n", " values=np.sqrt(x),\n", " unit=sc.units.counts)\n", "d1.coords['tof'] = sc.Variable(dims=['tof'],\n", " values=x,\n", " unit=sc.units.us)\n", "d1.coords['somelabels'] = sc.Variable(dims=['tof'],\n", " values=np.linspace(101., 105., N),\n", " unit=sc.units.s)\n", "sc.plot(d1, labels={'tof': 'somelabels'})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Plotting masks\n", "\n", "If a dataset item contains masks, the symbols of masks data points will have a thick black contour in a 1D plot:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "d4 = sc.Dataset()\n", "N = 50\n", "x = np.arange(N).astype(float)\n", "d4['Sample'] = sc.Variable(dims=['tof'], values=3*np.sin(x/5)+3,\n", " unit=sc.units.counts)\n", "d4['Background'] = sc.Variable(dims=['tof'], values=1.0*np.random.rand(N),\n", " unit=sc.units.counts)\n", "d4.coords['tof'] = sc.Variable(dims=['tof'], values=x,\n", " unit=sc.units.us)\n", "d4['Sample'].masks['mask1'] = sc.Variable(dims=['tof'],\n", " values=np.where(np.abs(x-40) < 10, True, False))\n", "d4['Background'].masks['mask1'] = ~d4['Sample'].masks['mask1']\n", "d4['Background'].masks['mask1']['tof', 0:20].values = np.zeros(20, dtype=bool)\n", "sc.plot(d4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Checkboxes below the plot can be used to hide/show the individual masks.\n", "A global toggle button is also available to hide/show all masks in one go.\n", "\n", "The color of the masks can be changed as follows:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sc.plot(d4, masks={'color': 'red'})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Masks on histograms\n", "\n", "Masks on a histogram show up as a thick black line:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "N = 50\n", "x = np.arange(N+1).astype(float)\n", "d4 = sc.DataArray(data=sc.Variable(dims=['tof'], values=3*np.sin(x[:-1]/5)+3, unit=sc.units.counts))\n", "d4.coords['tof'] = sc.Variable(dims=['tof'], values=x, unit=sc.units.us)\n", "d4.masks['mask1'] = sc.Variable(dims=['tof'], values=np.where(np.abs(x[:-1]-40) < 10, True, False))\n", "sc.plot(d4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Plotting time series\n", "\n", "When plotting data with time-series (`dtype=datetime64`) coordinates,\n", "a special axis tick label formatting, which dynamically adapts to the zoom level, is used." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "time = sc.array(dims=['time'], values=np.arange(np.datetime64('2017-01-01T12:00:00'),\n", " np.datetime64('2017-01-01T13:00:00'), 20))\n", "N = time.sizes['time']\n", "data = sc.DataArray(data=sc.array(dims=['time'],\n", " values=np.arange(N) + 50.*np.random.random(N),\n", " unit=\"K\"),\n", " coords={'time':time})\n", "data.plot(title=\"Temperature as a function of time\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Saving figures\n", "Static `pdf` or `png` copies of the figures can be saved to file (note that any buttons displayed under a figure are not saved to file). This is achieved as follows:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sc.plot(d4, filename='my_1d_figure.pdf')" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.10" } }, "nbformat": 4, "nbformat_minor": 4 }