Esempio n. 1
0
    def _regenerate_model(self):
        """Regenerate model fit from parameters."""

        self._bg_fit = gen_background(self.freqs, self.background_params_)
        self._peak_fit = gen_peaks(self.freqs,
                                   np.ndarray.flatten(self._gaussian_params))
        self.fooofed_spectrum_ = self._peak_fit + self._bg_fit
Esempio n. 2
0
    def _robust_bg_fit(self, freqs, power_spectrum):
        """Fit the 1/f background of power spectrum robustly, ignoring outliers.

        Parameters
        ----------
        freqs : 1d array
            Frequency values for the power spectrum, in linear scale.
        power_spectrum : 1d array
            Power spectrum values, in log10 scale.

        Returns
        -------
        background_params : 1d array
            Parameter estimates for background fit.
        """

        # Do a quick, initial background fit.
        popt = self._simple_bg_fit(freqs, power_spectrum)
        initial_fit = gen_background(freqs, popt)

        # Flatten power_spectrum based on initial background fit.
        flatspec = power_spectrum - initial_fit

        # Flatten outliers - any points that drop below 0.
        flatspec[flatspec < 0] = 0

        # Amplitude threshold - in terms of # of points.
        perc_thresh = np.percentile(flatspec, self._bg_amp_thresh)
        amp_mask = flatspec <= perc_thresh
        freqs_ignore = freqs[amp_mask]
        spectrum_ignore = power_spectrum[amp_mask]

        # Second background fit - using results of first fit as guess parameters.
        #  See note in _simple_bg_fit about warnings
        with warnings.catch_warnings():
            warnings.simplefilter("ignore")
            background_params, _ = curve_fit(get_bg_func(self.background_mode),
                                             freqs_ignore,
                                             spectrum_ignore,
                                             p0=popt,
                                             maxfev=5000,
                                             bounds=self._bg_bounds)

        return background_params
Esempio n. 3
0
fm.plot(plt_log)

###############################################################################
#
# The FOOOF object stores most of the intermediate steps internally.
#
# For this notebook, we will first fit the full model, as normal, but then step through, and visualize each step the algorithm takes to come to that final fit.

# Fit the FOOOF model.
fm.fit(freqs, spectrum, [3, 40])

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

# Do an initial aperiodic signal fit - a robust background fit (excluding outliers)
#  This recreates an initial fit that isn't ultimately stored in the FOOOF object)
init_bg_fit = gen_background(fm.freqs,
                             fm._robust_bg_fit(fm.freqs, fm.power_spectrum))

# Plot the initial aperiodic 'background' fit
_, ax = plt.subplots(figsize=(12, 10))
plot_spectrum(fm.freqs,
              fm.power_spectrum,
              plt_log,
              label='Original Power Spectrum',
              ax=ax)
plot_spectrum(fm.freqs,
              init_bg_fit,
              plt_log,
              label='Initial Background Fit',
              ax=ax)

###############################################################################
Esempio n. 4
0
    def fit(self, freqs=None, power_spectrum=None, freq_range=None):
        """Fit the full power spectrum as a combination of background and peaks.

        Parameters
        ----------
        freqs : 1d array, optional
            Frequency values for the power spectrum, in linear space.
        power_spectrum : 1d array, optional
            Power spectrum values, in linear space.
        freq_range : list of [float, float], optional
            Frequency range to restrict power spectrum to. If not provided, keeps the entire range.

        Notes
        -----
        Data is optional if data has been already been added to FOOOF object.
        """

        # If freqs & power_spectrum provided together, add data to object.
        if isinstance(freqs, np.ndarray) and isinstance(
                power_spectrum, np.ndarray):
            self.add_data(freqs, power_spectrum, freq_range)
        # If power spectrum provided alone, add to object, and use existing frequency data
        #  Note: be careful passing in power_spectrum data like this:
        #    It assumes the power_spectrum is already logged, with correct freq_range.
        elif isinstance(power_spectrum, np.ndarray):
            self.power_spectrum = power_spectrum

        # Check that data is available
        if not (np.all(self.freqs) and np.all(self.power_spectrum)):
            raise ValueError('No data available to fit - can not proceed.')

        # Check and warn about width limits (if in verbose mode)
        if self.verbose:
            self._check_width_limits()

        # In rare cases, the model fails to fit. Therefore it's in a try/except
        #  Cause of failure: RuntimeError, failure to find parameters in curve_fit
        try:

            # Fit the background 1/f.
            self.background_params_ = self._robust_bg_fit(
                self.freqs, self.power_spectrum)
            self._bg_fit = gen_background(self.freqs, self.background_params_)

            # Flatten the power_spectrum using fit background.
            self._spectrum_flat = self.power_spectrum - self._bg_fit

            # Find peaks, and fit them with gaussians.
            self._gaussian_params = self._fit_peaks(
                np.copy(self._spectrum_flat))

            # Calculate the peak fit.
            #  Note: if no peaks are found, this creates a flat (all zero) peak fit.
            self._peak_fit = gen_peaks(
                self.freqs, np.ndarray.flatten(self._gaussian_params))

            # Create peak-removed (but not flattened) power spectrum.
            self._spectrum_peak_rm = self.power_spectrum - self._peak_fit

            # Run final background fit on peak-removed power spectrum.
            #   Note: This overwrites previous background fit.
            self.background_params_ = self._simple_bg_fit(
                self.freqs, self._spectrum_peak_rm)
            self._bg_fit = gen_background(self.freqs, self.background_params_)

            # Create full power_spectrum model fit.
            self.fooofed_spectrum_ = self._peak_fit + self._bg_fit

            # Convert gaussian definitions to peak parameters
            self.peak_params_ = self._create_peak_params(self._gaussian_params)

            # Calculate R^2 and error of the model fit.
            self._calc_r_squared()
            self._calc_rmse_error()

        # Catch failure, stemming from curve_fit process
        except RuntimeError:

            # Clear any interim model results that may have run
            #  Partial model results shouldn't be interpreted in light of overall failure
            self._reset_data_results(clear_freqs=False,
                                     clear_spectrum=False,
                                     clear_results=True)

            # Print out status
            if self.verbose:
                print('Model fitting was unsuccessful.')