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_scatter_2(data_0, label_0, data_1, label_1, title=None, ax=None): """Plot a scatter plot, with two y-axes. Parameters ---------- data_0 : 1d array Data to plot on the first axis. label_0 : str Label for the data on the first axis, to be set as the axis label. data_1 : 1d array Data to plot on the second axis. label_0 : str Label for the data on the second axis, to be set as the axis label. title : str, optional Title for the plot. ax : matplotlib.Axes, optional Figure axes upon which to plot. Notes ----- Data is jittered slightly, for visualization purposes (deviations on x-axis are meaningless). """ ax = check_ax(ax) ax1 = ax.twinx() plot_scatter_1(data_0, label_0, ax=ax) plot_scatter_1(data_1, label_1, x_val=1, ax=ax1) if title: ax.set_title(title, fontsize=20) ax.set(xlim=[-0.5, 1.5], xticks=[0, 1], xticklabels=[label_0, label_1]) ax.tick_params(axis='x', labelsize=16)
def plot_hist(data, label, title=None, n_bins=25, x_lims=None, ax=None): """Plot a histogram. Parameters ---------- data : 1d array Data to plot. label : str Label for the data, to be set as the x-axis label. title : str, optional Title for the plot. n_bins : int, optional, default: 25 Number of bins to use for the histogram. x_lims : list of float, optional Limits for the x-axis of the plot. ax : matplotlib.Axes, optional Figure axes upon which to plot. """ ax = check_ax(ax) ax.hist(data[~np.isnan(data)], n_bins, range=x_lims, alpha=0.8) ax.set_xlabel(label, fontsize=16) ax.set_ylabel('Count', fontsize=16) if x_lims: ax.set_xlim(x_lims) if title: ax.set_title(title, fontsize=20) ax.tick_params(axis='both', labelsize=12)
def plot_spectrum_shading(freqs, power_spectrum, shades, add_center=False, ax=None, plot_style=style_spectrum_plot, **kwargs): """Plot a power spectrum with a shaded frequency region (or regions). Parameters ---------- freqs : 1d array X-axis data, frequency values. power_spectrum : 1d array Y-axis data, power values for spectrum to plot. shades : list of [float, float] or list of list of [float, float] Shaded region(s) to add to plot, defined as [lower_bound, upper_bound]. add_center : boolean, optional, default: False Whether to add a line at the center point of the shaded regions. 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. **kwargs Keyword arguments to be passed to the plot call. """ ax = check_ax(ax) plot_spectrum(freqs, power_spectrum, plot_style=None, ax=ax, **kwargs) add_shades(ax, shades, add_center, kwargs.get('log_freqs', False)) check_n_style(plot_style, ax, kwargs.get('log_freqs', False), kwargs.get('log_powers', False))
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_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_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 plot_spectra(freqs, power_spectra, log_freqs=False, log_powers=False, labels=None, ax=None, plot_style=style_spectrum_plot, **plot_kwargs): """Plot multiple power spectra on the same plot. Parameters ---------- freqs : 2d array or 1d array or list of 1d array Frequency values, to be plotted on the x-axis. power_spectra : 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. labels : list of str, optional Legend labels, for each power spectrum. 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 the plot call. """ ax = check_ax(ax, plot_kwargs.pop('figsize', PLT_FIGSIZES['spectral'])) # Make inputs iterable if need to be passed multiple times to plot each spectrum freqs = repeat(freqs) if isinstance( freqs, np.ndarray) and freqs.ndim == 1 else freqs labels = repeat(labels) if not isinstance(labels, list) else labels for freq, power_spectrum, label in zip(freqs, power_spectra, labels): plot_spectrum(freq, power_spectrum, log_freqs, log_powers, label=label, plot_style=None, ax=ax, **plot_kwargs) check_n_style(plot_style, ax, log_freqs, log_powers)
def plot_spectra_shading(freqs, power_spectra, shades, shade_colors='r', add_center=False, ax=None, plot_style=style_spectrum_plot, **plot_kwargs): """Plot a group of power spectra with a shaded frequency region (or regions). Parameters ---------- freqs : 2d array or 1d array or list of 1d array Frequency values, to be plotted on the x-axis. power_spectra : 2d array or list of 1d array Power values, to be plotted on the y-axis. shades : list of [float, float] or list of list of [float, float] Shaded region(s) to add to plot, defined as [lower_bound, upper_bound]. shade_colors : str or list of string Color(s) to plot shades. add_center : bool, optional, default: False Whether to add a line at the center point of the shaded regions. 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. Notes ----- Parameters for `plot_spectra` can also be passed into this function as keyword arguments. This includes `log_freqs`, `log_powers` & `labels`. See `plot_spectra` for usage details. """ ax = check_ax(ax, plot_kwargs.pop('figsize', PLT_FIGSIZES['spectral'])) plot_spectra(freqs, power_spectra, ax=ax, plot_style=None, **plot_kwargs) add_shades(ax, shades, shade_colors, add_center, plot_kwargs.get('log_freqs', False)) check_n_style(plot_style, ax, plot_kwargs.get('log_freqs', False), plot_kwargs.get('log_powers', False))
def plot_spectra(freqs, power_spectra, log_freqs=False, log_powers=False, labels=None, ax=None, plot_style=style_spectrum_plot, **kwargs): """Plot multiple power spectra on the same plot. Parameters ---------- freqs : 2d array or 1d array or list of 1d array X-axis data, frequency values. power_spectra : 2d array or list of 1d array Y-axis data, power values for spectra to plot. log_freqs : boolean, optional, default: False Whether or not to take the log of the frequency axis before plotting. log_powers : boolean, optional, default: False Whether or not to take the log of the power axis before plotting. labels : list of str, optional Legend labels, for each power spectrum. 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. **kwargs Keyword arguments to be passed to the plot call. """ freqs = repeat(freqs) if isinstance( freqs, np.ndarray) and freqs.ndim == 1 else freqs labels = repeat(labels) if not isinstance(labels, list) else labels ax = check_ax(ax) for freq, power_spectrum, label in zip(freqs, power_spectra, labels): plot_spectrum(freq, power_spectrum, log_freqs, log_powers, label=label, plot_style=None, ax=ax, **kwargs) check_n_style(plot_style, ax, log_freqs, log_powers)
def plot_spectrum_shading(freqs, power_spectrum, shades, add_center=False, ax=None, **kwargs): """Plot a power spectrum with a shaded frequency region (or regions). Parameters ---------- freqs : 1d array X-axis data, frequency values. power_spectrum : list of 1d array Y-axis data, power spectrum power values for spectrum to plot. shades : list of [float, float] or list of list of [float, float] Shaded region(s) to add to plot, defined as [lower_bound, upper_bound]. add_center : boolean Whether to add a line at the center point of the shaded regions. """ ax = check_ax(ax) plot_spectrum(freqs, power_spectrum, ax=ax, **kwargs) add_shades(ax, shades, add_center, kwargs.get('log_freqs', False))
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')))
def plot_scatter_1(data, label=None, title=None, x_val=0, ax=None): """Plot a scatter plot, with a single y-axis. Parameters ---------- data : 1d array Data to plot. label : str, optional Label for the data, to be set as the y-axis label. title : str, optional Title for the plot. x_val : int, optional, default: 0 Position along the x-axis to plot set of data. ax : matplotlib.Axes, optional Figure axes upon which to plot. Notes ----- Data is jittered slightly, for visualization purposes (deviations on x-axis are meaningless). """ ax = check_ax(ax) # Create x-axis data, with small jitter for visualization purposes x_data = np.ones_like(data) * x_val + np.random.normal( 0, 0.025, data.shape) ax.scatter(x_data, data, s=36, alpha=set_alpha(len(data))) if label: ax.set_ylabel(label, fontsize=16) ax.set(xticks=[x_val], xticklabels=[label]) if title: ax.set_title(title, fontsize=20) ax.tick_params(axis='x', labelsize=16) ax.tick_params(axis='y', labelsize=12) ax.set_xlim([-0.5, 0.5])
def plot_spectra(freqs, power_spectra, log_freqs=False, log_powers=False, ax=None, **kwargs): """Plot multiple power spectra on the same plot. Parameters ---------- freqs : 1d array X-axis data, frequency values. power_spectra : list of 1d array Y-axis data, power spectrum power values for spectra to plot. log_freqs : boolean, optional Whether or not to take the log of the power axis before plotting. default: False log_powers : boolean, optional Whether or not to take the log of the power axis before plotting. default: False ax : matplotlib.Axes, optional Figure axes upon which to plot. **kwargs Keyword arguments to be passed to the plot call. """ ax = check_ax(ax) for power_spectrum in power_spectra: plot_spectrum(freqs, power_spectrum, log_freqs, log_powers, ax=ax, **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_spectra_shading(freqs, power_spectra, shades, add_center=False, ax=None, plot_style=style_spectrum_plot, **kwargs): """Plot a group of power spectra with a shaded frequency region (or regions). Parameters ---------- freqs : 2d array or 1d array or list of 1d array X-axis data, frequency values. power_spectra : 2d array or list of 1d array Y-axis data, power values for spectra to plot. shades : list of [float, float] or list of list of [float, float] Shaded region(s) to add to plot, defined as [lower_bound, upper_bound]. add_center : boolean, optional, default: False Whether to add a line at the center point of the shaded regions. 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. **kwargs Keyword arguments to be passed to plot_spectra or the plot call. Notes ----- Parameters for `plot_spectra` can also be passed into this function as **kwargs. This includes `log_freqs`, `log_powers` & `labels`. See `plot_spectra for usage details. """ ax = check_ax(ax) plot_spectra(freqs, power_spectra, ax=ax, plot_style=None, **kwargs) add_shades(ax, shades, add_center, kwargs.get('log_freqs', False)) check_n_style(plot_style, ax, kwargs.get('log_freqs', False), kwargs.get('log_powers', False))
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)
def plot_spectra_yshade(freqs, power_spectra, shade='std', average='mean', scale=1, log_freqs=False, log_powers=False, color=None, label=None, ax=None, **plot_kwargs): """Plot standard deviation or error as a shaded region around the mean spectrum. Parameters ---------- freqs : 1d array Frequency values, to be plotted on the x-axis. power_spectra : 1d or 2d array Power values, to be plotted on the y-axis. ``shade`` must be provided if 1d. shade : 'std', 'sem', 1d array or callable, optional, default: 'std' Approach for shading above/below the mean spectrum. average : 'mean', 'median' or callable, optional, default: 'mean' Averaging approach for the average spectrum to plot. Only used if power_spectra is 2d. scale : int, optional, default: 1 Factor to multiply the plotted shade by. 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. color : str, optional, default: None Line color of the spectrum. label : str, optional, default: None Legend label for the spectrum. ax : matplotlib.Axes, optional Figure axes upon which to plot. **plot_kwargs Keyword arguments to be passed to `plot_spectra` or to the plot call. """ if (isinstance(shade, str) or isfunction(shade)) and power_spectra.ndim != 2: raise ValueError('Power spectra must be 2d if shade is not given.') 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_spectra) if log_powers else power_spectra # Organize mean spectrum to plot avg_funcs = {'mean': np.mean, 'median': np.median} if isinstance(average, str) and plt_powers.ndim == 2: avg_powers = avg_funcs[average](plt_powers, axis=0) elif isfunction(average) and plt_powers.ndim == 2: avg_powers = average(plt_powers) else: avg_powers = plt_powers # Plot average power spectrum ax.plot(plt_freqs, avg_powers, linewidth=2.0, color=color, label=label) # Organize shading to plot shade_funcs = {'std': np.std, 'sem': sem} if isinstance(shade, str): shade_vals = scale * shade_funcs[shade](plt_powers, axis=0) elif isfunction(shade): shade_vals = scale * shade(plt_powers) else: shade_vals = scale * shade upper_shade = avg_powers + shade_vals lower_shade = avg_powers - shade_vals # Plot +/- yshading around spectrum alpha = plot_kwargs.pop('alpha', 0.25) ax.fill_between(plt_freqs, lower_shade, upper_shade, alpha=alpha, color=color, **plot_kwargs) style_spectrum_plot(ax, log_freqs, log_powers)
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 plot_annotated_model(fm, plt_log=False, annotate_peaks=True, annotate_aperiodic=True, ax=None, plot_style=style_spectrum_plot): """Plot a an annotated power spectrum and model, from a FOOOF object. Parameters ---------- fm : FOOOF FOOOF object, with model fit, data and settings available. plt_log : boolean, optional, default: False Whether to plot the frequency values in log10 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 plots. Raises ------ NoModelError If there are no model results available to plot. """ # Check that model is available if not fm.has_model: raise NoModelError("No model is available to plot, can not proceed.") # Settings fontsize = 15 lw1 = 4.0 lw2 = 3.0 ms1 = 12 # Create the baseline figure ax = check_ax(ax, PLT_FIGSIZES['spectral']) fm.plot(plot_peaks='dot-shade-width', plt_log=plt_log, ax=ax, plot_style=None, data_kwargs={ 'lw': lw1, 'alpha': 0.6 }, aperiodic_kwargs={ 'lw': lw1, 'zorder': 10 }, model_kwargs={ 'lw': lw1, 'alpha': 0.5 }, peak_kwargs={ 'dot': { 'color': PLT_COLORS['periodic'], 'ms': ms1, 'lw': lw2 }, 'shade': { 'color': PLT_COLORS['periodic'] }, 'width': { 'color': PLT_COLORS['periodic'], 'alpha': 0.75, 'lw': lw2 } }) # Get freqs for plotting, and convert to log if needed freqs = fm.freqs if not plt_log else np.log10(fm.freqs) ## Buffers: for spacing things out on the plot (scaled by plot values) x_buff1 = max(freqs) * 0.1 x_buff2 = max(freqs) * 0.25 y_buff1 = 0.15 * np.ptp(ax.get_ylim()) shrink = 0.1 # There is a bug in annotations for some perpendicular lines, so add small offset # See: https://github.com/matplotlib/matplotlib/issues/12820. Fixed in 3.2.1. bug_buff = 0.000001 if annotate_peaks: # Extract largest peak, to annotate, grabbing gaussian params gauss = get_band_peak_fm(fm, fm.freq_range, attribute='gaussian_params') peak_ctr, peak_hgt, peak_wid = gauss bw_freqs = [ peak_ctr - 0.5 * compute_fwhm(peak_wid), peak_ctr + 0.5 * compute_fwhm(peak_wid) ] if plt_log: peak_ctr = np.log10(peak_ctr) bw_freqs = np.log10(bw_freqs) peak_top = fm.power_spectrum[nearest_ind(freqs, peak_ctr)] # Annotate Peak CF ax.annotate('Center Frequency', xy=(peak_ctr, peak_top), xytext=(peak_ctr, peak_top + np.abs(0.6 * peak_hgt)), verticalalignment='center', horizontalalignment='center', arrowprops=dict(facecolor=PLT_COLORS['periodic'], shrink=shrink), color=PLT_COLORS['periodic'], fontsize=fontsize) # Annotate Peak PW ax.annotate('Power', xy=(peak_ctr, peak_top - 0.3 * peak_hgt), xytext=(peak_ctr + x_buff1, peak_top - 0.3 * peak_hgt), verticalalignment='center', arrowprops=dict(facecolor=PLT_COLORS['periodic'], shrink=shrink), color=PLT_COLORS['periodic'], fontsize=fontsize) # Annotate Peak BW bw_buff = (peak_ctr - bw_freqs[0]) / 2 ax.annotate('Bandwidth', xy=(peak_ctr - bw_buff + bug_buff, peak_top - (0.5 * peak_hgt)), xytext=(peak_ctr - bw_buff, peak_top - (1.5 * peak_hgt)), verticalalignment='center', horizontalalignment='right', arrowprops=dict(facecolor=PLT_COLORS['periodic'], shrink=shrink), color=PLT_COLORS['periodic'], fontsize=fontsize, zorder=20) if annotate_aperiodic: # Annotate Aperiodic Offset # Add a line to indicate offset, without adjusting plot limits below it ax.set_autoscaley_on(False) ax.plot([freqs[0], freqs[0]], [ax.get_ylim()[0], fm.fooofed_spectrum_[0]], color=PLT_COLORS['aperiodic'], linewidth=lw2, alpha=0.5) ax.annotate('Offset', xy=(freqs[0] + bug_buff, fm.power_spectrum[0] - y_buff1), xytext=(freqs[0] - x_buff1, fm.power_spectrum[0] - y_buff1), verticalalignment='center', horizontalalignment='center', arrowprops=dict(facecolor=PLT_COLORS['aperiodic'], shrink=shrink), color=PLT_COLORS['aperiodic'], fontsize=fontsize) # Annotate Aperiodic Knee if fm.aperiodic_mode == 'knee': # Find the knee frequency point to annotate knee_freq = compute_knee_frequency( fm.get_params('aperiodic', 'knee'), fm.get_params('aperiodic', 'exponent')) knee_freq = np.log10(knee_freq) if plt_log else knee_freq knee_pow = fm.power_spectrum[nearest_ind(freqs, knee_freq)] # Add a dot to the plot indicating the knee frequency ax.plot(knee_freq, knee_pow, 'o', color=PLT_COLORS['aperiodic'], ms=ms1 * 1.5, alpha=0.7) ax.annotate('Knee', xy=(knee_freq, knee_pow), xytext=(knee_freq - x_buff2, knee_pow - y_buff1), verticalalignment='center', arrowprops=dict(facecolor=PLT_COLORS['aperiodic'], shrink=shrink), color=PLT_COLORS['aperiodic'], fontsize=fontsize) # Annotate Aperiodic Exponent mid_ind = int(len(freqs) / 2) ax.annotate('Exponent', xy=(freqs[mid_ind], fm.power_spectrum[mid_ind]), xytext=(freqs[mid_ind] - x_buff2, fm.power_spectrum[mid_ind] - y_buff1), verticalalignment='center', arrowprops=dict(facecolor=PLT_COLORS['aperiodic'], shrink=shrink), color=PLT_COLORS['aperiodic'], fontsize=fontsize) # Apply style to plot & tune grid styling check_n_style(plot_style, ax, plt_log, True) ax.grid(True, alpha=0.5) # Add labels to plot in the legend da_patch = mpatches.Patch(color=PLT_COLORS['data'], label='Original Data') ap_patch = mpatches.Patch(color=PLT_COLORS['aperiodic'], label='Aperiodic Parameters') pe_patch = mpatches.Patch(color=PLT_COLORS['periodic'], label='Peak Parameters') mo_patch = mpatches.Patch(color=PLT_COLORS['model'], label='Full Model') handles = [ da_patch, ap_patch if annotate_aperiodic else None, pe_patch if annotate_peaks else None, mo_patch ] handles = [el for el in handles if el is not None] ax.legend(handles=handles, handlelength=1, fontsize='x-large')
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 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')
def plot_peak_fits(peaks, freq_range=None, colors=None, labels=None, ax=None, **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_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_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) ax.plot(freqs, peak_vals, color=colors, alpha=0.35, linewidth=1.25) # 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=3.75, 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 style_param_plot(ax)