{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Customizing figures\n", "\n", "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.\n", "\n", "## Modifying the returned Plot object\n", "\n", "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.\n", "\n", "The `plot` commands returns an object which is represented in a notebook as a figure (or multiple figures) using the `_ipython_display_` property.\n", "This object can subsequently be modified post-creation." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "import numpy as np\n", "import scipp as sc" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "N = 60\n", "M = 5\n", "d = sc.Dataset()\n", "d['noise'] = sc.Variable(dims=['x', 'tof'], values=10.0*np.random.rand(M, N))\n", "d.coords['tof'] = sc.Variable(dims=['tof'],\n", " values=np.arange(N+1).astype(np.float64),\n", " unit=sc.units.us)\n", "d.coords['x'] = sc.Variable(dims=['x'], values=np.arange(M).astype(np.float64),\n", " unit=sc.units.m)\n", "out = sc.plot(d, projection=\"1d\")\n", "out" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `out` object is a `Plot` object which is made up of several pieces:\n", "- 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\n", "- a `view` which contains a `figure` and is the visual interface between the user and the data\n", "- in the case of 1D and 3D plots, the `Plot` object also contains a `panel` which provides additional control widgets\n", "\n", "Each one of these pieces can individually be displayed in the notebook.\n", "For instance, we can display the `widgets` of the 2D image by doing" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "out.widgets" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and they are still connected to the figure above.\n", "\n", "It is also possible to customize figures such as changing the figure title or the axes labels by accessing the underlying matplotlib axes:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "out.ax.set_title('This is a new title!')\n", "out.ax.set_xlabel('My new Xaxis label')\n", "out" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A line color may be modified by accessing the underlying axes and lines using the Matplotlib API\n", "(although we do recommend that changing line styles should instead be done by passing arguments to the `plot` command,\n", "as shown [here](plotting-1d-data.ipynb#Customizing-linestyles,-markers-and-colors)):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "out.ax.get_lines()[0].set_color('red')\n", "out" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "**Note**\n", "\n", "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.\n", "The keys are either a combination of dimension and unit for 1d figures, or the name of the variable (`noise`) in our case.\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Placing figures inside existing Matplotlib axes\n", "\n", "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.\n", "\n", "This is achieved via the `ax` keyword argument (and `cax` for colorbar axes), and is best illustrated via a short demo.\n", "\n", "We first create 3 subplots:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "figs, axs = plt.subplots(1, 3, figsize=(12, 3))\n", "figs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Then a `Dataset` with some 2D data:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "N = 100\n", "M = 50\n", "xx = np.arange(N, dtype=np.float64)\n", "yy = np.arange(M, dtype=np.float64)\n", "x, y = np.meshgrid(xx[:-1], yy)\n", "b = N/20.0\n", "c = M/2.0\n", "r = np.sqrt(((x-c)/b)**2 + ((y-c)/b)**2)\n", "a = 10.0 * np.sin(r)\n", "d1 = sc.Dataset()\n", "d1['Signal'] = sc.Variable(dims=['y', 'x'], values=a, unit=sc.units.counts)\n", "d1.coords['x'] = sc.Variable(dims=['x'], values=xx, unit=sc.units.m)\n", "d1.coords['y'] = sc.Variable(dims=['y'], values=yy, unit=sc.units.m)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, we attach the 2D image plot to the first subplot, and display the colorbar in the third subplot:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "out = sc.plot(d1, ax=axs[0], cax=axs[2])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This has just returned a `Plot` object, but then we can check that our original figure has been updated:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "figs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can add a 1D plot of a slice through the 2D data in the middle panel, and check once again the original figure:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "out1 = sc.plot(d1['Signal']['x', 1], ax=axs[1])\n", "figs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next we create a second dataset with some more 1D data and add it to the middle panel:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "d2 = sc.Dataset()\n", "N = 100\n", "d2[\"Sample\"] = sc.Variable(dims=['tof'],\n", " values=10.0 * np.random.rand(N),\n", " variances=np.random.rand(N),\n", " unit=sc.units.counts)\n", "d2[\"Background\"] = sc.Variable(dims=['tof'],\n", " values=2.0 * np.random.rand(N),\n", " unit=sc.units.counts)\n", "d2.coords['tof'] = sc.Variable(dims=['tof'],\n", " values=np.arange(N+1).astype(np.float64),\n", " unit=sc.units.us)\n", "out2 = sc.plot(d2, ax=axs[1], color=['r', 'g'])\n", "figs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can now for example modify the axes labels:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "axs[0].set_xlabel('This is my new label!')\n", "figs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can then also access the individual plot objects and change their properties using the Matplotlib API.\n", "For example, if we wish to change the line color of the `'Sample'` from green to purple, we can do:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "axs[1].get_lines()[2].set_color('purple')\n", "figs" ] } ], "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 }