def _add_peaks_outline(fm, plt_log, ax, **plot_kwargs): """Add an outline of each peak. Parameters ---------- fm : FOOOF FOOOF object containing results from fitting. plt_log : boolean Whether to plot the frequency values in log10 spacing. ax : matplotlib.Axes Figure axes upon which to plot. **plot_kwargs Keyword arguments to pass into the plot call. """ defaults = {'color': PLT_COLORS['periodic'], 'alpha': 0.7, 'lw': 1.5} plot_kwargs = check_plot_kwargs(plot_kwargs, defaults) for peak in fm.gaussian_params_: # Define the frequency range around each peak to plot - peak bandwidth +/- 3 peak_range = [peak[0] - peak[2] * 3, peak[0] + peak[2] * 3] # Generate a peak reconstruction for each peak, and trim to desired range peak_line = fm._ap_fit + gen_periodic(fm.freqs, peak) peak_freqs, peak_line = trim_spectrum(fm.freqs, peak_line, peak_range) # Plot the peak outline peak_freqs = np.log10(peak_freqs) if plt_log else peak_freqs ax.plot(peak_freqs, peak_line, **plot_kwargs)
def _add_peaks_line(fm, plt_log, ax, **plot_kwargs): """Add a long line, from the top of the plot, down through the peak, with an arrow at the top. Parameters ---------- fm : FOOOF FOOOF object containing results from fitting. plt_log : boolean Whether to plot the frequency values in log10 spacing. ax : matplotlib.Axes Figure axes upon which to plot. **plot_kwargs Keyword arguments to pass into the plot call. """ defaults = { 'color': PLT_COLORS['periodic'], 'alpha': 0.7, 'lw': 1.4, 'ms': 10 } plot_kwargs = check_plot_kwargs(plot_kwargs, defaults) ylims = ax.get_ylim() for peak in fm.peak_params_: freq_point = np.log10(peak[0]) if plt_log else peak[0] ax.plot([freq_point, freq_point], ylims, '-', **plot_kwargs) ax.plot(freq_point, ylims[1], 'v', **plot_kwargs)
def _add_peaks_dot(fm, plt_log, ax, **plot_kwargs): """Add a short line, from aperiodic to peak, with a dot at the top. Parameters ---------- fm : FOOOF FOOOF object containing results from fitting. plt_log : boolean Whether to plot the frequency values in log10 spacing. ax : matplotlib.Axes Figure axes upon which to plot. **plot_kwargs Keyword arguments to pass into the plot call. """ defaults = { 'color': PLT_COLORS['periodic'], 'alpha': 0.6, 'lw': 2.5, 'ms': 6 } plot_kwargs = check_plot_kwargs(plot_kwargs, defaults) for peak in fm.peak_params_: ap_point = np.interp(peak[0], fm.freqs, fm._ap_fit) freq_point = np.log10(peak[0]) if plt_log else peak[0] # Add the line from the aperiodic fit up the tip of the peak ax.plot([freq_point, freq_point], [ap_point, ap_point + peak[1]], **plot_kwargs) # Add an extra dot at the tip of the peak ax.plot(freq_point, ap_point + peak[1], marker='o', **plot_kwargs)
def _add_peaks_shade(fm, plt_log, ax, **plot_kwargs): """Add a shading in of all peaks. Parameters ---------- fm : FOOOF FOOOF object containing results from fitting. plt_log : boolean Whether to plot the frequency values in log10 spacing. ax : matplotlib.Axes Figure axes upon which to plot. **plot_kwargs Keyword arguments to pass into the plot call. """ kwargs = check_plot_kwargs(plot_kwargs, { 'color': PLT_COLORS['periodic'], 'alpha': 0.25 }) for peak in fm.gaussian_params_: peak_freqs = np.log10(fm.freqs) if plt_log else fm.freqs peak_line = fm._ap_fit + gen_periodic(fm.freqs, peak) ax.fill_between(peak_freqs, peak_line, fm._ap_fit, **kwargs)
def plot_peak_params(peaks, freq_range=None, colors=None, labels=None, ax=None, plot_style=style_param_plot, **plot_kwargs): """Plot peak parameters as dots representing center frequency, power and bandwidth. Parameters ---------- peaks : 2d array or list of 2d array Peak data. Each row is a peak, as [CF, PW, BW]. freq_range : list of [float, float] , optional The frequency range to plot the peak parameters across, as [f_min, f_max]. 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_style : callable, optional, default: style_param_plot A function to call to apply styling & aesthetics to the plot. **plot_kwargs Keyword arguments to pass into the plot call. """ ax = check_ax(ax, plot_kwargs.pop('figsize', PLT_FIGSIZES['params'])) # If there is a list, use recurse function to loop across arrays of data and plot them if isinstance(peaks, list): recursive_plot(peaks, plot_peak_params, ax, colors=colors, labels=labels, plot_style=plot_style, **plot_kwargs) # Otherwise, plot the array of data else: # Unpack data: CF as x; PW as y; BW as size xs, ys = peaks[:, 0], peaks[:, 1] sizes = peaks[:, 2] * plot_kwargs.pop('s', 150) # Create the plot plot_kwargs = check_plot_kwargs(plot_kwargs, {'alpha': 0.7}) ax.scatter(xs, ys, sizes, c=colors, label=labels, **plot_kwargs) # Add axis labels ax.set_xlabel('Center Frequency') ax.set_ylabel('Power') # Set plot limits if freq_range: ax.set_xlim(freq_range) ax.set_ylim([0, ax.get_ylim()[1]]) check_n_style(plot_style, ax)
def plot_spectra(freqs, power_spectra, log_freqs=False, log_powers=False, colors=None, labels=None, ax=None, **plot_kwargs): """Plot one or multiple power spectra. Parameters ---------- freqs : 1d or 2d array or list of 1d array Frequency values, to be plotted on the x-axis. power_spectra : 1d or 2d array or list of 1d array Power values, to be plotted on the y-axis. log_freqs : bool, optional, default: False Whether to plot the frequency axis in log spacing. log_powers : bool, optional, default: False Whether to plot the power axis in log spacing. colors : list of str, optional, default: None Line colors of the spectra. labels : list of str, optional, default: None Legend labels for the spectra. 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['spectral'])) # Create the plot plot_kwargs = check_plot_kwargs(plot_kwargs, {'linewidth': 2.0}) # Make inputs iterable if need to be passed multiple times to plot each spectrum plt_powers = np.reshape(power_spectra, (1, -1)) if np.ndim(power_spectra) == 1 else \ power_spectra plt_freqs = repeat(freqs) if isinstance( freqs, np.ndarray) and freqs.ndim == 1 else freqs # Set labels labels = plot_kwargs.pop('label') if 'label' in plot_kwargs.keys( ) and labels is None else labels labels = repeat(labels) if not isinstance(labels, list) else cycle(labels) colors = repeat(colors) if not isinstance(colors, list) else cycle(colors) # Plot for freqs, powers, color, label in zip(plt_freqs, plt_powers, colors, labels): # Set plot data, logging if requested, and collect color, if absent freqs = np.log10(freqs) if log_freqs else freqs powers = np.log10(powers) if log_powers else powers if color: plot_kwargs['color'] = color ax.plot(freqs, powers, label=label, **plot_kwargs) style_spectrum_plot(ax, log_freqs, log_powers)
def plot_aperiodic_params(aps, colors=None, labels=None, ax=None, plot_style=style_param_plot, **plot_kwargs): """Plot aperiodic parameters as dots representing offset and exponent value. Parameters ---------- aps : 2d array or list of 2d array Aperiodic parameters. Each row is a parameter set, as [Off, Exp] or [Off, Knee, Exp]. 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_style : callable, optional, default: style_param_plot A function to call to apply styling & aesthetics to the plot. **plot_kwargs Keyword arguments to pass into the plot call. """ ax = check_ax(ax, plot_kwargs.pop('figsize', PLT_FIGSIZES['params'])) if isinstance(aps, list): recursive_plot(aps, plot_aperiodic_params, ax, colors=colors, labels=labels, plot_style=plot_style, **plot_kwargs) else: # Unpack data: offset as x; exponent as y xs, ys = aps[:, 0], aps[:, -1] sizes = plot_kwargs.pop('s', 150) plot_kwargs = check_plot_kwargs(plot_kwargs, {'alpha': 0.7}) ax.scatter(xs, ys, sizes, c=colors, label=labels, **plot_kwargs) # Add axis labels ax.set_xlabel('Offset') ax.set_ylabel('Exponent') check_n_style(plot_style, ax)
def _add_peaks_width(fm, plt_log, ax, **plot_kwargs): """Add a line across the width of peaks. Parameters ---------- fm : FOOOF FOOOF object containing results from fitting. plt_log : boolean Whether to plot the frequency values in log10 spacing. ax : matplotlib.Axes Figure axes upon which to plot. **plot_kwargs Keyword arguments to pass into the plot call. Notes ----- This line represents the bandwidth (width or gaussian standard deviation) of the peak, though what is literally plotted is the full-width half-max. """ defaults = { 'color': PLT_COLORS['periodic'], 'alpha': 0.6, 'lw': 2.5, 'ms': 6 } plot_kwargs = check_plot_kwargs(plot_kwargs, defaults) for peak in fm.gaussian_params_: peak_top = fm.power_spectrum[nearest_ind(fm.freqs, peak[0])] bw_freqs = [ peak[0] - 0.5 * compute_fwhm(peak[2]), peak[0] + 0.5 * compute_fwhm(peak[2]) ] if plt_log: bw_freqs = np.log10(bw_freqs) ax.plot(bw_freqs, [peak_top - (0.5 * peak[1]), peak_top - (0.5 * peak[1])], **plot_kwargs)
def plot_spectrum(freqs, power_spectrum, log_freqs=False, log_powers=False, ax=None, plot_style=style_spectrum_plot, **plot_kwargs): """Plot a power spectrum. Parameters ---------- freqs : 1d array Frequency values, to be plotted on the x-axis. power_spectrum : 1d array Power values, to be plotted on the y-axis. log_freqs : bool, optional, default: False Whether to plot the frequency axis in log spacing. log_powers : bool, optional, default: False Whether to plot the power axis in log spacing. ax : matplotlib.Axes, optional Figure axis 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 the plot call. """ ax = check_ax(ax, plot_kwargs.pop('figsize', PLT_FIGSIZES['spectral'])) # Set plot data & labels, logging if requested plt_freqs = np.log10(freqs) if log_freqs else freqs plt_powers = np.log10(power_spectrum) if log_powers else power_spectrum # Create the plot plot_kwargs = check_plot_kwargs(plot_kwargs, {'linewidth': 2.0}) ax.plot(plt_freqs, plt_powers, **plot_kwargs) check_n_style(plot_style, ax, log_freqs, log_powers)
def plot_aperiodic_fits(aps, freq_range, control_offset=False, log_freqs=False, colors=None, labels=None, ax=None, plot_style=style_param_plot, **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, optonal, default: False Whether to control for the offset, by setting it to zero. log_freqs : boolean, optonal, 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_style : callable, optional, default: style_param_plot A function to call to apply styling & aesthetics to the plot. **plot_kwargs Keyword arguments to pass into the plot call. """ 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_function=plot_aperiodic_fits, ax=ax, freq_range=tuple(freq_range), control_offset=control_offset, log_freqs=log_freqs, colors=colors, labels=labels, plot_style=plot_style, **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) plot_kwargs = check_plot_kwargs(plot_kwargs, { 'alpha': 0.35, 'linewidth': 1.25 }) ax.plot(plt_freqs, ap_vals, color=colors, **plot_kwargs) # 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=plot_kwargs.get('linewidth') * 3, 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) # Apply plot style check_n_style(plot_style, ax)
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, data_kwargs=None, model_kwargs=None, aperiodic_kwargs=None, peak_kwargs=None, **plot_kwargs): """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. data_kwargs, model_kwargs, aperiodic_kwargs, peak_kwargs : None or dict, optional Keyword arguments to pass into the plot call for each plot element. **plot_kwargs Keyword arguments to pass into the ``style_plot``. 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_defaults = { 'color': PLT_COLORS['data'], 'linewidth': 2.0, 'label': 'Original Spectrum' if add_legend else None } data_kwargs = check_plot_kwargs(data_kwargs, data_defaults) plot_spectra(fm.freqs, fm.power_spectrum, log_freqs, log_powers, ax=ax, **data_kwargs) # Add the full model fit, and components (if requested) if fm.has_model: model_defaults = { 'color': PLT_COLORS['model'], 'linewidth': 3.0, 'alpha': 0.5, 'label': 'Full Model Fit' if add_legend else None } model_kwargs = check_plot_kwargs(model_kwargs, model_defaults) plot_spectra(fm.freqs, fm.fooofed_spectrum_, log_freqs, log_powers, ax=ax, **model_kwargs) # Plot the aperiodic component of the model fit if plot_aperiodic: aperiodic_defaults = { 'color': PLT_COLORS['aperiodic'], 'linewidth': 3.0, 'alpha': 0.5, 'linestyle': 'dashed', 'label': 'Aperiodic Fit' if add_legend else None } aperiodic_kwargs = check_plot_kwargs(aperiodic_kwargs, aperiodic_defaults) plot_spectra(fm.freqs, fm._ap_fit, log_freqs, log_powers, ax=ax, **aperiodic_kwargs) # Plot the periodic components of the model fit if plot_peaks: _add_peaks(fm, plot_peaks, plt_log, ax, peak_kwargs) # Apply default style to plot style_spectrum_plot(ax, log_freqs, True)
def plot_peak_fits(peaks, freq_range=None, colors=None, labels=None, ax=None, plot_style=style_param_plot, **plot_kwargs): """Plot reconstructions of model peak fits. Parameters ---------- peaks : 2d array Peak data. Each row is a peak, as [CF, PW, BW]. freq_range : list of [float, float] , optional The frequency range to plot the peak fits across, as [f_min, f_max]. If not provided, defaults to +/- 4 around given peak center frequencies. 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_style : callable, optional, default: style_param_plot A function to call to apply styling & aesthetics to the plot. **plot_kwargs Keyword arguments to pass into the plot call. """ ax = check_ax(ax, plot_kwargs.pop('figsize', PLT_FIGSIZES['params'])) if isinstance(peaks, list): if not colors: colors = cycle(plt.rcParams['axes.prop_cycle'].by_key()['color']) recursive_plot( peaks, plot_function=plot_peak_fits, ax=ax, freq_range=tuple(freq_range) if freq_range else freq_range, colors=colors, labels=labels, plot_style=plot_style, **plot_kwargs) else: if not freq_range: # Extract all the CF values, excluding any NaNs cfs = peaks[~np.isnan(peaks[:, 0]), 0] # Define the frequency range as +/- buffer around the data range # This also doesn't let the plot range drop below 0 f_buffer = 4 freq_range = [ cfs.min() - f_buffer if cfs.min() - f_buffer > 0 else 0, cfs.max() + f_buffer ] # Create the frequency axis, which will be the plot x-axis freqs = gen_freqs(freq_range, 0.1) colors = colors[0] if isinstance(colors, list) else colors avg_vals = np.zeros(shape=[len(freqs)]) for peak_params in peaks: # Create & plot the peak model from parameters peak_vals = gaussian_function(freqs, *peak_params) plot_kwargs = check_plot_kwargs(plot_kwargs, { 'alpha': 0.35, 'linewidth': 1.25 }) ax.plot(freqs, peak_vals, color=colors, **plot_kwargs) # Collect a running average average peaks avg_vals = np.nansum(np.vstack([avg_vals, peak_vals]), axis=0) # Plot the average across all components avg = avg_vals / peaks.shape[0] avg_color = 'black' if not colors else colors ax.plot(freqs, avg, color=avg_color, linewidth=plot_kwargs.get('linewidth') * 3, label=labels) # Add axis labels ax.set_xlabel('Frequency') ax.set_ylabel('log(Power)') # Set plot limits ax.set_xlim(freq_range) ax.set_ylim([0, ax.get_ylim()[1]]) # Apply plot style check_n_style(plot_style, ax)