From 37d6cd86eec8b35d9ce99fbd47d4b51ab931942f Mon Sep 17 00:00:00 2001 From: Tom Donoghue Date: Sun, 27 Aug 2023 18:06:06 -0400 Subject: [PATCH 1/7] make grid an updateable argument in PSD plots --- fooof/plts/spectra.py | 12 +++++++----- fooof/plts/style.py | 6 ++++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/fooof/plts/spectra.py b/fooof/plts/spectra.py index 81ec7eb55..2c01fe6d8 100644 --- a/fooof/plts/spectra.py +++ b/fooof/plts/spectra.py @@ -50,10 +50,10 @@ def plot_spectra(freqs, power_spectra, log_freqs=False, log_powers=False, freq_r Additional plot related keyword arguments. """ + # Create the plot & collect plot kwargs of interest ax = check_ax(ax, plot_kwargs.pop('figsize', PLT_FIGSIZES['spectral'])) - - # Create the plot plot_kwargs = check_plot_kwargs(plot_kwargs, {'linewidth' : 2.0}) + grid = plot_kwargs.pop('grid', True) # Check for frequency range input, and log if x-axis is in log space if freq_range is not None: @@ -82,7 +82,7 @@ def plot_spectra(freqs, power_spectra, log_freqs=False, log_powers=False, freq_r ax.set_xlim(freq_range) - style_spectrum_plot(ax, log_freqs, log_powers) + style_spectrum_plot(ax, log_freqs, log_powers, grid) # Alias `plot_spectrum` to `plot_spectra` for backwards compatibility @@ -127,7 +127,8 @@ def plot_spectra_shading(freqs, power_spectra, shades, shade_colors='r', add_shades(ax, shades, shade_colors, add_center, plot_kwargs.get('log_freqs', False)) style_spectrum_plot(ax, plot_kwargs.get('log_freqs', False), - plot_kwargs.get('log_powers', False)) + plot_kwargs.get('log_powers', False), + plot_kwargs.get('grid', True)) # Alias `plot_spectrum_shading` to `plot_spectra_shading` for backwards compatibility @@ -172,6 +173,7 @@ def plot_spectra_yshade(freqs, power_spectra, shade='std', average='mean', scale raise ValueError('Power spectra must be 2d if shade is not given.') ax = check_ax(ax, plot_kwargs.pop('figsize', PLT_FIGSIZES['spectral'])) + grid = plot_kwargs.pop('grid', True) # Set plot data & labels, logging if requested plt_freqs = np.log10(freqs) if log_freqs else freqs @@ -208,4 +210,4 @@ def plot_spectra_yshade(freqs, power_spectra, shade='std', average='mean', scale ax.fill_between(plt_freqs, lower_shade, upper_shade, alpha=alpha, color=color, **plot_kwargs) - style_spectrum_plot(ax, log_freqs, log_powers) + style_spectrum_plot(ax, log_freqs, log_powers, grid) diff --git a/fooof/plts/style.py b/fooof/plts/style.py index b91a5b32e..a3a720ff6 100644 --- a/fooof/plts/style.py +++ b/fooof/plts/style.py @@ -12,7 +12,7 @@ ################################################################################################### ################################################################################################### -def style_spectrum_plot(ax, log_freqs, log_powers): +def style_spectrum_plot(ax, log_freqs, log_powers, grid=True): """Apply style and aesthetics to a power spectrum plot. Parameters @@ -23,6 +23,8 @@ def style_spectrum_plot(ax, log_freqs, log_powers): Whether the frequency axis is plotted in log space. log_powers : bool Whether the power axis is plotted in log space. + grid : bool, optional, default: True + Whether to add grid lines to the plot. """ # Get labels, based on log status @@ -33,7 +35,7 @@ def style_spectrum_plot(ax, log_freqs, log_powers): ax.set_xlabel(xlabel, fontsize=20) ax.set_ylabel(ylabel, fontsize=20) ax.tick_params(axis='both', which='major', labelsize=16) - ax.grid(True) + ax.grid(grid) # If labels were provided, add a legend if ax.get_legend_handles_labels()[0]: From 81cc698e505c7ebe84a7c741830466369a6decbf Mon Sep 17 00:00:00 2001 From: Tom Donoghue Date: Tue, 5 Sep 2023 13:54:30 -0400 Subject: [PATCH 2/7] update styling stuff - collection, check func --- fooof/plts/settings.py | 9 +++++++-- fooof/plts/style.py | 30 +++++++++++++++++++++++++----- fooof/tests/plts/test_styles.py | 4 ++++ 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/fooof/plts/settings.py b/fooof/plts/settings.py index c6d82c138..045eaef3d 100644 --- a/fooof/plts/settings.py +++ b/fooof/plts/settings.py @@ -40,8 +40,13 @@ # Custom style arguments are those that are custom-handled by the plot style function CUSTOM_STYLE_ARGS = ['title_fontsize', 'label_size', 'tick_labelsize', 'legend_size', 'legend_loc'] -STYLERS = ['axis_styler', 'line_styler', 'custom_styler'] -STYLE_ARGS = AXIS_STYLE_ARGS + LINE_STYLE_ARGS + CUSTOM_STYLE_ARGS + STYLERS + +# Define list of available style functions - these can also be replaced by arguments +STYLERS = ['axis_styler', 'line_styler', 'collection_styler', 'custom_styler'] + +# Collect the full set of possible style related input keyword arguments +STYLE_ARGS = \ + AXIS_STYLE_ARGS + LINE_STYLE_ARGS + COLLECTION_STYLE_ARGS + CUSTOM_STYLE_ARGS + STYLERS ## Define default values for plot aesthetics # These are all custom style arguments diff --git a/fooof/plts/style.py b/fooof/plts/style.py index a3a720ff6..821579ec5 100644 --- a/fooof/plts/style.py +++ b/fooof/plts/style.py @@ -6,12 +6,22 @@ import matplotlib.pyplot as plt from fooof.plts.settings import (AXIS_STYLE_ARGS, LINE_STYLE_ARGS, COLLECTION_STYLE_ARGS, - STYLE_ARGS, LABEL_SIZE, LEGEND_SIZE, LEGEND_LOC, - TICK_LABELSIZE, TITLE_FONTSIZE) + CUSTOM_STYLE_ARGS, STYLE_ARGS, TICK_LABELSIZE, TITLE_FONTSIZE, + LABEL_SIZE, LEGEND_SIZE, LEGEND_LOC) ################################################################################################### ################################################################################################### +def check_style_options(): + """Check the list of valid style arguments that can be passed into plot functions.""" + + print('Valid style arguments:') + for label, options in zip(['Axis', 'Line', 'Collection', 'Custom'], + [AXIS_STYLE_ARGS, LINE_STYLE_ARGS, + COLLECTION_STYLE_ARGS, CUSTOM_STYLE_ARGS]): + print(' ', label, '\t', ', '.join(options)) + + def style_spectrum_plot(ax, log_freqs, log_powers, grid=True): """Apply style and aesthetics to a power spectrum plot. @@ -229,9 +239,19 @@ def style_plot(func, *args, **kwargs): By default, this function applies styling with the `apply_style` function. Custom functions for applying style can be passed in using `apply_style` as a keyword argument. - The `apply_style` function calls sub-functions for applying style different plot elements, - and these sub-functions can be overridden by passing in alternatives for `axis_styler`, - `line_styler`, and `custom_styler`. + The `apply_style` function calls sub-functions for applying different plot elements, including: + + - `axis_styler`: apply style options to an axis + - `line_styler`: applies style options to lines objects in a plot + - `collection_styler`: applies style options to collections objects in a plot + - `custom_style`: applies custom style options + + Each of these sub-functions can be overridden by passing in alternatives. + + To see the full set of style arguments that are supported, run the following code: + + >>> from fooof.plts.style import check_style_options + >>> check_style_options() """ @wraps(func) diff --git a/fooof/tests/plts/test_styles.py b/fooof/tests/plts/test_styles.py index 72854ff97..9ac25c755 100644 --- a/fooof/tests/plts/test_styles.py +++ b/fooof/tests/plts/test_styles.py @@ -6,6 +6,10 @@ ################################################################################################### ################################################################################################### +def test_check_style_options(): + + check_style_options() + def test_style_spectrum_plot(skip_if_no_mpl): # Create a dummy plot and style it From 85a3fdb993d26aef98e2d503444748fbc2642d90 Mon Sep 17 00:00:00 2001 From: Tom Donoghue Date: Tue, 5 Sep 2023 13:54:44 -0400 Subject: [PATCH 3/7] add public facing plt utils to API list --- doc/api.rst | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/doc/api.rst b/doc/api.rst index b22e43358..f57f0ab49 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -326,6 +326,27 @@ Annotated plots that describe the model and fitting process. plot_annotated_model plot_annotated_peak_search +Plot Utilities & Styling +~~~~~~~~~~~~~~~~~~~~~~~~ + +Plot related utilies for styling and managing plots. + +.. currentmodule:: fooof.plts.style + +.. autosummary:: + :toctree: generated/ + + check_style_options + +.. currentmodule:: fooof.plts.utils + +.. autosummary:: + :toctree: generated/ + + check_ax + recursive_plot + save_figure + Utilities --------- From d4b27d13ae8ebf046af042641498ea8a9f870f60 Mon Sep 17 00:00:00 2001 From: Tom Donoghue Date: Tue, 5 Sep 2023 14:27:07 -0400 Subject: [PATCH 4/7] update descriptions of plot_kwargs --- fooof/plts/aperiodic.py | 4 ++-- fooof/plts/error.py | 2 +- fooof/plts/fg.py | 8 +++++--- fooof/plts/fm.py | 4 ++-- fooof/plts/periodic.py | 4 ++-- fooof/plts/spectra.py | 12 ++++++++---- 6 files changed, 20 insertions(+), 14 deletions(-) diff --git a/fooof/plts/aperiodic.py b/fooof/plts/aperiodic.py index 905b95145..b4a08368d 100644 --- a/fooof/plts/aperiodic.py +++ b/fooof/plts/aperiodic.py @@ -33,7 +33,7 @@ def plot_aperiodic_params(aps, colors=None, labels=None, ax=None, **plot_kwargs) ax : matplotlib.Axes, optional Figure axes upon which to plot. **plot_kwargs - Keyword arguments to pass into the ``style_plot``. + Additional plot related keyword arguments, with styling options managed by ``style_plot``. """ ax = check_ax(ax, plot_kwargs.pop('figsize', PLT_FIGSIZES['params'])) @@ -83,7 +83,7 @@ def plot_aperiodic_fits(aps, freq_range, control_offset=False, ax : matplotlib.Axes, optional Figure axes upon which to plot. **plot_kwargs - Keyword arguments to pass into the ``style_plot``. + Additional plot related keyword arguments, with styling options managed by ``style_plot``. """ ax = check_ax(ax, plot_kwargs.pop('figsize', PLT_FIGSIZES['params'])) diff --git a/fooof/plts/error.py b/fooof/plts/error.py index af04840f9..fd488ddfc 100644 --- a/fooof/plts/error.py +++ b/fooof/plts/error.py @@ -33,7 +33,7 @@ def plot_spectral_error(freqs, error, shade=None, log_freqs=False, ax=None, **pl ax : matplotlib.Axes, optional Figure axes upon which to plot. **plot_kwargs - Keyword arguments to pass into the ``style_plot``. + Additional plot related keyword arguments, with styling options managed by ``style_plot``. """ ax = check_ax(ax, plot_kwargs.pop('figsize', PLT_FIGSIZES['spectral'])) diff --git a/fooof/plts/fg.py b/fooof/plts/fg.py index 310d95d1c..eef0b6e41 100644 --- a/fooof/plts/fg.py +++ b/fooof/plts/fg.py @@ -33,6 +33,8 @@ def plot_fg(fg, save_fig=False, file_name=None, file_path=None, **plot_kwargs): Name to give the saved out file. file_path : Path or str, optional Path to directory to save to. If None, saves to current directory. + **plot_kwargs + Additional plot related keyword arguments, with styling options managed by ``style_plot``. Raises ------ @@ -76,7 +78,7 @@ def plot_fg_ap(fg, ax=None, **plot_kwargs): ax : matplotlib.Axes, optional Figure axes upon which to plot. **plot_kwargs - Keyword arguments to pass into the ``style_plot``. + Additional plot related keyword arguments, with styling options managed by ``style_plot``. """ if fg.aperiodic_mode == 'knee': @@ -101,7 +103,7 @@ def plot_fg_gf(fg, ax=None, **plot_kwargs): ax : matplotlib.Axes, optional Figure axes upon which to plot. **plot_kwargs - Keyword arguments to pass into the ``style_plot``. + Additional plot related keyword arguments, with styling options managed by ``style_plot``. """ plot_scatter_2(fg.get_params('error'), 'Error', @@ -121,7 +123,7 @@ def plot_fg_peak_cens(fg, ax=None, **plot_kwargs): ax : matplotlib.Axes, optional Figure axes upon which to plot. **plot_kwargs - Keyword arguments to pass into the ``style_plot``. + Additional plot related keyword arguments, with styling options managed by ``style_plot``. """ plot_hist(fg.get_params('peak_params', 0)[:, 0], 'Center Frequency', diff --git a/fooof/plts/fm.py b/fooof/plts/fm.py index 016a6a7b1..ae3be4d0b 100644 --- a/fooof/plts/fm.py +++ b/fooof/plts/fm.py @@ -63,7 +63,7 @@ def plot_fm(fm, plot_peaks=None, plot_aperiodic=True, freqs=None, power_spectrum 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``. + Additional plot related keyword arguments, with styling options managed by ``style_plot``. Notes ----- @@ -169,7 +169,7 @@ def _add_peaks_shade(fm, plt_log, ax, **plot_kwargs): ax : matplotlib.Axes Figure axes upon which to plot. **plot_kwargs - Keyword arguments to pass into the ``fill_between``. + Keyword arguments to pass into ``fill_between``. """ defaults = {'color' : PLT_COLORS['periodic'], 'alpha' : 0.25} diff --git a/fooof/plts/periodic.py b/fooof/plts/periodic.py index 17e66f1b9..e6e923dd3 100644 --- a/fooof/plts/periodic.py +++ b/fooof/plts/periodic.py @@ -35,7 +35,7 @@ def plot_peak_params(peaks, freq_range=None, colors=None, labels=None, ax=None, ax : matplotlib.Axes, optional Figure axes upon which to plot. **plot_kwargs - Keyword arguments to pass into the ``style_plot``. + Additional plot related keyword arguments, with styling options managed by ``style_plot``. """ ax = check_ax(ax, plot_kwargs.pop('figsize', PLT_FIGSIZES['params'])) @@ -86,7 +86,7 @@ def plot_peak_fits(peaks, freq_range=None, colors=None, labels=None, ax=None, ** ax : matplotlib.Axes, optional Figure axes upon which to plot. **plot_kwargs - Keyword arguments to pass into the plot call. + Additional plot related keyword arguments, with styling options managed by ``style_plot``. """ ax = check_ax(ax, plot_kwargs.pop('figsize', PLT_FIGSIZES['params'])) diff --git a/fooof/plts/spectra.py b/fooof/plts/spectra.py index 2c01fe6d8..852b198ba 100644 --- a/fooof/plts/spectra.py +++ b/fooof/plts/spectra.py @@ -47,7 +47,8 @@ def plot_spectra(freqs, power_spectra, log_freqs=False, log_powers=False, freq_r ax : matplotlib.Axes, optional Figure axes upon which to plot. **plot_kwargs - Additional plot related keyword arguments. + Additional plot related keyword arguments, with styling options managed by ``style_plot``. + For spectra plots, boolean input `grid` can be used to control if the figure has a grid. """ # Create the plot & collect plot kwargs of interest @@ -110,8 +111,9 @@ def plot_spectra_shading(freqs, power_spectra, shades, shade_colors='r', ax : matplotlib.Axes, optional Figure axes upon which to plot. **plot_kwargs - Additional plot related keyword arguments. - This can include additional inputs into :func:`~.plot_spectra`. + Additional plot related keyword arguments, with styling options managed by ``style_plot``. + For spectra plots, boolean input `grid` can be used to control if the figure has a grid. + This can also include additional inputs into :func:`~.plot_spectra`. Notes ----- @@ -166,7 +168,9 @@ def plot_spectra_yshade(freqs, power_spectra, shade='std', average='mean', scale ax : matplotlib.Axes, optional Figure axes upon which to plot. **plot_kwargs - Additional plot related keyword arguments. + Additional plot related keyword arguments, with styling options managed by ``style_plot``. + For spectra plots, boolean input `grid` can be used to control if the figure has a grid. + This can also include additional inputs into :func:`~.plot_spectra`. """ if (isinstance(shade, str) or isfunction(shade)) and power_spectra.ndim != 2: From 6ac86e667799977986168a638c43bc2d48a69053 Mon Sep 17 00:00:00 2001 From: Tom Donoghue Date: Tue, 5 Sep 2023 15:00:58 -0400 Subject: [PATCH 5/7] update check_style options --- fooof/plts/style.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/fooof/plts/style.py b/fooof/plts/style.py index 821579ec5..52b9ec504 100644 --- a/fooof/plts/style.py +++ b/fooof/plts/style.py @@ -18,8 +18,8 @@ def check_style_options(): print('Valid style arguments:') for label, options in zip(['Axis', 'Line', 'Collection', 'Custom'], [AXIS_STYLE_ARGS, LINE_STYLE_ARGS, - COLLECTION_STYLE_ARGS, CUSTOM_STYLE_ARGS]): - print(' ', label, '\t', ', '.join(options)) + COLLECTION_STYLE_ARGS, CUSTOM_STYLE_ARGS]): + print(' {:10s} {}'.format(label, ', '.join(options))) def style_spectrum_plot(ax, log_freqs, log_powers, grid=True): @@ -252,6 +252,11 @@ def style_plot(func, *args, **kwargs): >>> from fooof.plts.style import check_style_options >>> check_style_options() + Valid style arguments: + Axis title, xlabel, ylabel, xlim, ylim + Line alpha, lw, linewidth, ls, linestyle, marker, ms, markersize + Collection alpha, edgecolor + Custom title_fontsize, label_size, tick_labelsize, legend_size, legend_loc """ @wraps(func) From f08616344a85557284f6958c193e6d8a5d958845 Mon Sep 17 00:00:00 2001 From: Tom Donoghue Date: Wed, 6 Sep 2023 13:01:48 -0400 Subject: [PATCH 6/7] extend setable axis plot kwargs --- fooof/plts/settings.py | 3 ++- fooof/plts/style.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/fooof/plts/settings.py b/fooof/plts/settings.py index 045eaef3d..cf9716f0d 100644 --- a/fooof/plts/settings.py +++ b/fooof/plts/settings.py @@ -28,7 +28,8 @@ 'linestyle' : ['ls', 'linestyle']} # Plot style arguments are those that can be defined on an axis object -AXIS_STYLE_ARGS = ['title', 'xlabel', 'ylabel', 'xlim', 'ylim'] +AXIS_STYLE_ARGS = ['title', 'xlabel', 'ylabel', 'xlim', 'ylim', + 'xticks', 'yticks', 'xticklabels', 'yticklabels'] # Line style arguments are those that can be defined on a line object LINE_STYLE_ARGS = ['alpha', 'lw', 'linewidth', 'ls', 'linestyle', diff --git a/fooof/plts/style.py b/fooof/plts/style.py index 52b9ec504..f4063c02d 100644 --- a/fooof/plts/style.py +++ b/fooof/plts/style.py @@ -253,7 +253,7 @@ def style_plot(func, *args, **kwargs): >>> from fooof.plts.style import check_style_options >>> check_style_options() Valid style arguments: - Axis title, xlabel, ylabel, xlim, ylim + Axis title, xlabel, ylabel, xlim, ylim, xticks, yticks, xticklabels, yticklabels Line alpha, lw, linewidth, ls, linestyle, marker, ms, markersize Collection alpha, edgecolor Custom title_fontsize, label_size, tick_labelsize, legend_size, legend_loc From 3874dc86195c0fd7c01f340dc009b9efe560d7d6 Mon Sep 17 00:00:00 2001 From: Tom Donoghue Date: Wed, 13 Sep 2023 08:30:46 -0400 Subject: [PATCH 7/7] fix docstring label --- fooof/objs/fit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fooof/objs/fit.py b/fooof/objs/fit.py index 48106edfd..35d6518c5 100644 --- a/fooof/objs/fit.py +++ b/fooof/objs/fit.py @@ -401,7 +401,7 @@ def report(self, freqs=None, power_spectrum=None, freq_range=None, Only relevant / effective if `freqs` and `power_spectrum` passed in in this call. **plot_kwargs Keyword arguments to pass into the plot method. - Plot options with a name conflict be passed by pre-pending 'plot_'. + Plot options with a name conflict be passed by pre-pending `plot_`. e.g. `freqs`, `power_spectrum` and `freq_range`. Notes