def plot_spectral_error(freqs, error, shade=None, log_freqs=False, ax=None, plot_style=style_spectrum_plot, **plot_kwargs): """Plot frequency by frequency error values. Parameters ---------- freqs : 1d array Frequency values, to be plotted on the x-axis. error : 1d array Calculated error values or mean error values across frequencies, to plot on the y-axis. shade : 1d array, optional Values to shade in around the plotted error. This could be, for example, the standard deviation of the errors. log_freqs : bool, optional, default: False Whether to plot the frequency axis in log spacing. ax : matplotlib.Axes, optional Figure axes upon which to plot. plot_style : callable, optional, default: style_spectrum_plot A function to call to apply styling & aesthetics to the plot. **plot_kwargs Keyword arguments to be passed to `plot_spectra` or to the plot call. """ ax = check_ax(ax, plot_kwargs.pop('figsize', PLT_FIGSIZES['spectral'])) plt_freqs = np.log10(freqs) if log_freqs else freqs plot_spectrum(plt_freqs, error, plot_style=None, ax=ax, linewidth=3, **plot_kwargs) if np.any(shade): ax.fill_between(plt_freqs, error - shade, error + shade, alpha=0.25) ymin, ymax = ax.get_ylim() if ymin < 0: ax.set_ylim([0, ymax]) ax.set_xlim(plt_freqs.min(), plt_freqs.max()) check_n_style(plot_style, ax, log_freqs, True) ax.set_ylabel('Absolute Error')
def plot_peak_iter(fm): """Plots a series of plots illustrating the peak search from a flattened spectrum. Parameters ---------- fm : FOOOF() object FOOOF object, with model fit, data and settings available. """ flatspec = fm._spectrum_flat n_gauss = fm._gaussian_params.shape[0] ylims = [ min(flatspec) - 0.1 * np.abs(min(flatspec)), max(flatspec) + 0.1 * max(flatspec) ] for ind in range(n_gauss + 1): # Note: this forces to create a new plotting axes per iteration ax = check_ax(None) plot_spectrum(fm.freqs, flatspec, linewidth=2.0, label='Flattened Spectrum', ax=ax) plot_spectrum(fm.freqs, [fm.peak_threshold * np.std(flatspec)] * len(fm.freqs), color='orange', linestyle='dashed', label='Relative Threshold', ax=ax) plot_spectrum(fm.freqs, [fm.min_peak_height] * len(fm.freqs), color='red', linestyle='dashed', label='Absolute Threshold', ax=ax) maxi = np.argmax(flatspec) ax.plot(fm.freqs[maxi], flatspec[maxi], '.', markersize=24) ax.set_ylim(ylims) ax.set_title('Iteration #' + str(ind + 1), fontsize=16) if ind < n_gauss: gauss = gaussian_function(fm.freqs, *fm._gaussian_params[ind, :]) plot_spectrum(fm.freqs, gauss, label='Gaussian Fit', linestyle=':', linewidth=2.0, ax=ax) flatspec = flatspec - gauss
def plot_fm(fm, plt_log=False, save_fig=False, file_name='FOOOF_fit', file_path=None, ax=None): """Plot the original power spectrum, and full model fit from FOOOF object. Parameters ---------- fm : FOOOF() object FOOOF object, containing a power spectrum and (optionally) results from fitting. plt_log : boolean, optional Whether or not to plot the frequency axis in log space. default: False save_fig : boolean, optional Whether to save out a copy of the plot. default : False file_name : str, optional Name to give the saved out file. file_path : str, optional Path to directory in which to save. If None, saves to current directory. ax : matplotlib.Axes, optional Figure axes upon which to plot. """ if not np.all(fm.freqs): raise RuntimeError('No data available to plot - can not proceed.') ax = check_ax(ax) # Log Plot Settings - note that power values in FOOOF objects are already logged log_freqs = plt_log log_powers = False # Create the plot, adding data as is available if np.any(fm.power_spectrum): plot_spectrum(fm.freqs, fm.power_spectrum, log_freqs, log_powers, ax, color='k', linewidth=1.25, label='Original Spectrum') if np.any(fm.fooofed_spectrum_): plot_spectrum(fm.freqs, fm.fooofed_spectrum_, log_freqs, log_powers, ax, color='r', linewidth=3.0, alpha=0.5, label='Full Model Fit') plot_spectrum(fm.freqs, fm._ap_fit, log_freqs, log_powers, ax, color='b', linestyle='dashed', linewidth=3.0, alpha=0.5, label='Aperiodic Fit') # Save out figure, if requested if save_fig: plt.savefig(fpath(file_path, fname(file_name, 'png')))
# 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) ############################################################################### # # The initial fit, as above, is used to create a flattened spectrum, from which peaks can be extracted. ############################################################################### # Flatten the power spectrum, by subtracting out the initial aperiodic fit
# Settings & parameters for creating a simulated power spectrum freq_range = [3, 40] # The frequency range to simulate aperiodic_params = [1, 1] # Parameters defining the aperiodic component periodic_params = [10, 0.3, 1] # Parameters for any periodic components ################################################################################################### # Generate a simulated power spectrum freqs, powers = gen_power_spectrum(freq_range, aperiodic_params, periodic_params) ################################################################################################### # Plot the simulated power spectrum plot_spectrum(freqs, powers, log_freqs=True, log_powers=False) ################################################################################################### # Simulating With Different Parameters # ------------------------------------ # # Power spectra can be simulated with any desired parameters in the power spectrum model. # # The aperiodic mode for the simulated power spectrum is inferred from the parameters provided. # If two parameters are provided, this is interpreted as [offset, exponent] for simulating # a power spectra with a 'fixed' aperiodic component. If three parameters are provided, as in # the example below, this is interpreted as [offset, knee, exponent] for a 'knee' spectrum. # # Power spectra can also be simulated with any number of peaks. Peaks can be listed in a flat # list with [center frequency, height, bandwidth] listed for as many peaks as you would # like to add, or as a list of lists containing the same information.
fs, ps, _ = gen_group_power_spectra(2, [3, 40], [[0.75, 1.5], [0.25, 1]], [6, 0.2, 1, 10, 0.3, 1, 25, 0.15, 3]) ps1, ps2 = ps ################################################################################################### # The FOOOF plotting module has plots for plotting single or multiple # power spectra, options for plotting in linear or log space, and # plots for shading frequency regions of interest. # # Plotting in FOOOF uses matplotlib. Plotting functions can also take in any # matplotlib keyword arguments, that will be passed into the plot call. ################################################################################################### # Create a spectrum plot with a single power spectrum plot_spectrum(fs, ps2, log_powers=True) ################################################################################################### # Plot multiple spectra on the same plot plot_spectra(fs, ps, log_freqs=True, log_powers=True) ################################################################################################### # Plot a single power spectrum, with a shaded region covering alpha plot_spectrum_shading(fs, ps1, [8, 12], log_powers=True) ################################################################################################### # Plot multiple power spectra, with shades covering theta & beta ranges plot_spectra_shading(fs, ps, [[4, 8], [20, 30]], log_powers=True)
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)
def main(): ## Individual Model Plot # Download examples data files needed for this example freqs = load_fooof_data('freqs.npy', folder='data') spectrum = load_fooof_data('spectrum.npy', folder='data') # Initialize and fit an example power spectrum model fm = FOOOF(peak_width_limits=[1, 6], max_n_peaks=6, min_peak_height=0.2, verbose=False) fm.fit(freqs, spectrum, [3, 40]) # Save out the report fm.save_report('FOOOF_report.png', 'img') ## Group Plot # Download examples data files needed for this example freqs = load_fooof_data('group_freqs.npy', folder='data') spectra = load_fooof_data('group_powers.npy', folder='data') # Initialize and fit a group of example power spectrum models fg = FOOOFGroup(peak_width_limits=[1, 6], max_n_peaks=6, min_peak_height=0.2, verbose=False) fg.fit(freqs, spectra, [3, 30]) # Save out the report fg.save_report('FOOOFGroup_report.png', 'img') ## Make the icon plot # Simulate an example power spectrum fs, ps = gen_power_spectrum([4, 35], [0, 1], [[10, 0.3, 1], [22, 0.15, 1.25]], nlv=0.01) def custom_style(ax, log_freqs, log_powers): """Custom styler-function for the icon plot.""" # Set the top and right side frame & ticks off ax.spines['right'].set_visible(False) ax.spines['top'].set_visible(False) # Set linewidth of remaining spines ax.spines['left'].set_linewidth(10) ax.spines['bottom'].set_linewidth(10) ax.set_xticks([], []) ax.set_yticks([], []) # Create and save out the plot plot_spectrum(fs, ps, log_freqs=False, log_powers=True, lw=12, alpha=0.8, color='grey', plot_style=custom_style, ax=check_ax(None, [6, 6])) plt.tight_layout() plt.savefig('img/spectrum.png') ## Clean Up # Remove the data folder shutil.rmtree('data')
################################################################################################### # # In the topography above, we can see that there is a fair amount of variation # across the scalp in terms of aperiodic exponent value, and there seems to be some # spatial structure to it. # # To visualize how much the exponent values vary, we can again plot some example power # spectra, in this case extracting those with the highest and lower exponent values. # ################################################################################################### # Compare the power spectra between low and high exponent channels fig, ax = plt.subplots(figsize=(8, 6)) plot_spectrum(fg.freqs, fg.get_fooof(np.argmin(exps)).power_spectrum, ax=ax, label='Low Exponent') plot_spectrum(fg.freqs, fg.get_fooof(np.argmax(exps)).power_spectrum, ax=ax, label='High Exponent') ################################################################################################### # Conclusion # ---------- # # In this example, we have seen how to apply power spectrum models to data that is # managed and processed with MNE. # ###################################################################################################
# --------------------------------- # # First we will start with the core plotting function that plots an individual power spectrum. # # The :func:`~.plot_spectrum` function takes in an array of frequency values and a 1d array of # of power values, and plots the corresponding power spectrum. # # This function, as all the functions in the plotting module, takes in optional inputs # `log_freqs` and `log_powers` that control whether the frequency and power axes # are plotted in log space. # ################################################################################################### # Create a spectrum plot with a single power spectrum plot_spectrum(freqs, powers1, log_powers=True) ################################################################################################### # Plotting Multiple Power Spectra # ------------------------------- # # The :func:`~.plot_spectra` function takes in one or more frequency arrays and one or more # array of associated power values and plots multiple power spectra. # # Note that the inputs for either can be either 2d arrays, or lists of 1d arrays. You can also # pass in additional optional inputs including `labels`, to specify labels to use in a plot # legend, and `colors` to specify which colors to plot each spectrum in. # ###################################################################################################
def plot_fm(fm, plot_peaks=None, plot_aperiodic=True, plt_log=False, add_legend=True, save_fig=False, file_name=None, file_path=None, ax=None, plot_style=style_spectrum_plot, data_kwargs=None, model_kwargs=None, aperiodic_kwargs=None, peak_kwargs=None): """Plot the power spectrum and model fit results from a FOOOF object. Parameters ---------- fm : FOOOF Object containing a power spectrum and (optionally) results from fitting. plot_peaks : None or {'shade', 'dot', 'outline', 'line'}, optional What kind of approach to take to plot peaks. If None, peaks are not specifically plotted. Can also be a combination of approaches, separated by '-', for example: 'shade-line'. plot_aperiodic : boolean, optional, default: True Whether to plot the aperiodic component of the model fit. plt_log : boolean, optional, default: False Whether to plot the frequency values in log10 spacing. add_legend : boolean, optional, default: False Whether to add a legend describing the plot components. save_fig : bool, optional, default: False Whether to save out a copy of the plot. file_name : str, optional Name to give the saved out file. file_path : str, optional Path to directory to save to. If None, saves to current directory. ax : matplotlib.Axes, optional Figure axes upon which to plot. plot_style : callable, optional, default: style_spectrum_plot A function to call to apply styling & aesthetics to the plot. data_kwargs, model_kwargs, aperiodic_kwargs, peak_kwargs : None or dict, optional Keyword arguments to pass into the plot call for each plot element. Notes ----- Since FOOOF objects store power values in log spacing, the y-axis (power) is plotted in log spacing by default. """ ax = check_ax(ax, PLT_FIGSIZES['spectral']) # Log settings - note that power values in FOOOF objects are already logged log_freqs = plt_log log_powers = False # Plot the data, if available if fm.has_data: data_kwargs = check_plot_kwargs(data_kwargs, \ {'color' : PLT_COLORS['data'], 'linewidth' : 2.0, 'label' : 'Original Spectrum' if add_legend else None}) plot_spectrum(fm.freqs, fm.power_spectrum, log_freqs, log_powers, ax=ax, plot_style=None, **data_kwargs) # Add the full model fit, and components (if requested) if fm.has_model: model_kwargs = check_plot_kwargs(model_kwargs, \ {'color' : PLT_COLORS['model'], 'linewidth' : 3.0, 'alpha' : 0.5, 'label' : 'Full Model Fit' if add_legend else None}) plot_spectrum(fm.freqs, fm.fooofed_spectrum_, log_freqs, log_powers, ax=ax, plot_style=None, **model_kwargs) # Plot the aperiodic component of the model fit if plot_aperiodic: aperiodic_kwargs = check_plot_kwargs(aperiodic_kwargs, \ {'color' : PLT_COLORS['aperiodic'], 'linewidth' : 3.0, 'alpha' : 0.5, 'linestyle' : 'dashed', 'label' : 'Aperiodic Fit' if add_legend else None}) plot_spectrum(fm.freqs, fm._ap_fit, log_freqs, log_powers, ax=ax, plot_style=None, **aperiodic_kwargs) # Plot the periodic components of the model fit if plot_peaks: _add_peaks(fm, plot_peaks, plt_log, ax=ax, peak_kwargs=peak_kwargs) # Apply style to plot check_n_style(plot_style, ax, log_freqs, True) # Save out figure, if requested if save_fig: if not file_name: raise ValueError( "Input 'file_name' is required to save out the plot.") plt.savefig(fpath(file_path, fname(file_name, 'png')))
# # Plotted below is an example power spectrum, plotted in semi-log space (log10 power values # and linear frequencies). This is our data, that we will be trying to model. # # In the plot, we see a power spectrum in which there is decreasing power across increasing # frequencies. In some frequency regions, there is a 'peak' of power, over and above the general # trend across frequencies. These properties - a pattern of decreasing power across frequencies, # with overlying peaks - are considered to be hallmarks of neural field data. # ################################################################################################### # Plot one of the example power spectra plot_spectrum(freqs1, powers1, log_powers=True, color='black', label='Original Spectrum') ################################################################################################### # Conceptual Overview # ------------------- # # The goal of this module is to fit models to parameterize neural power spectra. # # One reason to do so is the idea that there are multiple distinct 'components' within # neural field data. The model is therefore designed to measure these different # 'components' of the data. # # By components, we mean that we are going to conceptualize neural field data as consisting # of a combination of periodic (oscillatory) and aperiodic activity. Restated, we could say
# Step 1: Initial Aperiodic Fit # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ # # We start by taking an initial aperiodic fit. This goal of this fit is to get an initial # fit that is good enough to get started with the fitting process. # ################################################################################################### # Do an initial aperiodic 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', color='black', ax=ax) plot_spectrum(fm.freqs, init_ap_fit, plt_log, label='Initial Aperiodic Fit', color='blue', alpha=0.5, linestyle='dashed', ax=ax) ################################################################################################### # Step 2: Flatten the Spectrum # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ # # The initial fit is then used to create a flattened spectrum. # # The initial aperiodic fit is subtracted out from the original data, leaving a flattened # version of the data which no longer contains the aperiodic component. # ###################################################################################################