Exemple #1
0
    def _regenerate_model(self):
        """Regenerate model fit from parameters."""

        self._ap_fit = gen_aperiodic(self.freqs, self.aperiodic_params_)
        self._peak_fit = gen_peaks(self.freqs,
                                   np.ndarray.flatten(self.gaussian_params_))
        self.fooofed_spectrum_ = self._peak_fit + self._ap_fit
Exemple #2
0
def refit_aperiodic(freqs, powers, peak_fit):
    """Refit the aperiodic component following the periodic refit.

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

    Returns
    -------
    ap_params : 1d array
        Exponent and offset values.
    ap_fit : 1d array
        Regenerated aperiodic fit.
    """
    # Access simple ap fit method
    _fm = FOOOF()
    _fm.power_spectrum = powers
    _fm.freqs = freqs

    # Remove peaks
    spectrum_peak_rm = powers - peak_fit

    # Refit
    ap_params = _fm._simple_ap_fit(freqs, spectrum_peak_rm)

    ap_fit = gen_aperiodic(freqs, ap_params)

    return ap_params, ap_fit
Exemple #3
0
    def _robust_ap_fit(self, freqs, power_spectrum):
        """Fit the aperiodic component of the power spectrum robustly, ignoring outliers.

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

        Returns
        -------
        aperiodic_params : 1d array
            Parameter estimates for aperiodic fit.
        """

        # Do a quick, initial aperiodic fit
        popt = self._simple_ap_fit(freqs, power_spectrum)
        initial_fit = gen_aperiodic(freqs, popt)

        # Flatten power_spectrum based on initial aperiodic fit
        flatspec = power_spectrum - initial_fit

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

        # Use percential threshold, in terms of # of points, to extract and re-fit
        perc_thresh = np.percentile(flatspec, self._ap_percentile_thresh)
        perc_mask = flatspec <= perc_thresh
        freqs_ignore = freqs[perc_mask]
        spectrum_ignore = power_spectrum[perc_mask]

        # Second aperiodic fit - using results of first fit as guess parameters
        #  See note in _simple_ap_fit about warnings
        with warnings.catch_warnings():
            warnings.simplefilter("ignore")
            aperiodic_params, _ = curve_fit(get_ap_func(self.aperiodic_mode),
                                            freqs_ignore,
                                            spectrum_ignore,
                                            p0=popt,
                                            maxfev=5000,
                                            bounds=self._ap_bounds)

        return aperiodic_params
Exemple #4
0
def plot_aperiodic_fits(aps,
                        freq_range,
                        control_offset=False,
                        log_freqs=False,
                        colors=None,
                        labels=None,
                        ax=None,
                        **plot_kwargs):
    """Plot reconstructions of model aperiodic fits.

    Parameters
    ----------
    aps : 2d array
        Aperiodic parameters. Each row is a parameter set, as [Off, Exp] or [Off, Knee, Exp].
    freq_range : list of [float, float]
        The frequency range to plot the peak fits across, as [f_min, f_max].
    control_offset : boolean, optional, default: False
        Whether to control for the offset, by setting it to zero.
    log_freqs : boolean, optional, default: False
        Whether to plot the x-axis in log space.
    colors : str or list of str, optional
        Color(s) to plot data.
    labels : list of str, optional
        Label(s) for plotted data, to be added in a legend.
    ax : matplotlib.Axes, optional
        Figure axes upon which to plot.
    **plot_kwargs
        Keyword arguments to pass into the ``style_plot``.
    """

    ax = check_ax(ax, plot_kwargs.pop('figsize', PLT_FIGSIZES['params']))

    if isinstance(aps, list):

        if not colors:
            colors = cycle(plt.rcParams['axes.prop_cycle'].by_key()['color'])

        recursive_plot(aps,
                       plot_aperiodic_fits,
                       ax=ax,
                       freq_range=tuple(freq_range),
                       control_offset=control_offset,
                       log_freqs=log_freqs,
                       colors=colors,
                       labels=labels,
                       **plot_kwargs)
    else:

        freqs = gen_freqs(freq_range, 0.1)
        plt_freqs = np.log10(freqs) if log_freqs else freqs

        colors = colors[0] if isinstance(colors, list) else colors

        avg_vals = np.zeros(shape=[len(freqs)])

        for ap_params in aps:

            if control_offset:

                # Copy the object to not overwrite any data
                ap_params = ap_params.copy()
                ap_params[0] = 0

            # Recreate & plot the aperiodic component from parameters
            ap_vals = gen_aperiodic(freqs, ap_params)

            ax.plot(plt_freqs,
                    ap_vals,
                    color=colors,
                    alpha=0.35,
                    linewidth=1.25)

            # Collect a running average across components
            avg_vals = np.nansum(np.vstack([avg_vals, ap_vals]), axis=0)

        # Plot the average component
        avg = avg_vals / aps.shape[0]
        avg_color = 'black' if not colors else colors
        ax.plot(plt_freqs, avg, linewidth=3.75, color=avg_color, label=labels)

    # Add axis labels
    ax.set_xlabel('log(Frequency)' if log_freqs else 'Frequency')
    ax.set_ylabel('log(Power)')

    # Set plot limit
    ax.set_xlim(np.log10(freq_range) if log_freqs else freq_range)

    style_param_plot(ax)
Exemple #5
0
###################################################################################################
#
# 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 fit, that excludes outliers
#  This recreates an initial fit that isn't ultimately stored in the FOOOF object)
init_ap_fit = gen_aperiodic(fm.freqs,
                            fm._robust_ap_fit(fm.freqs, fm.power_spectrum))

# Plot the initial aperiodic 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_ap_fit,
              plt_log,
              label='Initial Aperiodic Fit',
              ax=ax)

###################################################################################################
Exemple #6
0
def plot_annotated_peak_search(fm, plot_style=style_spectrum_plot):
    """Plot a series of plots illustrating the peak search from a flattened spectrum.

    Parameters
    ----------
    fm : FOOOF
        FOOOF object, with model fit, data and settings available.
    plot_style : callable, optional, default: style_spectrum_plot
        A function to call to apply styling & aesthetics to the plots.
    """

    # Recalculate the initial aperiodic fit and flattened spectrum that
    #   is the same as the one that is used in the peak fitting procedure
    flatspec = fm.power_spectrum - \
        gen_aperiodic(fm.freqs, fm._robust_ap_fit(fm.freqs, fm.power_spectrum))

    # Calculate ylims of the plot that are scaled to the range of the data
    ylims = [
        min(flatspec) - 0.1 * np.abs(min(flatspec)),
        max(flatspec) + 0.1 * max(flatspec)
    ]

    # Loop through the iterative search for each peak
    for ind in range(fm.n_peaks_ + 1):

        # This forces the creation of a new plotting axes per iteration
        ax = check_ax(None, PLT_FIGSIZES['spectral'])

        plot_spectrum(fm.freqs,
                      flatspec,
                      ax=ax,
                      plot_style=None,
                      label='Flattened Spectrum',
                      color=PLT_COLORS['data'],
                      linewidth=2.5)
        plot_spectrum(fm.freqs,
                      [fm.peak_threshold * np.std(flatspec)] * len(fm.freqs),
                      ax=ax,
                      plot_style=None,
                      label='Relative Threshold',
                      color='orange',
                      linewidth=2.5,
                      linestyle='dashed')
        plot_spectrum(fm.freqs, [fm.min_peak_height] * len(fm.freqs),
                      ax=ax,
                      plot_style=None,
                      label='Absolute Threshold',
                      color='red',
                      linewidth=2.5,
                      linestyle='dashed')

        maxi = np.argmax(flatspec)
        ax.plot(fm.freqs[maxi],
                flatspec[maxi],
                '.',
                color=PLT_COLORS['periodic'],
                alpha=0.75,
                markersize=30)

        ax.set_ylim(ylims)
        ax.set_title('Iteration #' + str(ind + 1), fontsize=16)

        if ind < fm.n_peaks_:

            gauss = gaussian_function(fm.freqs, *fm.gaussian_params_[ind, :])
            plot_spectrum(fm.freqs,
                          gauss,
                          ax=ax,
                          plot_style=None,
                          label='Gaussian Fit',
                          color=PLT_COLORS['periodic'],
                          linestyle=':',
                          linewidth=3.0)

            flatspec = flatspec - gauss

        check_n_style(plot_style, ax, False, True)
Exemple #7
0
    def fit(self, freqs=None, power_spectrum=None, freq_range=None):
        """Fit the full power spectrum as a combination of periodic and aperiodic components.

        Parameters
        ----------
        freqs : 1d array, optional
            Frequency values for the power spectrum, in linear space.
        power_spectrum : 1d array, optional
            Power values, which must be input 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 freqs is not None and power_spectrum is not None:
            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 self.freqs is None or self.power_spectrum is None:
            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 aperiodic component
            self.aperiodic_params_ = self._robust_ap_fit(
                self.freqs, self.power_spectrum)
            self._ap_fit = gen_aperiodic(self.freqs, self.aperiodic_params_)

            # Flatten the power_spectrum using fit aperiodic fit
            self._spectrum_flat = self.power_spectrum - self._ap_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 aperiodic fit on peak-removed power spectrum
            #   Note: This overwrites previous aperiodic fit
            self.aperiodic_params_ = self._simple_ap_fit(
                self.freqs, self._spectrum_peak_rm)
            self._ap_fit = gen_aperiodic(self.freqs, self.aperiodic_params_)

            # Create full power_spectrum model fit
            self.fooofed_spectrum_ = self._peak_fit + self._ap_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.')