Ejemplo n.º 1
0
def test_rounding_consistency_axis_type_half(shift):

    axis = UniformDataAxis(size=20, scale=0.1, offset=-1.0);
    test_vals = axis.axis[:-1] + shift

    uaxis_indices = axis.value2index(test_vals)
    nuaxis_indices = super(type(axis), axis).value2index(test_vals)

    np.testing.assert_allclose(uaxis_indices, nuaxis_indices)
Ejemplo n.º 2
0
def test_to_eV(jacobian):
    axis = DataAxis(size=20, offset=200, scale=10)
    data = ones(20)
    S1 = LumiSpectrum(data, axes=(axis.get_axis_dictionary(), ))

    if not "axis" in getfullargspec(DataAxis)[0]:
        raises(ImportError, S1.to_eV)
    try:
        from hyperspy.axes import UniformDataAxis
    except ImportError:
        skip("HyperSpy version doesn't support non-uniform axis")

    axis = UniformDataAxis(size=20, offset=200, scale=10)
    data = ones(20)
    S1 = LumiSpectrum(data, axes=(axis.get_axis_dictionary(), ))
    S2 = S1.to_eV(inplace=False, jacobian=jacobian)
    S1.axes_manager[0].units = "µm"
    S1.axes_manager[0].axis = axis.axis / 1000
    S1.data *= 1000
    S1.to_eV(jacobian=jacobian)
    assert S1.axes_manager[0].units == "eV"
    assert S2.axes_manager[0].name == "Energy"
    assert S2.axes_manager[0].size == 20
    assert S1.axes_manager[0].axis[0] == S2.axes_manager[0].axis[0]
    assert_allclose(S1.data, S2.data, 5e-4)
    nav = UniformDataAxis(size=4)
    # navigation dimension 1
    L1 = LumiSpectrum(
        ones((4, 20)),
        axes=[nav.get_axis_dictionary(),
              axis.get_axis_dictionary()])
    L2 = L1.to_eV(inplace=False, jacobian=jacobian)
    L1.to_eV(jacobian=jacobian)
    assert L1.axes_manager.signal_axes[0].units == "eV"
    assert L2.axes_manager.signal_axes[0].name == "Energy"
    assert L2.axes_manager.signal_axes[0].size == 20
    assert (L1.axes_manager.signal_axes[0].axis[0] ==
            L2.axes_manager.signal_axes[0].axis[0])
    assert_allclose(L1.data, L2.data, 5e-4)
    # navigation dimension 2
    M1 = LumiSpectrum(
        ones((4, 4, 20)),
        axes=[
            nav.get_axis_dictionary(),
            nav.get_axis_dictionary(),
            axis.get_axis_dictionary(),
        ],
    )
    M2 = M1.to_eV(inplace=False, jacobian=jacobian)
    M1.to_eV(jacobian=jacobian)
    assert M1.axes_manager.signal_axes[0].units == "eV"
    assert M2.axes_manager.signal_axes[0].name == "Energy"
    assert M2.axes_manager.signal_axes[0].size == 20
    assert (M1.axes_manager.signal_axes[0].axis[0] ==
            M2.axes_manager.signal_axes[0].axis[0])
    assert_allclose(M1.data, M2.data, 5e-4)
Ejemplo n.º 3
0
 def test_crop_reverses_indexing(self, mixed):
     axis = UniformDataAxis(size=10, scale=0.1, offset=10)
     if mixed:
         i1, i2 = 2, -6
     else:
         i1, i2 = -8, -6
     axis.crop(i1, i2)
     assert axis.size == 2
     np.testing.assert_almost_equal(axis.axis[0], 10.2)
     np.testing.assert_almost_equal(axis.axis[-1], 10.3)
     np.testing.assert_almost_equal(axis.offset, 10.2)
     np.testing.assert_almost_equal(axis.scale, 0.1)
Ejemplo n.º 4
0
def test_rounding_consistency_axis_type():
    scales = [0.1, -0.1, 0.1, -0.1]
    offsets = [-11.0, -10.9, 10.9, 11.0]
    values = [-10.95, -10.95, 10.95, 10.95]

    for i, (scale, offset, value) in enumerate(zip(scales, offsets, values)):
        ax = UniformDataAxis(scale=scale, offset=offset, size=3)
        ua_idx = ax.value2index(value)
        nua_idx = super(type(ax), ax).value2index(value)
        print('scale', scale)
        print('offset', offset)
        print('Axis values:', ax.axis)
        print(f"value: {value} --> uniform: {ua_idx}, non-uniform: {nua_idx}")
        print("\n")
        assert nua_idx == ua_idx
Ejemplo n.º 5
0
def test_axis2eV():
    axis = DataAxis(size=20, offset=200, scale=10)

    if not "axis" in getfullargspec(DataAxis)[0]:
        raises(ImportError, axis2eV, axis)
    try:
        from hyperspy.axes import UniformDataAxis
    except ImportError:
        skip("HyperSpy version doesn't support non-uniform axis")

    axis = UniformDataAxis(size=20, offset=200, scale=10)
    axis2 = DataAxis(axis=arange(0.2, 0.400, 0.01), units="µm")
    axis3 = DataAxis(axis=arange(1, 2, 0.1), units="eV")
    evaxis, factor = axis2eV(axis)
    evaxis2, factor2 = axis2eV(axis2)
    raises(AttributeError, axis2eV, axis3)
    assert factor == 1e6
    assert factor2 == 1e3
    assert evaxis.name == "Energy"
    assert evaxis.units == "eV"
    assert not evaxis.navigate
    assert evaxis2.units == "eV"
    assert evaxis2.size == 20
    assert_allclose(evaxis.axis[0], evaxis2.axis[0])
    assert_allclose(evaxis.axis[-1], evaxis2.axis[-1])
    assert_allclose(evaxis.axis[0], 3.1781816)
Ejemplo n.º 6
0
def test_axis2invcm():
    axis = DataAxis(size=20, offset=200, scale=10)

    if not "axis" in getfullargspec(DataAxis)[0]:
        raises(ImportError, axis2invcm, axis)
    try:
        from hyperspy.axes import UniformDataAxis
    except ImportError:
        skip("HyperSpy version doesn't support non-uniform axis")

    axis = UniformDataAxis(size=21, offset=200, scale=10)
    axis2 = DataAxis(axis=arange(0.2, 0.410, 0.01), units="µm")
    axis3 = DataAxis(axis=arange(1, 2, 0.1), units=r"cm$^{-1}$")
    invcmaxis, factor = axis2invcm(axis)
    invcmaxis2, factor2 = axis2invcm(axis2)
    raises(AttributeError, axis2invcm, axis3)
    assert factor == 1e7
    assert factor2 == 1e4
    assert invcmaxis.name == "Wavenumber"
    assert invcmaxis.units == r"cm$^{-1}$"
    assert not invcmaxis.navigate
    assert invcmaxis2.units == r"cm$^{-1}$"
    assert invcmaxis2.size == 21
    assert_allclose(invcmaxis.axis[0], invcmaxis2.axis[0])
    assert_allclose(invcmaxis.axis[-1], invcmaxis2.axis[-1])
    assert_allclose(invcmaxis.axis[0], 25000)
Ejemplo n.º 7
0
def test_check_axes_calibration():
    axisDA1 = DataAxis(axis=arange(10)**2)
    axisDA2 = DataAxis(axis=arange(10))
    axisDA3 = DataAxis(axis=arange(10), units='s')
    axisDA4 = DataAxis(axis=arange(10), units='seconds')
    expression = "x ** power"
    axisFDA1 = FunctionalDataAxis(size=10, expression=expression, power=2)
    axisFDA2 = FunctionalDataAxis(size=12, expression=expression, power=2)
    axisUDA1 = UniformDataAxis(size=10, scale=1, offset=10)
    axisUDA2 = UniformDataAxis(size=10, scale=1, offset=0)
    assert check_axes_calibration(axisDA1, axisDA1)
    assert not check_axes_calibration(axisDA1, axisDA2)
    assert check_axes_calibration(axisDA3, axisDA4)
    assert check_axes_calibration(axisFDA1, axisFDA1)
    assert not check_axes_calibration(axisFDA1, axisFDA2)
    assert check_axes_calibration(axisUDA1, axisUDA1)
    assert not check_axes_calibration(axisUDA1, axisUDA2)
    assert not check_axes_calibration(axisDA1, axisUDA1)
    assert not check_axes_calibration(axisFDA1, axisUDA1)
    assert check_axes_calibration(axisDA1, axisFDA1)
    assert check_axes_calibration(axisDA2, axisUDA2)
Ejemplo n.º 8
0
def test_nonuniformFDA(tmp_path, file, lazy):
    fname = tmp_path / file
    data = np.arange(10)
    x0 = UniformDataAxis(size=data.size, offset=1)
    axis = FunctionalDataAxis(expression='1/x', x=x0, navigate=False)
    s = Signal1D(data, axes=(axis.get_axis_dictionary(), ))
    if lazy:
        s = s.as_lazy()
    print(axis.get_axis_dictionary())
    s.save(fname, overwrite=True)
    s2 = load(fname)
    np.testing.assert_array_almost_equal(s.axes_manager[0].axis,
                                         s2.axes_manager[0].axis)
    assert (s2.axes_manager[0].is_uniform == False)
    assert (s2.axes_manager[0].navigate == False)
    assert (s2.axes_manager[0].size == data.size)
Ejemplo n.º 9
0
class TestUniformDataAxisValueRangeToIndicesNegativeScale:

    def setup_method(self, method):
        self.axis = UniformDataAxis(size=10, scale=-0.1, offset=10)

    def test_value_range_to_indices_in_range(self):
        assert self.axis.value_range_to_indices(9.9, 9.2) == (1, 8)

    def test_value_range_to_indices_endpoints(self):
        assert self.axis.value_range_to_indices(10, 9.1) == (0, 9)

    def test_value_range_to_indices_out(self):
        assert self.axis.value_range_to_indices(11, 9) == (0, 9)

    def test_value_range_to_indices_None(self):
        assert self.axis.value_range_to_indices(None, None) == (0, 9)

    def test_value_range_to_indices_v1_greater_than_v2(self):
        with pytest.raises(ValueError):
            self.axis.value_range_to_indices(1, 2)
Ejemplo n.º 10
0
def get_luminescence_signal(navigation_dimension=0,
                            uniform=False,
                            add_baseline=False,
                            add_noise=True,
                            random_state=None):
    """Get an artificial luminescence signal in wavelength scale (nm, uniform) or
    energy scale (eV, non-uniform), simulating luminescence data recorded with a
    diffracting spectrometer. Some random noise is also added to the spectrum,
    to simulate experimental noise.

    Parameters
    ----------
    navigation_dimension: positive int.
        The navigation dimension(s) of the signal. 0 = single spectrum,
        1 = linescan, 2 = spectral map etc...
    uniform: bool.
        return uniform (wavelength) or non-uniform (energy) spectrum
    add_baseline : bool
        If true, adds a constant baseline to the spectrum. Conversion to
        energy representation will turn the constant baseline into inverse
        powerlaw.
    %s

    Example
    -------
    >>> import hyperspy.datasets.artificial_data as ad
    >>> s = ad.get_luminescence_signal()
    >>> s.plot()

    With constant baseline

    >>> s = ad.get_luminescence_signal(uniform=True, add_baseline=True)
    >>> s.plot()

    To make the noise the same for multiple spectra, which can
    be useful for testing fitting routines

    >>> s1 = ad.get_luminescence_signal(random_state=10)
    >>> s2 = ad.get_luminescence_signal(random_state=10)
    >>> (s1.data == s2.data).all()
    True

    2D map

    >>> s = ad.get_luminescence_signal(navigation_dimension=2)
    >>> s.plot()

    See also
    --------
    get_low_loss_eels_signal,
    get_core_loss_eels_signal,
    get_low_loss_eels_line_scan_signal,
    get_core_loss_eels_line_scan_signal,
    get_core_loss_eels_model,
    get_atomic_resolution_tem_signal2d,

    """

    #Initialisation of random number generator
    random_state = check_random_state(random_state)

    #Creating a uniform data axis, roughly similar to Horiba iHR320 with a 150 mm-1 grating
    nm_axis = UniformDataAxis(
        index_in_array=None,
        name="Wavelength",
        units="nm",
        navigate=False,
        size=1024,
        scale=0.54,
        offset=222.495,
        is_binned=False,
    )

    #Artificial luminescence peak
    gaussian_peak = components1d.Gaussian(A=5000, centre=375, sigma=25)

    if navigation_dimension >= 0:
        #Generate empty data (ones)
        data = np.ones([10
                        for i in range(navigation_dimension)] + [nm_axis.size])
        #Generate spatial axes
        spaxes = [
            UniformDataAxis(
                index_in_array=None,
                name="X{:d}".format(i),
                units="um",
                navigate=False,
                size=10,
                scale=2.1,
                offset=0,
                is_binned=False,
            ) for i in range(navigation_dimension)
        ]
        #Generate empty signal
        sig = signals.Signal1D(data, axes=spaxes + [nm_axis])
        sig.metadata.General.title = '{:d}d-map Artificial Luminescence Signal'\
                                        .format(navigation_dimension)
    else:
        raise ValueError("Value {:d} invalid as navigation dimension.".format(\
                            navigation_dimension))

    #Populating data array, possibly with noise and baseline
    sig.data *= gaussian_peak.function(nm_axis.axis)
    if add_noise:
        sig.data += (random_state.uniform(size=sig.data.shape) - 0.5) * 1.4
    if add_baseline:
        data += 350.

    #if not uniform, transformation into non-uniform axis
    if not uniform:
        hc = 1239.84198  #nm/eV
        #converting to non-uniform axis
        sig.axes_manager.signal_axes[0].convert_to_functional_data_axis(\
                                                              expression="a/x",
                                                              name='Energy',
                                                              units='eV',
                                                              a=hc,
                                                          )
        #Reverting the orientation of signal axis to have increasing Energy
        sig = sig.isig[::-1]
        #Jacobian transformation
        Eax = sig.axes_manager.signal_axes[0].axis
        sig *= hc / Eax**2
    return sig
Ejemplo n.º 11
0
 def setup_method(self, method):
     self.axis = UniformDataAxis(size=10, scale=-0.1, offset=10)
Ejemplo n.º 12
0
    def test_crop(self, use_indices):
        axis = UniformDataAxis(size=10, scale=0.1, offset=10)
        start = 10.2
        if use_indices:
            start = axis.value2index(start)
        axis.crop(start)
        assert axis.size == 8
        np.testing.assert_almost_equal(axis.axis[0], 10.2)
        np.testing.assert_almost_equal(axis.axis[-1], 10.9)
        np.testing.assert_almost_equal(axis.offset, 10.2)
        np.testing.assert_almost_equal(axis.scale, 0.1)

        axis = UniformDataAxis(size=10, scale=0.1, offset=10)
        end = 10.4
        if use_indices:
            end = axis.value2index(end)
        axis.crop(start, end)
        assert axis.size == 2
        np.testing.assert_almost_equal(axis.axis[0], 10.2)
        np.testing.assert_almost_equal(axis.axis[-1], 10.3)
        np.testing.assert_almost_equal(axis.offset, 10.2)
        np.testing.assert_almost_equal(axis.scale, 0.1)

        axis = UniformDataAxis(size=10, scale=0.1, offset=10)
        axis.crop(None, end)
        assert axis.size == 4
        np.testing.assert_almost_equal(axis.axis[0], 10.0)
        np.testing.assert_almost_equal(axis.axis[-1], 10.3)
        np.testing.assert_almost_equal(axis.offset, 10.0)
        np.testing.assert_almost_equal(axis.scale, 0.1)
Ejemplo n.º 13
0
 def test_update_from(self):
     ax2 = UniformDataAxis(size=2, units="nm", scale=0.5)
     self.axis.update_from(ax2, attributes=("units", "scale"))
     assert ((ax2.units, ax2.scale) ==
             (self.axis.units, self.axis.scale))
Ejemplo n.º 14
0
class TestUniformDataAxis:

    def setup_method(self, method):
        self.axis = UniformDataAxis(size=10, scale=0.1, offset=10)

    def _test_initialisation_parameters(self, axis):
        assert axis.scale == 0.1
        assert axis.offset == 10
        def func(x): return axis.scale * x + axis.offset
        np.testing.assert_allclose(axis.axis, func(np.arange(10)))

    def test_initialisation_parameters(self):
        self._test_initialisation_parameters(self.axis)

    def test_create_axis(self):
        axis = create_axis(**self.axis.get_axis_dictionary())
        assert isinstance(axis, UniformDataAxis)
        self._test_initialisation_parameters(axis)

    def test_value_range_to_indices_in_range(self):
        assert self.axis.is_uniform
        assert (
            self.axis.value_range_to_indices(
                10.1, 10.8) == (1, 8))

    def test_value_range_to_indices_endpoints(self):
        assert self.axis.value_range_to_indices(10, 10.9) == (0, 9)

    def test_value_range_to_indices_out(self):
        assert self.axis.value_range_to_indices(9, 11) == (0, 9)

    def test_value_range_to_indices_None(self):
        assert self.axis.value_range_to_indices(None, None) == (0, 9)

    def test_value_range_to_indices_v1_greater_than_v2(self):
        with pytest.raises(ValueError):
            self.axis.value_range_to_indices(2, 1)

    def test_deepcopy(self):
        ac = copy.deepcopy(self.axis)
        ac.offset = 100
        assert self.axis.offset != ac.offset
        assert self.axis.navigate == ac.navigate
        assert self.axis.is_binned == ac.is_binned

    def test_deepcopy_on_trait_change(self):
        ac = copy.deepcopy(self.axis)
        ac.offset = 100
        assert ac.axis[0] == ac.offset

    def test_uniform_value2index(self):
        #Tests for value2index
        #Works as intended
        assert self.axis.value2index(10.15) == 1
        assert self.axis.value2index(10.17, rounding=math.floor) == 1
        assert self.axis.value2index(10.13, rounding=math.ceil) == 2
        # Test that output is integer
        assert isinstance(self.axis.value2index(10.15), (int, np.integer))
        #Endpoint left
        assert self.axis.value2index(10.) == 0
        #Endpoint right
        assert self.axis.value2index(10.9) == 9
        #Input None --> output None
        assert self.axis.value2index(None) == None
        #NaN in --> error out
        with pytest.raises(ValueError):
            self.axis.value2index(np.nan)
        #Values in out of bounds --> error out (both sides of axis)
        with pytest.raises(ValueError):
            self.axis.value2index(-2)
        with pytest.raises(ValueError):
            self.axis.value2index(111)
        #str without unit in --> error out
        with pytest.raises(ValueError):
            self.axis.value2index("69")
        #Empty str in --> error out
        with pytest.raises(ValueError):
            self.axis.value2index("")
        #Value with unit when axis is unitless --> Error out
        with pytest.raises(ValueError):
            self.axis.value2index("0.0101um")

        #Tests with array Input
        #Arrays work as intended
        arval = np.array([[10.15, 10.15], [10.24, 10.28]])
        assert np.all(self.axis.value2index(arval) \
                        == np.array([[1, 1], [2, 3]]))
        assert np.all(self.axis.value2index(arval, rounding=math.floor) \
                        == np.array([[1, 1], [2, 2]]))
        assert np.all(self.axis.value2index(arval, rounding=math.ceil)\
                        == np.array([[2, 2], [3, 3]]))
        #List in --> array out
        assert np.all(self.axis.value2index(arval.tolist()) \
                                            == np.array([[1, 1], [2, 3]]))
        #One value out of bound in array in --> error out (both sides)
        arval[1, 1] = 111
        with pytest.raises(ValueError):
            self.axis.value2index(arval)
        arval[1, 1] = -0.3
        with pytest.raises(ValueError):
            self.axis.value2index(arval)
        #One NaN in array in --> error out
        if platform.machine() != 'aarch64':
            # Skip aarch64 platform because it doesn't raise error
            arval[1, 1] = np.nan
            with pytest.raises(ValueError):
                self.axis.value2index(arval)

        #Copy of axis with units
        axis = copy.deepcopy(self.axis)
        axis.units = 'nm'

        #Value with unit in --> OK out
        assert axis.value2index("0.0101um") == 1
        #Value with relative units in --> OK out
        assert self.axis.value2index("rel0.5") == 4

        #Works with arrays of values with units in
        np.testing.assert_allclose(
            axis.value2index(['0.01um', '0.0101um', '0.0103um']),
            np.array([0, 1, 3])
            )
        #Raises errors if a weird unit is passed in
        with pytest.raises(BaseException):
            axis.value2index(["0.01uma", '0.0101uma', '0.0103uma'])
        #Values
        np.testing.assert_allclose(
            self.axis.value2index(["rel0.0", "rel0.5", "rel1.0"]),
            np.array([0, 4, 9])
            )

    def test_slice_me(self):
        assert (
            self.axis._slice_me(slice(np.float32(10.2), 10.4, 2)) ==
            slice(2, 4, 2)
            )

    def test_update_from(self):
        ax2 = UniformDataAxis(size=2, units="nm", scale=0.5)
        self.axis.update_from(ax2, attributes=("units", "scale"))
        assert ((ax2.units, ax2.scale) ==
                (self.axis.units, self.axis.scale))

    def test_value_changed_event(self):
        ax = self.axis
        m = mock.Mock()
        ax.events.value_changed.connect(m.trigger_me)
        ax.value = ax.value
        assert not m.trigger_me.called
        ax.value = ax.value + ax.scale * 0.3
        assert not m.trigger_me.called
        ax.value = ax.value + ax.scale
        assert m.trigger_me.called

    def test_index_changed_event(self):
        ax = self.axis
        m = mock.Mock()
        ax.events.index_changed.connect(m.trigger_me)
        ax.index = ax.index
        assert not m.trigger_me.called
        ax.index += 1
        assert m.trigger_me.called

    def test_convert_to_non_uniform_axis(self):
        axis = np.copy(self.axis.axis)
        is_binned = self.axis.is_binned
        navigate = self.axis.navigate
        self.axis.name = "parrot"
        self.axis.units = "plumage"
        s = Signal1D(np.arange(10), axes=[self.axis])
        index_in_array = s.axes_manager[0].index_in_array
        s.axes_manager[0].convert_to_non_uniform_axis()
        assert isinstance(s.axes_manager[0], DataAxis)
        assert s.axes_manager[0].name == "parrot"
        assert s.axes_manager[0].units == "plumage"
        assert s.axes_manager[0].size == 10
        assert s.axes_manager[0].low_value == 10
        assert s.axes_manager[0].high_value == 10 + 0.1 * 9
        np.testing.assert_allclose(s.axes_manager[0].axis, axis)
        with pytest.raises(AttributeError):
            s.axes_manager[0].offset
        with pytest.raises(AttributeError):
            s.axes_manager[0].scale
        assert index_in_array == s.axes_manager[0].index_in_array
        assert is_binned == s.axes_manager[0].is_binned
        assert navigate == s.axes_manager[0].navigate

    def test_convert_to_functional_data_axis(self):
        axis = np.copy(self.axis.axis)
        is_binned = self.axis.is_binned
        navigate = self.axis.navigate
        self.axis.name = "parrot"
        self.axis.units = "plumage"
        s = Signal1D(np.arange(10), axes=[self.axis])
        index_in_array = s.axes_manager[0].index_in_array
        s.axes_manager[0].convert_to_functional_data_axis(expression = 'x**2')
        assert isinstance(s.axes_manager[0], FunctionalDataAxis)
        assert s.axes_manager[0].name == "parrot"
        assert s.axes_manager[0].units == "plumage"
        assert s.axes_manager[0].size == 10
        assert s.axes_manager[0].low_value == 10**2
        assert s.axes_manager[0].high_value == (10 + 0.1 * 9)**2
        assert s.axes_manager[0]._expression == 'x**2'
        assert isinstance(s.axes_manager[0].x, UniformDataAxis)
        np.testing.assert_allclose(s.axes_manager[0].axis, axis**2)
        with pytest.raises(AttributeError):
            s.axes_manager[0].offset
        with pytest.raises(AttributeError):
            s.axes_manager[0].scale
        assert index_in_array == s.axes_manager[0].index_in_array
        assert is_binned == s.axes_manager[0].is_binned
        assert navigate == s.axes_manager[0].navigate

    @pytest.mark.parametrize("use_indices", (False, True))
    def test_crop(self, use_indices):
        axis = UniformDataAxis(size=10, scale=0.1, offset=10)
        start = 10.2
        if use_indices:
            start = axis.value2index(start)
        axis.crop(start)
        assert axis.size == 8
        np.testing.assert_almost_equal(axis.axis[0], 10.2)
        np.testing.assert_almost_equal(axis.axis[-1], 10.9)
        np.testing.assert_almost_equal(axis.offset, 10.2)
        np.testing.assert_almost_equal(axis.scale, 0.1)

        axis = UniformDataAxis(size=10, scale=0.1, offset=10)
        end = 10.4
        if use_indices:
            end = axis.value2index(end)
        axis.crop(start, end)
        assert axis.size == 2
        np.testing.assert_almost_equal(axis.axis[0], 10.2)
        np.testing.assert_almost_equal(axis.axis[-1], 10.3)
        np.testing.assert_almost_equal(axis.offset, 10.2)
        np.testing.assert_almost_equal(axis.scale, 0.1)

        axis = UniformDataAxis(size=10, scale=0.1, offset=10)
        axis.crop(None, end)
        assert axis.size == 4
        np.testing.assert_almost_equal(axis.axis[0], 10.0)
        np.testing.assert_almost_equal(axis.axis[-1], 10.3)
        np.testing.assert_almost_equal(axis.offset, 10.0)
        np.testing.assert_almost_equal(axis.scale, 0.1)

    @pytest.mark.parametrize("mixed", (False, True))
    def test_crop_reverses_indexing(self, mixed):
        axis = UniformDataAxis(size=10, scale=0.1, offset=10)
        if mixed:
            i1, i2 = 2, -6
        else:
            i1, i2 = -8, -6
        axis.crop(i1, i2)
        assert axis.size == 2
        np.testing.assert_almost_equal(axis.axis[0], 10.2)
        np.testing.assert_almost_equal(axis.axis[-1], 10.3)
        np.testing.assert_almost_equal(axis.offset, 10.2)
        np.testing.assert_almost_equal(axis.scale, 0.1)

    def test_parse_value(self):
        ax = copy.deepcopy(self.axis)
        ax.units = 'nm'
        # slicing by index
        assert ax._parse_value(5) == 5
        assert type(ax._parse_value(5)) is int
        # slicing by calibrated value
        assert ax._parse_value(10.5) == 10.5
        assert type(ax._parse_value(10.5)) is float
        # slicing by unit
        assert ax._parse_value('10.5nm') == 10.5
        np.testing.assert_almost_equal(ax._parse_value('10500pm'), 10.5)

    def test_parse_value_from_relative_string(self):
        ax = self.axis
        assert ax._parse_value_from_string('rel0.0') == 10.0
        assert ax._parse_value_from_string('rel0.5') == 10.45
        assert ax._parse_value_from_string('rel1.0') == 10.9

    def test_slice_empty_string(self):
        ax = self.axis
        with pytest.raises(ValueError):
            ax._parse_value("")

    def test_calibrate(self):
        offset, scale = self.axis.calibrate(value_tuple=(11,12), \
                                index_tuple=(0,5), modify_calibration=False)
        assert scale == 0.2
        assert offset == 11
        self.axis.calibrate(value_tuple=(11,12), index_tuple=(0,5))
        assert self.axis.scale == 0.2
        assert self.axis.offset == 11