示例#1
0
文件: fit.py 项目: anchandm/fooof
    def _fit_peak_guess(self, guess):
        """Fits a group of peak guesses with a fit function.

        Parameters
        ----------
        guess : 2d array, shape=[n_peaks, 3]
            Guess parameters for gaussian fits to peaks, as gaussian parameters.

        Returns
        -------
        gaussian_params : 2d array, shape=[n_peaks, 3]
            Parameters for gaussian fits to peaks, as gaussian parameters.
        """

        # Set the bounds for center frequency, enforce positive height value, and set bandwidth limits.
        #  Note that 'guess' is in terms of gaussian standard deviation, so +/- BW is 2 * the guess_gauss_std.
        #  This set of list comprehensions is a way to end up with bounds in the form:
        #   ((cf_low_bound_peak1, height_low_bound_peak1, bw_low_bound_peak1, *repeated for n_peak*),
        #    (cf_high_bound_peak1, height_high_bound_peak1, bw_high_bound_peak, *repeated for n_peak*))
        lo_bound = [[
            peak[0] - 2 * self._cf_bound * peak[2], 0,
            self._gauss_std_limits[0]
        ] for peak in guess]
        hi_bound = [[
            peak[0] + 2 * self._cf_bound * peak[2], np.inf,
            self._gauss_std_limits[1]
        ] for peak in guess]

        # Check that CF bounds are within frequency range, and, if not, updates them to be restricted to frequency range.
        lo_bound = [
            bound if bound[0] > self.freq_range[0] else
            [self.freq_range[0], *bound[1:]] for bound in lo_bound
        ]
        hi_bound = [
            bound if bound[0] < self.freq_range[1] else
            [self.freq_range[1], *bound[1:]] for bound in hi_bound
        ]

        # Unpacks the embedded lists into flat tuples, which is what the fit function requires as input.
        gaus_param_bounds = (tuple([
            item for sublist in lo_bound for item in sublist
        ]), tuple([item for sublist in hi_bound for item in sublist]))

        # Flatten guess, for use with curve fit.
        guess = np.ndarray.flatten(guess)

        # Fit the peaks.
        gaussian_params, _ = curve_fit(gaussian_function,
                                       self.freqs,
                                       self._spectrum_flat,
                                       p0=guess,
                                       maxfev=5000,
                                       bounds=gaus_param_bounds)

        # Re-organize params into 2d matrix.
        gaussian_params = np.array(group_three(gaussian_params))

        return gaussian_params
示例#2
0
def test_fooof_fit_nk():
    """Test FOOOF fit, no knee."""

    bgp = [50, 2]
    gauss_params = [10, 0.5, 2, 20, 0.3, 4]

    xs, ys = gen_power_spectrum([3, 50], bgp, gauss_params)

    tfm = FOOOF()
    tfm.fit(xs, ys)

    # Check model results - background parameters
    assert np.all(np.isclose(bgp, tfm.background_params_, [0.5, 0.1]))

    # Check model results - gaussian parameters
    for ii, gauss in enumerate(group_three(gauss_params)):
        assert np.all(np.isclose(gauss, tfm._gaussian_params[ii], [1.5, 0.25, 0.5]))
示例#3
0
    def _fit_peak_guess(self, guess):
        """Fit a guess of peak gaussian fit(s).

        Parameters
        ----------
        guess : 2d array
            Guess parameters for gaussian fits to peaks. [n_oscs, 3], row: [CF, amp, BW].

        Returns
        -------
        gaussian_params : 2d array
            Parameters for gaussian fits to peaks. [n_oscs, 3], row: [CF, amp, BW].
        """

        # Set the bounds for center frequency, enforce positive amp value, and set width limits.
        #  Note that osc_guess is in terms of gaussian std, so +/- BW is 2 * the guess_gauss_std.
        #  This set of list comprehensions is a way to end up with bounds in the form:
        #   ((cf_low_bound_osc1, amp_low_bound_osc1, bw_low_bound_osc1, *repeated for n oscs*),
        #    (cf_high_bound_osc1, amp_high_bound_osc1, bw_high_bound_osc1, *repeated for n oscs*))
        lo_bound = [[
            osc[0] - 2 * self._cf_bound * osc[2], 0, self._gauss_std_limits[0]
        ] for osc in guess]
        hi_bound = [[
            osc[0] + 2 * self._cf_bound * osc[2], np.inf,
            self._gauss_std_limits[1]
        ] for osc in guess]
        # The double for-loop here unpacks the embedded lists
        gaus_param_bounds = (tuple([
            item for sublist in lo_bound for item in sublist
        ]), tuple([item for sublist in hi_bound for item in sublist]))

        # Flatten guess, for use with curve fit.
        guess = np.ndarray.flatten(guess)

        # Fit the peaks.
        gaussian_params, _ = curve_fit(gaussian_function,
                                       self.freqs,
                                       self._spectrum_flat,
                                       p0=guess,
                                       maxfev=5000,
                                       bounds=gaus_param_bounds)

        # Re-organize params into 2d matrix.
        gaussian_params = np.array(group_three(gaussian_params))

        return gaussian_params
示例#4
0
def test_fooof_fit_knee():
    """Test FOOOF fit, with a knee."""

    ap_params = [50, 10, 1]
    gauss_params = [10, 0.3, 2, 20, 0.1, 4, 60, 0.3, 1]
    nlv = 0.0025

    xs, ys = gen_power_spectrum([1, 150], ap_params, gauss_params, nlv)

    tfm = FOOOF(aperiodic_mode='knee', verbose=False)
    tfm.fit(xs, ys)

    # Check model results - aperiodic parameters
    assert np.allclose(ap_params, tfm.aperiodic_params_, [1, 2, 0.2])

    # Check model results - gaussian parameters
    for ii, gauss in enumerate(group_three(gauss_params)):
        assert np.allclose(gauss, tfm.gaussian_params_[ii], [2.0, 0.5, 1.0])
示例#5
0
def test_fooof_fit_nk():
    """Test FOOOF fit, no knee."""

    ap_params = [50, 2]
    gauss_params = [10, 0.5, 2, 20, 0.3, 4]
    nlv = 0.0025

    xs, ys = gen_power_spectrum([3, 50], ap_params, gauss_params, nlv)

    tfm = FOOOF(verbose=False)
    tfm.fit(xs, ys)

    # Check model results - aperiodic parameters
    assert np.allclose(ap_params, tfm.aperiodic_params_, [0.5, 0.1])

    # Check model results - gaussian parameters
    for ii, gauss in enumerate(group_three(gauss_params)):
        assert np.allclose(gauss, tfm.gaussian_params_[ii], [2.0, 0.5, 1.0])
示例#6
0
def collect_sim_params(aperiodic_params, periodic_params, nlv):
    """Collect simulation parameters into a SimParams object.

    Parameters
    ----------
    aperiodic_params : list of float
        Parameters of the aperiodic component of the power spectrum.
    periodic_params : list of float or list of list of float
        Parameters of the periodic component of the power spectrum.
    nlv : float
        Noise level of the power spectrum.

    Returns
    -------
    SimParams
        Object containing the simulation parameters.
    """

    return SimParams(aperiodic_params.copy(),
                     sorted(group_three(check_flat(periodic_params))), nlv)
示例#7
0
def gen_group_power_spectra(n_spectra, freq_range, background_params, gauss_params, nlvs=0.005, freq_res=0.5):
    """Generate a group of synthetic power spectra.

    Parameters
    ----------
    n_spectra : int
        The number of power spectra to generate in the matrix.
    freq_range : list of [float, float]
        Minimum and maximum values of the desired frequency vector.
    background_params : list of float or generator
        Parameter for the background of the power spectra.
    gauss_params : list of float or generator
        Parameters for the peaks of the power spectra.
            Length of n_peaks * 3.
    nlvs : float or list of float or generator, optional
        Noise level to add to generated power spectrum. default: 0.005
    freq_res : float, optional
        Frequency resolution for the synthetic power spectra. default: 0.5

    Returns
    -------
    xs : 1d array
        Frequency values (linear).
    ys : 2d array
        Matrix of power values (linear).
    syn_params : list of SynParams
        Definitions of parameters used for each spectrum. Has length of n_spectra.

    Notes
    -----
    Parameters options can be:
        - A single set of parameters
            - If so, these same parameters are used for all spectra.
        - A list of parameters whose length is n_spectra.
            - If so, each successive parameter set is such for each successive spectrum.
        - A generator object that returns parameters for a power spectrum.
            - If so, each spectrum has parameters pulled from the generator.
    Background Parameters:
        - The type of background process to use is inferred from the provided parameters.
            - If length of 2, 'fixed' background is used, if length of 3, 'knee' is used.
    Gaussian Parameters:
        - Each gaussian description is a set of three values:
            - mean (CF), amplitude (Amp), and std (BW)


    Examples
    --------
    Generate 2 power spectra using the same parameters.
    >>> freqs, psds, _ = gen_group_power_spectra(2, [1, 50], [0, 2], [10, 1, 1])

    Generate 10 power spectra, randomly sampling possible parameters
    >>> bg_opts = param_sampler([[0, 1.0], [0, 1.5], [0, 2]])
    >>> gauss_opts = param_sampler([[], [10, 1, 1], [10, 1, 1, 20, 2, 1]])
    >>> freqs, psds, syn_params = gen_group_power_spectra(10, [1, 50], bg_opts, gauss_opts)
    """

    # Initialize things
    xs = gen_freqs(freq_range, freq_res)
    ys = np.zeros([n_spectra, len(xs)])
    syn_params = [None] * n_spectra

    # Check if inputs are generators, if not, make them into repeat generators
    background_params = _check_iter(background_params, n_spectra)
    gauss_params = _check_iter(gauss_params, n_spectra)
    nlvs = _check_iter(nlvs, n_spectra)

    # Synthesize power spectra
    for ind, bgp, gp, nlv in zip(range(n_spectra), background_params, gauss_params, nlvs):

        syn_params[ind] = SynParams(bgp, sorted(group_three(gp)), nlv)
        ys[ind, :] = gen_power_vals(xs, bgp, gp, nlv)

    return xs, ys, syn_params
示例#8
0
文件: gen.py 项目: vlitvak/fooof
def gen_group_power_spectra(n_spectra,
                            freq_range,
                            aperiodic_params,
                            gaussian_params,
                            nlvs=0.005,
                            freq_res=0.5):
    """Generate a group of simulated power spectra.

    Parameters
    ----------
    n_spectra : int
        The number of power spectra to generate in the matrix.
    freq_range : list of [float, float]
        Minimum and maximum values of the desired frequency vector.
    aperiodic_params : list of float or generator
        Parameters for the aperiodic component of the power spectra.
    gaussian_params : list of float or generator
        Parameters for the peaks of the power spectra.
            Length of n_peaks * 3.
    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.

    Returns
    -------
    freqs : 1d array
        Frequency values (linear).
    powers : 2d array
        Matrix of power values (linear), as [n_power_spectra, n_freqs].
    sim_params : list of SimParams
        Definitions of parameters used for each spectrum. Has length of n_spectra.

    Notes
    -----
    Parameters options can be:

    - A single set of parameters.
      If so, these same parameters are used for all spectra.
    - A list of parameters whose length is n_spectra.
      If so, each successive parameter set is such for each successive spectrum.
    - A generator object that returns parameters for a power spectrum.
      If so, each spectrum has parameters pulled 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.

    Gaussian Parameters:

    - Each gaussian description is a set of three values:

      * mean (Center Frequency), height (Power), and standard deviation (Bandwidth).
      * Make sure any center frequencies you request are within the simulated frequency range.

    Examples
    --------
    Generate 2 power spectra using the same parameters:

    >>> freqs, psds, _ = gen_group_power_spectra(2, [1, 50], [0, 2], [10, 1, 1])

    Generate 10 power spectra, randomly sampling possible parameters:

    >>> ap_opts = param_sampler([[0, 1.0], [0, 1.5], [0, 2]])
    >>> gauss_opts = param_sampler([[], [10, 1, 1], [10, 1, 1, 20, 2, 1]])
    >>> freqs, psds, sim_params = gen_group_power_spectra(10, [1, 50], ap_opts, gauss_opts)
    """

    # Initialize things
    freqs = gen_freqs(freq_range, freq_res)
    powers = np.zeros([n_spectra, len(freqs)])
    sim_params = [None] * n_spectra

    # Check if inputs are generators, if not, make them into repeat generators
    aperiodic_params = check_iter(aperiodic_params, n_spectra)
    gaussian_params = check_iter(gaussian_params, n_spectra)
    nlvs = check_iter(nlvs, n_spectra)

    # Simulate power spectra
    for ind, bgp, gp, nlv in zip(range(n_spectra), aperiodic_params,
                                 gaussian_params, nlvs):

        sim_params[ind] = SimParams(bgp.copy(), sorted(group_three(gp)), nlv)
        powers[ind, :] = gen_power_vals(freqs, bgp, gp, nlv)

    return freqs, powers, sim_params
fm.report(freqs, spectrum)

###################################################################################################
#
# The synthetic definition is defined in terms of Gaussian parameters (these are
# slightly different from the peak parameters - see the note in tutorial 02).
#
# We can compare how FOOOF, with our updated settings, compares to the
# ground truth of the synthetic spectrum.
#

###################################################################################################

# Compare ground truth synthetic parameters to model fit results
print('Ground Truth \t\t FOOOF Reconstructions')
for sy, fi in zip(np.array(group_three(gauss_params)), fm._gaussian_params):
    print('{:5.2f} {:5.2f} {:5.2f} \t {:5.2f} {:5.2f} {:5.2f}'.format(
        *sy, *fi))

###################################################################################################
# Power Spectra with No Peaks
# ---------------------------
#
# A known case in which FOOOF can overfit is in power spectra in which no peaks
# are present. In this case, the standard deviation can be very low, and so the
# relative peak height check (`min_peak_threshold`) is very liberal at keeping gaussian fits.
#
# If you expect, or know, you have power spectra without peaks in your data,
# we therefore recommend making sure you set some value for `min_peak_height`,
# as otherwise FOOOF is unlikely to appropriately fit power spectra as having
# no peaks. Setting this value requires checking the scale of your power spectra,