diff --git a/specparam/algorithms/spectral_fit.py b/specparam/algorithms/spectral_fit.py index c2be3fc28..e3ea7d573 100644 --- a/specparam/algorithms/spectral_fit.py +++ b/specparam/algorithms/spectral_fit.py @@ -160,7 +160,7 @@ def _fit(self): # Take an initial fit of the aperiodic component temp_aperiodic_params = self._robust_ap_fit(self.data.freqs, self.data.power_spectrum) - temp_ap_fit = self.modes.aperiodic.func(self.data.freqs, *temp_aperiodic_params) + temp_ap_fit = self.modes.aperiodic.generate(self.data.freqs, *temp_aperiodic_params) # Find peaks from the flattened power spectrum, and fit them temp_spectrum_flat = self.data.power_spectrum - temp_ap_fit @@ -168,7 +168,7 @@ def _fit(self): # Calculate the peak fit # Note: if no peaks are found, this creates a flat (all zero) peak fit - self.results.model._peak_fit = self.modes.periodic.func(\ + self.results.model._peak_fit = self.modes.periodic.generate(\ self.data.freqs, *np.ndarray.flatten(self.results.params.periodic.get_params('fit'))) # Create peak-removed (but not flattened) power spectrum @@ -178,7 +178,7 @@ def _fit(self): # Run final aperiodic fit on peak-removed power spectrum self.results.params.aperiodic.add_params('fit', \ self._simple_ap_fit(self.data.freqs, self.results.model._spectrum_peak_rm)) - self.results.model._ap_fit = self.modes.aperiodic.func(\ + self.results.model._ap_fit = self.modes.aperiodic.generate(\ self.data.freqs, *self.results.params.aperiodic.params) # Create remaining model components: flatspec & full power_spectrum model fit @@ -305,7 +305,7 @@ def _robust_ap_fit(self, freqs, power_spectrum): # Do a quick, initial aperiodic fit popt = self._simple_ap_fit(freqs, power_spectrum) - initial_fit = self.modes.aperiodic.func(freqs, *popt) + initial_fit = self.modes.aperiodic.generate(freqs, *popt) # Flatten power_spectrum based on initial aperiodic fit flatspec = power_spectrum - initial_fit @@ -406,7 +406,7 @@ def _fit_peaks(self, flatspec): # Fit and subtract guess peak from the spectrum guess = np.vstack((guess, cur_guess)) - peak_fit = self.modes.periodic.func(self.data.freqs, *cur_guess) + peak_fit = self.modes.periodic.generate(self.data.freqs, *cur_guess) flat_iter = flat_iter - peak_fit # Check peaks based on edges, and on overlap, dropping any that violate requirements diff --git a/specparam/modes/mode.py b/specparam/modes/mode.py index bdcb79f51..d7c245904 100644 --- a/specparam/modes/mode.py +++ b/specparam/modes/mode.py @@ -88,6 +88,25 @@ def n_params(self): return self.params.n_params + def generate(self, freqs, *params): + """Generate component values from component function. + + Parameters + ---------- + freqs : 1d array + Frequency values to generate component for. + *params + Parameters for the fit mode to generate with. + + Returns + ------- + 1d array + Generated component values. + """ + + return self.func(freqs, *params) + + def check_params(self): """Check the description of the parameters for the current mode.""" diff --git a/specparam/sim/gen.py b/specparam/sim/gen.py index 38d2dc429..020b580b5 100644 --- a/specparam/sim/gen.py +++ b/specparam/sim/gen.py @@ -59,8 +59,7 @@ def gen_aperiodic(freqs, aperiodic_mode, aperiodic_params): """ ap_mode = check_mode_definition(aperiodic_mode, 'aperiodic') - - ap_vals = ap_mode.func(freqs, *aperiodic_params) + ap_vals = ap_mode.generate(freqs, *aperiodic_params) return ap_vals @@ -84,8 +83,7 @@ def gen_periodic(freqs, periodic_mode, periodic_params): """ pe_mode = check_mode_definition(periodic_mode, 'periodic') - - pe_vals = pe_mode.func(freqs, *check_flat(periodic_params)) + pe_vals = pe_mode.generate(freqs, *check_flat(periodic_params)) return pe_vals diff --git a/specparam/sim/sim.py b/specparam/sim/sim.py index ef1c808ec..f68e1f8fd 100644 --- a/specparam/sim/sim.py +++ b/specparam/sim/sim.py @@ -22,17 +22,22 @@ def sim_power_spectrum(freq_range, aperiodic_params, periodic_params, Frequency range to simulate power spectrum across, as [f_low, f_high], inclusive. aperiodic_params : dict of {str : array} Mode definition and parameters to create the aperiodic component of a power spectrum. - Should be organized as {mode : params}. - periodic_params : list of float or list of list of float + Should be organized as {mode : params}, where `mode` is a string label for a mode to + simulate with and `params` is a set of parameter values of length aperiodic_mode.n_params. + periodic_params : dict of {str : list of float or list of list of float} Mode definition and parameters to create the periodic component of a power spectrum. - Should be organized as {mode : params}. + Should be organized as {mode : params}, where `mode` is a string label for a mode to + simulate with and `params` corresponds to the number of desired peaks, with a total number + of values matching periodic_mode.n_params * n_peaks. + This can can be a flat list (ex: [10, 1, 1, 20, 0.5, 1]) or be grouped into a + list of lists (ex: [[10, 1, 1], [20, 0.5, 1]]). nlv : float, optional, default: 0.005 Noise level to add to generated power spectrum. freq_res : float, optional, default: 0.5 Frequency resolution for the simulated power spectrum. f_rotation : float, optional - Frequency value, in Hz, to rotate around. - Should only be set if spectrum is to be rotated. + Frequency value, in Hz, to rotate around. Should only be set if spectrum is to be rotated. + Can only be used with `powerlaw` aperiodic mode. return_params : bool, optional, default: False Whether to return the parameters for the simulated spectrum. @@ -48,44 +53,21 @@ def sim_power_spectrum(freq_range, aperiodic_params, periodic_params, Notes ----- - Aperiodic Parameters: - - - The function for the aperiodic process to use is inferred from the provided parameters. - - If length of 2, the 'fixed' aperiodic mode is used, if length of 3, 'knee' is used. - - Periodic Parameters: - - - The periodic component is comprised of a set of 'peaks', each of which is described as: - - * Mean (Center Frequency), height (Power), and standard deviation (Bandwidth). - * Make sure any center frequencies you request are within the simulated frequency range. - - - The total number of parameters that need to be specified is number of peaks * 3 - - * These can be specified in as all together in a flat list (ex: [10, 1, 1, 20, 0.5, 1]) - * They can also be grouped into a list of lists (ex: [[10, 1, 1], [20, 0.5, 1]]) + - See `check_modes` for more information on the available modes to use to simulate, + as well as descriptions of the parameters for each mode. Rotating Power Spectra: - - You can optionally specify a rotation frequency, such that power spectra will be - simulated and rotated around that point to the specified aperiodic exponent. + - For the powerlaw aperiodic component only, you can optionally specify a rotation frequency, + such that power spectra will be simulated and rotated around that point to the specified + aperiodic exponent. Any power spectra simulated with the same rotation frequency will relate + to each other by having the specified rotation point. - * This can be used so that any power spectra simulated with the same 'f_rotation' - will relate to each other by having the specified rotation point. - - - Note that rotating power spectra changes the offset. - - * If you specify an offset value to simulate as well as 'f_rotation', the returned - spectrum will NOT have the requested offset. It instead will have the offset - value required to create the requested aperiodic exponent with the requested - rotation point. - * If you return SimParams, the recorded offset will be the calculated offset - of the data post rotation, and not the entered value. - - - You cannot rotate power spectra simulated with a knee. - - * The procedure we use to rotate does not support spectra with a knee, and so - setting 'f_rotation' with a knee will lead to an error. + - Note that rotating power spectra changes the offset. If a simulated spectrum is rotated, + the returned spectrum will NOT have the offset of the input parameters, and will instead + have offset value required to create the given aperiodic exponent with the requested + rotation point. If you return SimParams, the recorded offset will be the calculated offset + of the data post rotation, and not the entered value. Examples -------- @@ -139,22 +121,22 @@ def sim_group_power_spectra(n_spectra, freq_range, aperiodic_params, periodic_pa The number of power spectra to generate. freq_range : list of [float, float] Frequency range to simulate power spectra across, as [f_low, f_high], inclusive. - aperiodic_params : list of float or generator - Parameters for the aperiodic component of the power spectra. - aperiodic_mode : Mode or str - Which kind of aperiodic component to simulate. - periodic_params : list of float or generator - Parameters for the periodic component of the power spectra. - Length of n_peaks * 3. - periodic_mode : Mode or str - Which kind of periodic component to simulate. + aperiodic_params : dict of {str : array} + Mode definition and parameters to create the aperiodic components of the power spectra. + Should be organized as {mode : params}, where `mode` is a string label for a mode to + simulate with and `params` (array_like or generator) defines the aperiodic parameters. + periodic_params : dict of {str : list of float or list of list of float} + Mode definition and parameters to create the periodic components of the power spectras. + Should be organized as {mode : params}, where `mode` is a string label for a mode to + simulate with and `params` (array_like or generator) defines the periodic parameters. nlvs : float or list of float or generator, optional, default: 0.005 Noise level to add to generated power spectrum. freq_res : float, optional, default: 0.5 Frequency resolution for the simulated power spectra. f_rotation : float, optional - Frequency value, in Hz, to rotate around. - Should only be set if spectra are to be rotated. + Frequency value, in Hz, to rotate around. Should only be set if spectrum is to be rotated. + Can only be used with `powerlaw` aperiodic mode. + See additional notes in `sim_power_spectra` on rotating simulated power spectra. return_params : bool, optional, default: False Whether to return the parameters for the simulated spectra. @@ -179,40 +161,6 @@ def sim_group_power_spectra(n_spectra, freq_range, aperiodic_params, periodic_pa - A generator object that returns parameters for a power spectrum. If so, each spectrum has parameters sampled from the generator. - Aperiodic Parameters: - - - The function for the aperiodic process to use is inferred from the provided parameters. - - If length of 2, the 'fixed' aperiodic mode is used, if length of 3, 'knee' is used. - - Periodic Parameters: - - - The periodic component is comprised of a set of 'peaks', each of which is described as: - - * Mean (Center Frequency), height (Power), and standard deviation (Bandwidth). - * Make sure any center frequencies you request are within the simulated frequency range. - - Rotating Power Spectra: - - - You can optionally specify a rotation frequency, such that power spectra will be - simulated and rotated around that point to the specified aperiodic exponent. - - * This can be used so that any power spectra simulated with the same 'f_rotation' - will relate to each other by having the specified rotation point. - - - Note that rotating power spectra changes the offset. - - * If you specify an offset value to simulate as well as 'f_rotation', the returned - spectrum will NOT have the requested offset. It instead will have the offset - value required to create the requested aperiodic exponent with the requested - rotation point. - * If you return SimParams, the recorded offset will be the calculated offset - of the data post rotation, and not the entered value. - - - You cannot rotate power spectra simulated with a knee. - - * The procedure we use to rotate does not support spectra with a knee, and so - setting 'f_rotation' with a knee will lead to an error. - Examples -------- Generate 2 power spectra using the same parameters: diff --git a/specparam/tests/modes/test_mode.py b/specparam/tests/modes/test_mode.py index 087882543..d7c99ae72 100644 --- a/specparam/tests/modes/test_mode.py +++ b/specparam/tests/modes/test_mode.py @@ -2,7 +2,10 @@ from collections import OrderedDict +import numpy as np + from specparam.modes.params import ParamDefinition +from specparam.modes.definitions import ap_powerlaw from specparam.modes.mode import * @@ -41,3 +44,11 @@ def tfit2(xs, *params): ndim=2, freq_space='linear', powers_space='linear') assert tmode assert isinstance(tmode.params, ParamDefinition) + +def test_mode_generate(): + + freqs = np.linspace(1, 40, 40) + params = [1, 1] + + output = ap_powerlaw.generate(freqs, *params) + assert isinstance(output, np.ndarray) diff --git a/specparam/tests/sim/test_utils.py b/specparam/tests/utils/test_random.py similarity index 75% rename from specparam/tests/sim/test_utils.py rename to specparam/tests/utils/test_random.py index ee6d45be7..a5a353eb3 100644 --- a/specparam/tests/sim/test_utils.py +++ b/specparam/tests/utils/test_random.py @@ -1,6 +1,6 @@ -"""Test functions for specparam.sim.utils.""" +"""Test functions for specparam.utils.random""" -from specparam.sim.utils import * +from specparam.utils.random import * ################################################################################################### ################################################################################################### diff --git a/specparam/sim/utils.py b/specparam/utils/random.py similarity index 90% rename from specparam/sim/utils.py rename to specparam/utils/random.py index fb1ede941..0de593fd5 100644 --- a/specparam/sim/utils.py +++ b/specparam/utils/random.py @@ -1,4 +1,4 @@ -"""Utilities related to simulations.""" +"""Utilities related to managing randomness.""" import numpy as np