Basics of signals#

Signal initialization#

Many of the values in the AxesManager can be set when making the BaseSignal object.

>>> dict0 = {'size': 10, 'name':'Axis0', 'units':'A', 'scale':0.2, 'offset':1}
>>> dict1 = {'size': 20, 'name':'Axis1', 'units':'B', 'scale':0.1, 'offset':2}
>>> s = hs.signals.BaseSignal(np.random.random((10,20)), axes=[dict0, dict1])
>>> s.axes_manager
<Axes manager, axes: (|20, 10)>
            Name |   size |  index |  offset |   scale |  units
================ | ====== | ====== | ======= | ======= | ======
---------------- | ------ | ------ | ------- | ------- | ------
           Axis1 |     20 |      0 |       2 |     0.1 |      B
           Axis0 |     10 |      0 |       1 |     0.2 |      A

This also applies to the metadata.

>>> metadata_dict = {'General':{'name':'A BaseSignal'}}
>>> metadata_dict['General']['title'] = 'A BaseSignal title'
>>> s = hs.signals.BaseSignal(np.arange(10), metadata=metadata_dict)
>>> s.metadata
├── General
│   ├── name = A BaseSignal
│   └── title = A BaseSignal title
└── Signal
        └── signal_type =

Instead of using a list of axes dictionaries [dict0, dict1] during signal initialization, you can also pass a list of axes objects: [axis0, axis1].

The navigation and signal dimensions#

HyperSpy can deal with data of arbitrary dimensions. Each dimension is internally classified as either “navigation” or “signal” and the way this classification is done determines the behaviour of the signal.

The concept is probably best understood with an example: let’s imagine a three dimensional dataset e.g. a numpy array with dimensions (10, 20, 30). This dataset could be an spectrum image acquired by scanning over a sample in two dimensions. As in this case the signal is one-dimensional we use a Signal1D subclass for this data e.g.:

>>> s = hs.signals.Signal1D(np.random.random((10, 20, 30)))
>>> s
<Signal1D, title: , dimensions: (20, 10|30)>

In HyperSpy’s terminology, the signal dimension of this dataset is 30 and the navigation dimensions (20, 10). Notice the separator | between the navigation and signal dimensions.

However, the same dataset could also be interpreted as an image stack instead. Actually it could has been acquired by capturing two dimensional images at different wavelengths. Then it would be natural to identify the two spatial dimensions as the signal dimensions and the wavelength dimension as the navigation dimension. To view the data in this way we could have used a Signal2D instead e.g.:

>>> im = hs.signals.Signal2D(np.random.random((10, 20, 30)))
>>> im
<Signal2D, title: , dimensions: (10|30, 20)>

Indeed, for data analysis purposes, one may like to operate with an image stack as if it was a set of spectra or viceversa. One can easily switch between these two alternative ways of classifying the dimensions of a three-dimensional dataset by transforming between BaseSignal subclasses.

The same dataset could be seen as a three-dimensional signal:

>>> td = hs.signals.BaseSignal(np.random.random((10, 20, 30)))
>>> td
<BaseSignal, title: , dimensions: (|30, 20, 10)>

Notice that with use BaseSignal because there is no specialised subclass for three-dimensional data. Also note that by default BaseSignal interprets all dimensions as signal dimensions. We could also configure it to operate on the dataset as a three-dimensional array of scalars by changing the default view of BaseSignal by taking the transpose of it:

>>> scalar = td.T
>>> scalar
<BaseSignal, title: , dimensions: (30, 20, 10|)>

For more examples of manipulating signal axes in the “signal-navigation” space can be found in Transposing (changing signal spaces).


Although each dimension can be arbitrarily classified as “navigation dimension” or “signal dimension”, for most common tasks there is no need to modify HyperSpy’s default choice.

Signal subclasses#

The signals module, which contains all available signal subclasses, is imported in the user namespace when loading HyperSpy. In the following example we create a Signal2D instance from a 2D numpy array:

>>> im = hs.signals.Signal2D(np.random.random((64,64)))
>>> im
<Signal2D, title: , dimensions: (|64, 64)>

The table below summarises all the BaseSignal subclasses currently distributed with HyperSpy. From HyperSpy 2.0, all domain specific signal subclasses, characterized by the signal_type metadata attribute, are provided by dedicated extension packages.

The generic subclasses provided by HyperSpy are characterized by the the data dtype and the signal dimension. In particular, there are specialised signal subclasses to handle complex data. See the table and diagram below. Where appropriate, functionalities are restricted to certain BaseSignal subclasses.


Diagram showing the inheritance structure of the different subclasses. The upper part contains the generic classes shipped with HyperSpy. The lower part contains examples of domain specific subclasses provided by some of the HyperSpy extensions.#

BaseSignal subclass characteristics.#

BaseSignal subclass




















Changed in version 1.0: The subclasses Simulation, SpectrumSimulation and ImageSimulation were removed.

Added in version 1.5: External packages can register extra BaseSignal subclasses.

Changed in version 2.0: The subclasses EELS, EDS_SEM, EDS_TEM and DielectricFunction have been moved to the extension package EleXSpy and the subclass hologram has been moved to the extension package HoloSpy.

HyperSpy extensions#

Domain specific functionalities for specific types of data are provided through a number of dedicated python packages that qualify as HyperSpy extensions. These packages provide subclasses of the generic signal classes listed above, depending on the dimensionality and type of the data. Some examples are included in the diagram above. If an extension package is installed on your system, the provided signal subclasses are registered with HyperSpy and these classes are directly available when loading the hyperspy.api into the namespace. A list of packages that extend HyperSpy is curated in a dedicated repository.

The metadata attribute signal_type describes the nature of the signal. It can be any string, normally the acronym associated with a particular signal. To print all BaseSignal subclasses available in your system call the function print_known_signal_types() as in the following example:

>>> hs.print_known_signal_types() 
|    signal_type     |       aliases       |     class name     | package  |
| DielectricFunction | dielectric function | DielectricFunction |  exspy   |
|      EDS_SEM       |                     |   EDSSEMSpectrum   |  exspy   |
|      EDS_TEM       |                     |   EDSTEMSpectrum   |  exspy   |
|        EELS        |       TEM EELS      |    EELSSpectrum    |  exspy   |
|      hologram      |                     |   HologramImage    | holospy  |

When loading data, the signal_type will be set automatically by the file reader, as defined in rosettasciio. If the extension providing the corresponding signal subclass is installed, load() will return the subclass from the hyperspy extension, otherwise a warning will be raised to explain that no registered signal class can be assigned to the given signal_type.

Since the load() can return domain specific signal objects (e.g. EDSSEMSpectrum from EleXSpy) provided by extensions, the corresponding functionalities (so-called method of object in object-oriented programming, e.g. EDSSEMSpectrum.get_lines_intensity()) implemented in signal classes of the extension can be accessed directly. To use additional functionalities implemented in extensions, but not as method of the signal class, the extensions need to be imported explicitly (e.g. import elexspy). Check the user guides of the respective HyperSpy extensions for details on the provided methods and functions.

For details on how to write and register extensions see Writing packages that extend HyperSpy.

Transforming between signal subclasses#

The BaseSignal method set_signal_type() changes the signal_type in place, which may result in a BaseSignal subclass transformation.

The following example shows how to change the signal dimensionality and how to transform between different subclasses:

>>> s = hs.signals.Signal1D(np.random.random((10,20,100)))
>>> s
<Signal1D, title: , dimensions: (20, 10|100)>
>>> s.metadata
├── General
│   └── title =
└── Signal
    └── signal_type =
>>> im = s.to_signal2D()
>>> im
<Signal2D, title: , dimensions: (100|20, 10)>
>>> im.metadata
├── General
│   └── title =
└── Signal
    └── signal_type =
>>> s.set_signal_type("EELS")
>>> s
<EELSSpectrum, title: , dimensions: (20, 10|100)>
>>> s.metadata
├── General
│   └── title =
└── Signal
    └── signal_type = EELS
>>> s.change_dtype("complex")
>>> s
<ComplexSignal1D, title: , dimensions: (20, 10|100)>