Skip to content

Commit

Permalink
fix merge
Browse files Browse the repository at this point in the history
  • Loading branch information
TomDonoghue committed Jun 29, 2023
2 parents 4b4caf6 + 117cbc0 commit 5a835bd
Show file tree
Hide file tree
Showing 26 changed files with 294 additions and 92 deletions.
18 changes: 13 additions & 5 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,20 @@ on:
jobs:
build:

runs-on: ubuntu-latest
# Tag ubuntu version to 20.04, in order to support python 3.6
# See issue: https://github.com/actions/setup-python/issues/544
# When ready to drop 3.6, can revert from 'ubuntu-20.04' -> 'ubuntu-latest'
runs-on: ubuntu-20.04
env:
MODULE_NAME: fooof
strategy:
matrix:
python-version: [3.6, 3.7, 3.8, 3.9]
python-version: ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11"]

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
Expand All @@ -32,5 +37,8 @@ jobs:
- name: Test with pytest
run: |
pytest --cov=./
- name: Run doctests
run: |
pytest --doctest-modules --ignore=$MODULE_NAME/tests $MODULE_NAME
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
uses: codecov/codecov-action@v3
38 changes: 38 additions & 0 deletions CITATION.cff
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
cff-version: 1.2.0
message: "If you use this software, please cite it as below."
preferred-citation:
type: article
authors:
- family-names: "Donoghue"
given-names: "Thomas"
orcid: "https://orcid.org/0000-0001-5911-0472"
- family-names: "Haller"
given-names: "Matar"
- family-names: "Peterson"
given-names: "Erik J"
- family-names: "Varma"
given-names: "Paroma"
- family-names: "Sebastian"
given-names: "Priyadarshini"
- family-names: "Gao"
given-names: "Richard"
- family-names: "Noto"
given-names: "Torben"
- family-names: "Lara"
given-names: "Antonio H"
- family-names: "Wallis"
given-names: "Joni D"
- family-names: "Knight"
given-names: "Robert T"
- family-names: "Shestyuk"
given-names: "Avgusta"
- family-names: "Voytek"
given-names: "Bradley"
doi: "10.1038/s41593-020-00744-x"
journal: "Nature Neuroscience"
title: "Parameterizing neural power spectra into periodic and aperiodic components"
issue: 12
volume: 23
year: 2020
start: 1655
end: 1665
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# setuptools For creating distributions
#
# The following command line utilities are required:
# cloc For counting code
# cloc For counting lines of code
#

##########################################################################
Expand Down
37 changes: 27 additions & 10 deletions doc/Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
# Minimal makefile for Sphinx documentation
# This make is adapted & extended from the original sphinx file

##########################################################################
## Settings

# You can set these variables from the command line.
SPHINXOPTS =
Expand All @@ -7,6 +11,12 @@ SPHINXPROJ = fooof
SOURCEDIR = .
BUILDDIR = _build

# Custom settings
GITHUBORG = https://github.com/fooof-tools

##########################################################################
## Standard sphinx tasks (from sphinx)

# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
Expand All @@ -18,6 +28,9 @@ help:
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

##########################################################################
## Custom tasks

# Custom cleaner that also removes the generated files from sphinx-gallery
clean:
rm -rf $(BUILDDIR)/*
Expand All @@ -35,24 +48,28 @@ check:
plots:
python make_doc_plots.py

# Build the html site, and push it to gh-pages branch of repo to deploy
install:
# Clean out existing build
make clean
# Deploy the site to github pages site
deploy:
# Clone, specifically, the gh-pages branch, putting it into '_build/gh_pages/'
# -b gh-pages --single-branch fetches specifically and only the gh-pages branch
# --no-checkout just fetches the root folder without content
# --depth 1 is a speed optimization since we don't need the history prior to the last commit
# -b gh-pages fetches only the branch for the gh-pages
git clone -b gh-pages --single-branch --no-checkout --depth 1 https://github.com/fooof-tools/fooof _build/gh_pages
# A .nojekyll file tells Github pages to bypass Jekyll processing
git clone -b gh-pages --single-branch --no-checkout --depth 1 $(GITHUBORG)/$(SPHINXPROJ) _build/gh_pages

# Add a .nojekyll file to tell Github pages to bypass Jekyll processing
touch _build/gh_pages/.nojekyll
# Build the sphinx site
make html

# Copy site into the gh-pages branch folder, then push to Github to deploy
cd _build/ && \
cp -r html/* gh_pages && \
cd gh_pages && \
git add * && \
git add .nojekyll && \
git commit -a -m 'Make install' && \
git commit -a -m 'deploy docsite' && \
git push

# Clean & rebuild the html site, then deploy docsite
install:
make clean
make html
make deploy
1 change: 1 addition & 0 deletions doc/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ Utilities for working with data.

trim_spectrum
interpolate_spectrum
subsample_spectra

Parameter Utilities
~~~~~~~~~~~~~~~~~~~
Expand Down
31 changes: 12 additions & 19 deletions examples/analyses/plot_mne_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
Parameterizing neural power spectra with MNE, doing a topographical analysis.
This tutorial requires that you have `MNE <https://mne-tools.github.io/>`_
installed.
installed. This tutorial needs mne >= 1.2.
If you don't already have MNE, you can follow instructions to get it
`here <https://mne-tools.github.io/stable/getting_started.html>`_.
Expand All @@ -23,10 +23,7 @@

# Import MNE, as well as the MNE sample dataset
import mne
from mne import io
from mne.datasets import sample
from mne.viz import plot_topomap
from mne.time_frequency import psd_welch

# FOOOF imports
from fooof import FOOOFGroup
Expand All @@ -52,16 +49,15 @@
###################################################################################################

# Get the data path for the MNE example data
raw_fname = sample.data_path() + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
event_fname = sample.data_path() + '/MEG/sample/sample_audvis_filt-0-40_raw-eve.fif'
raw_fname = sample.data_path() / 'MEG' / 'sample' / 'sample_audvis_filt-0-40_raw.fif'

# Load the example MNE data
raw = mne.io.read_raw_fif(raw_fname, preload=True, verbose=False)

###################################################################################################

# Select EEG channels from the dataset
raw = raw.pick_types(meg=False, eeg=True, eog=False, exclude='bads')
raw = raw.pick(['eeg'], exclude='bads')

###################################################################################################

Expand Down Expand Up @@ -110,15 +106,16 @@ def check_nans(data, nan_policy='zero'):
# frequency representations - meaning we have to calculate power spectra.
#
# To do so, we will leverage the time frequency tools available with MNE,
# in the `time_frequency` module. In particular, we can use the ``psd_welch``
# function, that takes in MNE data objects and calculates and returns power spectra.
# in the `time_frequency` module. In particular, we can use the ``compute_psd``
# method, that takes in MNE data objects and calculates and returns power spectra.
#

###################################################################################################

# Calculate power spectra across the the continuous data
spectra, freqs = psd_welch(raw, fmin=1, fmax=40, tmin=0, tmax=250,
n_overlap=150, n_fft=300)
# Calculate power spectra across the continuous data
psd = raw.compute_psd(method="welch", fmin=1, fmax=40, tmin=0, tmax=250,
n_overlap=150, n_fft=300)
spectra, freqs = psd.get_data(return_freqs=True)

###################################################################################################
# Fitting Power Spectrum Models
Expand Down Expand Up @@ -193,7 +190,7 @@ def check_nans(data, nan_policy='zero'):
###################################################################################################

# Plot the topography of alpha power
plot_topomap(alpha_pw, raw.info, cmap=cm.viridis, contours=0);
mne.viz.plot_topomap(alpha_pw, raw.info, cmap=cm.viridis, contours=0, size=4)

###################################################################################################
#
Expand All @@ -214,8 +211,7 @@ def check_nans(data, nan_policy='zero'):
band_power = check_nans(get_band_peak_fg(fg, band_def)[:, 1])

# Create a topomap for the current oscillation band
mne.viz.plot_topomap(band_power, raw.info, cmap=cm.viridis, contours=0,
axes=axes[ind], show=False);
mne.viz.plot_topomap(band_power, raw.info, cmap=cm.viridis, contours=0, axes=axes[ind])

# Set the plot title
axes[ind].set_title(label + ' power', {'fontsize' : 20})
Expand Down Expand Up @@ -268,7 +264,7 @@ def check_nans(data, nan_policy='zero'):
###################################################################################################

# Plot the topography of aperiodic exponents
plot_topomap(exps, raw.info, cmap=cm.viridis, contours=0)
mne.viz.plot_topomap(exps, raw.info, cmap=cm.viridis, contours=0, size=4)

###################################################################################################
#
Expand Down Expand Up @@ -297,6 +293,3 @@ def check_nans(data, nan_policy='zero'):
# In this example, we have seen how to apply power spectrum models to data that is
# managed and processed with MNE.
#

###################################################################################################
#
64 changes: 32 additions & 32 deletions fooof/core/modutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,38 @@ def safe_import(*args):
return mod


def check_dependency(dep, name):
"""Decorator that checks if an optional dependency is available.
Parameters
----------
dep : module or False
Module, if successfully imported, or boolean (False) if not.
name : str
Full name of the module, to be printed in message.
Returns
-------
wrap : callable
The decorated function.
Raises
------
ImportError
If the requested dependency is not available.
"""

def wrap(func):
@wraps(func)
def wrapped_func(*args, **kwargs):
if not dep:
raise ImportError("Optional FOOOF dependency " + name + \
" is required for this functionality.")
return func(*args, **kwargs)
return wrapped_func
return wrap


def docs_drop_param(docstring):
"""Drop the first parameter description for a string representation of a docstring.
Expand Down Expand Up @@ -148,35 +180,3 @@ def wrapper(func):
return func

return wrapper


def check_dependency(dep, name):
"""Decorator that checks if an optional dependency is available.
Parameters
----------
dep : module or False
Module, if successfully imported, or boolean (False) if not.
name : str
Full name of the module, to be printed in message.
Returns
-------
wrap : callable
The decorated function.
Raises
------
ImportError
If the requested dependency is not available.
"""

def wrap(func):
@wraps(func)
def wrapped_func(*args, **kwargs):
if not dep:
raise ImportError("Optional FOOOF dependency " + name + \
" is required for this functionality.")
return func(*args, **kwargs)
return wrapped_func
return wrap
11 changes: 7 additions & 4 deletions fooof/objs/fit.py
Original file line number Diff line number Diff line change
Expand Up @@ -762,16 +762,16 @@ def _simple_ap_fit(self, freqs, power_spectrum):
# Note that these are collected as lists, to concatenate with or without knee later
off_guess = [power_spectrum[0] if not self._ap_guess[0] else self._ap_guess[0]]
kne_guess = [self._ap_guess[1]] if self.aperiodic_mode == 'knee' else []
exp_guess = [np.abs(self.power_spectrum[-1] - self.power_spectrum[0] /
np.log10(self.freqs[-1]) - np.log10(self.freqs[0]))
exp_guess = [np.abs((self.power_spectrum[-1] - self.power_spectrum[0]) /
(np.log10(self.freqs[-1]) - np.log10(self.freqs[0])))
if not self._ap_guess[2] else self._ap_guess[2]]

# Get bounds for aperiodic fitting, dropping knee bound if not set to fit knee
ap_bounds = self._ap_bounds if self.aperiodic_mode == 'knee' \
else tuple(bound[0::2] for bound in self._ap_bounds)

# Collect together guess parameters
guess = np.array([off_guess + kne_guess + exp_guess])
guess = np.array(off_guess + kne_guess + exp_guess)

# Ignore warnings that are raised in curve_fit
# A runtime warning can occur while exploring parameters in curve fitting
Expand Down Expand Up @@ -1128,7 +1128,10 @@ def _calc_error(self, metric=None):
Parameters
----------
metric : {'MAE', 'MSE', 'RMSE'}, optional
Which error measure to calculate.
Which error measure to calculate:
* 'MAE' : mean absolute error
* 'MSE' : mean squared error
* 'RMSE' : root mean squared error
Raises
------
Expand Down
5 changes: 4 additions & 1 deletion fooof/plts/annotate.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ def plot_annotated_peak_search(fm):
# 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)]

# Sort parameters by peak height
gaussian_params = fm.gaussian_params_[fm.gaussian_params_[:, 1].argsort()][::-1]

# Loop through the iterative search for each peak
for ind in range(fm.n_peaks_ + 1):

Expand All @@ -63,7 +66,7 @@ def plot_annotated_peak_search(fm):

if ind < fm.n_peaks_:

gauss = gaussian_function(fm.freqs, *fm.gaussian_params_[ind, :])
gauss = gaussian_function(fm.freqs, *gaussian_params[ind, :])
plot_spectra(fm.freqs, gauss, ax=ax, label='Gaussian Fit',
color=PLT_COLORS['periodic'], linestyle=':', linewidth=3.0)

Expand Down
4 changes: 2 additions & 2 deletions fooof/plts/fg.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ def plot_fg_ap(fg, ax=None, **plot_kwargs):
"""

if fg.aperiodic_mode == 'knee':
plot_scatter_2(fg.get_params('aperiodic_params', 'exponent'), 'Knee',
fg.get_params('aperiodic_params', 'knee'), 'Exponent',
plot_scatter_2(fg.get_params('aperiodic_params', 'exponent'), 'Exponent',
fg.get_params('aperiodic_params', 'knee'), 'Knee',
'Aperiodic Fit', ax=ax)
else:
plot_scatter_1(fg.get_params('aperiodic_params', 'exponent'), 'Exponent',
Expand Down
Loading

0 comments on commit 5a835bd

Please sign in to comment.