Skip to content

Commit

Permalink
fix merge
Browse files Browse the repository at this point in the history
  • Loading branch information
TomDonoghue committed Sep 13, 2023
2 parents c0019a2 + cdf2919 commit 4a1ca34
Show file tree
Hide file tree
Showing 13 changed files with 132 additions and 39 deletions.
21 changes: 21 additions & 0 deletions doc/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
---------

Expand Down
2 changes: 1 addition & 1 deletion specparam/objs/fit.py
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,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
Expand Down
4 changes: 2 additions & 2 deletions specparam/plts/aperiodic.py
Original file line number Diff line number Diff line change
Expand Up @@ -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']))
Expand Down Expand Up @@ -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']))
Expand Down
2 changes: 1 addition & 1 deletion specparam/plts/error.py
Original file line number Diff line number Diff line change
Expand Up @@ -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']))
Expand Down
8 changes: 4 additions & 4 deletions specparam/plts/group.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def plot_group(group, **plot_kwargs):
group : SpectralGroupModel
Object containing results from fitting a group of power spectra.
**plot_kwargs
Keyword arguments to apply to the plot.
Additional plot related keyword arguments, with styling options managed by ``style_plot``.
Raises
------
Expand Down Expand Up @@ -72,7 +72,7 @@ def plot_group_aperiodic(group, 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 group.aperiodic_mode == 'knee':
Expand All @@ -97,7 +97,7 @@ def plot_group_goodness(group, 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(group.get_params('error'), 'Error',
Expand All @@ -117,7 +117,7 @@ def plot_group_peak_frequencies(group, 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(group.get_params('peak_params', 0)[:, 0], 'Center Frequency',
Expand Down
4 changes: 2 additions & 2 deletions specparam/plts/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def plot_model(model, plot_peaks=None, plot_aperiodic=True, freqs=None, power_sp
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 apply to the plot.
Additional plot related keyword arguments, with styling options managed by ``style_plot``.
Notes
-----
Expand Down Expand Up @@ -163,7 +163,7 @@ def _add_peaks_shade(model, 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}
Expand Down
4 changes: 2 additions & 2 deletions specparam/plts/periodic.py
Original file line number Diff line number Diff line change
Expand Up @@ -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']))
Expand Down Expand Up @@ -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']))
Expand Down
12 changes: 9 additions & 3 deletions specparam/plts/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -40,8 +41,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
Expand Down
24 changes: 15 additions & 9 deletions specparam/plts/spectra.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,14 @@ 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
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:
Expand Down Expand Up @@ -83,7 +84,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
Expand Down Expand Up @@ -111,8 +112,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
-----
Expand All @@ -128,7 +130,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
Expand Down Expand Up @@ -166,13 +169,16 @@ 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:
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
Expand Down Expand Up @@ -209,4 +215,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)
41 changes: 34 additions & 7 deletions specparam/plts/style.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,23 @@
import matplotlib.pyplot as plt

from specparam.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 style_spectrum_plot(ax, log_freqs, log_powers):
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(' {:10s} {}'.format(label, ', '.join(options)))


def style_spectrum_plot(ax, log_freqs, log_powers, grid=True):
"""Apply style and aesthetics to a power spectrum plot.
Parameters
Expand All @@ -23,6 +33,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
Expand All @@ -33,7 +45,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]:
Expand Down Expand Up @@ -227,9 +239,24 @@ 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()
Valid style arguments:
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
"""

@wraps(func)
Expand Down
4 changes: 4 additions & 0 deletions specparam/tests/plts/test_styles.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion specparam/tests/utils/test_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def test_compute_knee_frequency():

def test_compute_time_constant():

assert compute_time_constant(100)
assert compute_time_constant(10)

def test_compute_fwhm():

Expand Down
43 changes: 36 additions & 7 deletions specparam/utils/params.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,55 @@ def compute_knee_frequency(knee, exponent):
-------
float
Frequency value, in Hz, of the knee occurs.
Notes
-----
The knee frequency is an estimate of the frequency in spectrum at which the spectrum
moves from the plateau region to the exponential decay.
This approach for estimating the knee frequency comes from [1]_ (see [2]_ for code).
Note that this provides an estimate of the knee frequency, but is not, in the general case,
a precisely defined value. In particular, this conversion is based on the case of a Lorentzian
with exponent = 2, and for other exponent values provides a non-exact approximation.
References
----------
.. [1] Gao, R., van den Brink, R. L., Pfeffer, T., & Voytek, B. (2020). Neuronal timescales
are functionally dynamic and shaped by cortical microarchitecture. Elife, 9, e61277.
https://doi.org/10.7554/eLife.61277
.. [2] https://github.com/rdgao/field-echos/blob/master/echo_utils.py#L64
"""

return knee ** (1./exponent)
return knee ** (1. / exponent)


def compute_time_constant(knee):
"""Compute the characteristic time constant based on the knee value.
def compute_time_constant(knee_freq):
"""Compute the characteristic time constant from the estimated knee frequency.
Parameters
----------
knee : float
Knee parameter value.
knee_freq : float
Estimated knee frequency.
Returns
-------
float
Calculated time constant value, tau, given the knee parameter.
Calculated time constant value, tau, given the knee frequency.
Notes
-----
This approach for estimating the time constant comes from [1]_ (see [2]_ for code).
References
----------
.. [1] Gao, R., van den Brink, R. L., Pfeffer, T., & Voytek, B. (2020). Neuronal timescales
are functionally dynamic and shaped by cortical microarchitecture. Elife, 9, e61277.
https://doi.org/10.7554/eLife.61277
.. [2] https://github.com/rdgao/field-echos/blob/master/echo_utils.py#L65
"""

return 1. / (2*np.pi*knee)
return 1. / (2 * np.pi * knee_freq)


def compute_fwhm(std):
Expand Down

0 comments on commit 4a1ca34

Please sign in to comment.