diff --git a/inner_workings_of_hyperspy_signals.ipynb b/inner_workings_of_hyperspy_signals.ipynb new file mode 100644 index 0000000..31c41c4 --- /dev/null +++ b/inner_workings_of_hyperspy_signals.ipynb @@ -0,0 +1,516 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Inner workings of HyperSpy signals" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This notebooks shows some of the inner working of HyperSpy, and how to interact directly with the underlying data.\n", + "\n", + "Intended for people who are not familiar with scientific python." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This notebook requires:\n", + "\n", + "HyperSpy 1.3 or later" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Author\n", + "* 2017/11/01 Magnus Nord: initial version\n", + "* 2019/6/24 Magnus Nord: minor text changes" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Importing library and loading data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib nbagg\n", + "import hyperspy.api as hs\n", + "s = hs.load(\"electron_microscopy/EELS/datasets/LSMO_STO_linescan.hdf5\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Signal object and data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Data-wise the signals can roughly be divided into three separate parts:\n", + "\n", + "- Raw data numbers, contained in `s.data` as a NumPy array\n", + "- Axes information, in `s.axes_manager`, like scaling, offset, units, ...\n", + "- Metadata, in `s.metadata` instrument information, markers, operator, ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## The data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "All the data is contained within an NumPy array, with the same size as the HyperSpy signal " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "s.data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This NumPy array can be changed directly" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "s.data = s.data/10\n", + "s.data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Interlude: scientific Python" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Scientific Python is a large group of libraries, which covers everything from large scale array operations, plotting, fitting, image analysis, ...\n", + "\n", + "The most important ones are:\n", + "\n", + "- NumPy: very fast array operations ++\n", + "- Matplotlib: plotting and visualization\n", + "- SciPy: fitting, image processing, interpolation, ...\n", + "\n", + "All these libraries easily interfaces with eachother, so matplotlib can directly plot at NumPy array, and SciPy can directly work with NumPy arrays.\n", + "\n", + "HyperSpy relies on all of these libraries (and more!), and this tight integration between them makes it very easy to apply many data processing techniques which hasn't been directly implemented in HyperSpy.\n", + "\n", + "For example:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from scipy.ndimage import gaussian_filter1d\n", + "data = s.data[5]\n", + "data_blur = gaussian_filter1d(input=data, sigma=3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "fig, ax = plt.subplots()\n", + "ax.plot(data)\n", + "ax.plot(data_blur)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.close()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Axes manager" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "All information about the different dimensions are contained in the `axes_manager` object." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "s.axes_manager" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Axes are of two different types: signal or navigation, which affects how the data is plotted and (sometimes) processed.\n", + "\n", + "Changing the axes properties does not change the raw data in `s.data`\n", + "\n", + "These values are changed by directly accessing the axis object. So to change the probe dimension axis:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "probe_axis = s.axes_manager.navigation_axes[0]\n", + "probe_axis.scale = 31.5\n", + "probe_axis.units = 'Å'\n", + "probe_axis.name = 'Probe'\n", + "probe_axis.offset = -93\n", + "s.axes_manager" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "s.plot()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The values themselves in an axis object is accessed by:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "probe_axis.axis" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that the axes must be linear, meaning that the axis values must be evenly spaced. So in the signal `s`, the probe positions cannot be `[-100, 80, 30, 0]`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "It is also possible to \"switch\" the navigation and signal axes by using transpose" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "s_transpose = s.T\n", + "s_transpose" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "So now we got the energy loss dimension as the navigation axis, which makes it easy to see which elements are located where." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "s_transpose.plot()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The ability to \"switch\" our navigation and signal axes is a very powerful functionality, since it allows for the data to be \"viewed\" in different directions. In addition, this new signal `s_transpose` does not use any addition memory, since it uses the same data as `s`, just \"viewed\" in a different direction." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Metadata dictionary" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Lastly, information about the microscope, acquisition parameters, and similar metadata is located in `s.metadata`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "s.metadata" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Some of these experimental parameters are used when modelling EELS edges. They can be changed by directly change the value. Note that all these values can be \"tab-completed\" so it is easy to navigate this metadata \"tree\"." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "s.metadata.Acquisition_instrument.TEM.Detector.EELS.collection_angle = 25" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The metadata also contains the elements added using `s.add_elements`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "s.add_elements(('Ti', 'Mn'))\n", + "s.metadata.Sample" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Slicing: inav and isig" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "HyperSpy has extensive support for slicing, which essentially means cropping the data along one or several dimensions. \n", + "\n", + "The main functions are `s.inav` and `s.isig`:\n", + "\n", + "- `inav` slices in the navigation dimension\n", + "- `isig` slices in the signal dimension\n", + "\n", + "If integers are used, it will slice in based on index. If decimal numbers are used, it will slice based on the scaling.\n", + "\n", + "To get just the Ti-L$_{3,2}$ edge (Note the use of decimal points):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "s_crop = s.isig[400.:500.]\n", + "s_crop.plot()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To grab the 20 first pixels in the EELS spectrum." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "s_crop2 = s.isig[0:20]\n", + "s_crop2.plot()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "An important thing to keep in mind is that the sliced signal `s_crop` uses the same data as `s`, so changing `s_crop` will also change `s`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "s_crop.data /= 2\n", + "s.plot()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If want want to get a new copy of the signal, use `s.deepcopy()`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "s_copy = s.deepcopy()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## HyperSpy and functions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "An important part of the HyperSpy signals are the methods in each signal. We've already seen some of them: `plot`, `create_model`, ... There are very many of these methods, and the easiest way to see them all is using tab-completion: in this case, typing the signal variable, add a `.`, and then press the __TAB__ button. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "s." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The types of functions available will depend on which type of type the signal is. For example our current signal is an EELSSpectrum, which contains methods for aligning the zero loss peak, making EELS models and other similar utility functions." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Signal types and methods" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In addition to the `EELSSpectrum` signal type we've been working with this far, there are several different signal types: `Signal1D`, `Signal2D`, `EDSTEMSpectrum`, `EDSSEMSpectrum`, `HologramImage` and more.\n", + "\n", + "It is possible to convert between the different types, so for our signal `s`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "s_2d = s.as_signal2D((0, 1))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "s_2d.plot()" + ] + } + ], + "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.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}