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)
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)
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_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
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)
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)
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)
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)
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)
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
def setup_method(self, method): self.axis = UniformDataAxis(size=10, scale=-0.1, offset=10)
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)
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))
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