diff --git a/README.md b/README.md index fdd1b556..d6df0563 100644 --- a/README.md +++ b/README.md @@ -3,57 +3,118 @@ [![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](http://www.repostatus.org/badges/latest/active.svg)](http://www.repostatus.org/#active) [![Build Status](https://travis-ci.org/voytekresearch/fooof.svg)](https://travis-ci.org/voytekresearch/fooof) [![codecov](https://codecov.io/gh/voytekresearch/fooof/branch/master/graph/badge.svg)](https://codecov.io/gh/voytekresearch/fooof) +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) -WARNING: FOOOF is not yet tagged for a stable release version, and updates may change the internal workings, and/or the API (although major changes to API are unlikely). Prior to tagged release, tutorials may not be kept entirely up to date, but in-file documentation will be. +FOOOF is a fast, efficient, physiologically-informed model to parameterize neural power spectra, characterizing both the 1/f background, and overlying peaks (putative oscillations). ---- +The model conceives of the neural power spectrum as consisting of two distinct functional processes: +- A 1/f background, modeled with an exponential fit, with: +- Band-limited peaks rising above this background (modeled as Gaussians). -FOOOF is a fast, efficient, physiologically-informed model to parameterize neural power spectra. - -The model conceives of the neural power spectral density (PSD) as consisting of two distinct functional processes: 1) A 1/f background modeled as a curve in log-log space with; 2) Band-limited oscillatory "bumps" rising above this background, modeled as Gaussians in log(power) space. - -Note that this conception of the 1/f as potentially functional (and therefore worth carefully modeling) is based on work from our lab suggesting that the 1/f slope may index excitation/inhibition balance ([Gao, Peterson, Voytek, _NeuroImage_ 2017](http://voyteklab.com/wp-content/uploads/Gao-NeuroImage2017.pdf); [Voytek & Knight, _Biol Psychiatry_ 2015](http://voyteklab.com/wp-content/uploads/Voytek-BiolPsychiatry2015.pdf)). At the very least, however, the 1/f appears to change with task ([Podvalny _et al._, _J Neurophysiol_ 2015](http://www.weizmann.ac.il/neurobiology/labs/malach/sites/neurobiology.labs.malach/files/Podvalny%20et%20al_2015_JNeurophysiol.pdf)), with aging ([Voytek _et al._, _J Neurosci_ 2015](http://voyteklab.com/wp-content/uploads/Voytek-JNeurosci2015.pdf)). +With regards to examing peaks in the frequency domain, as putative oscillations, the benefit of the FOOOF approach is that these peaks are characterized in terms of their specific center frequency, amplitude and bandwidth without requiring predefining specific bands of interest. In particular, it separates these peaks from a dynamic, and independently interesting 1/f background. This conception of the 1/f as potentially functional (and therefore worth carefully modeling) is based on work from our lab suggesting that the 1/f slope may index excitation/inhibition balance ([Gao, Peterson, Voytek, _NeuroImage_ 2017](http://voyteklab.com/wp-content/uploads/Gao-NeuroImage2017.pdf); [Voytek & Knight, _Biol Psychiatry_ 2015](http://voyteklab.com/wp-content/uploads/Voytek-BiolPsychiatry2015.pdf)). At the very least, however, the 1/f appears to change with task ([Podvalny _et al._, _J Neurophysiol_ 2015](http://www.weizmann.ac.il/neurobiology/labs/malach/sites/neurobiology.labs.malach/files/Podvalny%20et%20al_2015_JNeurophysiol.pdf)), with aging ([Voytek _et al._, _J Neurosci_ 2015](http://voyteklab.com/wp-content/uploads/Voytek-JNeurosci2015.pdf)). ## Python Version -FOOOF is written and tested in Python 3.6. It is not currently tested or supported on earlier versions (but should work on earlier versions of Python3 and should be compatible with Python2.7) +FOOOF runs on Python 3.5 and 3.6. ## Dependencies - numpy -- matplotlib - scipy >= 0.19 +- matplotlib (optional) +- pytest (optional) + +That is, if you are using [Anaconda](https://www.anaconda.com/download/), then you are good to go. + +If you aren't using Anaconda, it is one way to get and manage these dependencies. + +Matplotlib is not required for running the model fitting, but is used if you want to visualize model fits. + +Pytest is only required to run the test suite. + +## Install + +To install the latest stable release of fooof, you can use pip: + +`$ pip install fooof` + +Note that this will install only the core (non-optional) fooof requirements. + +If you don't have matplotlib installed, and want to include it for plotting functionally, install using: + +`$ pip install fooof[plot]` + +## Development Branch + +To get the lastest, development version, you can get the code using git: + +`$ git clone https://github.com/voytekresearch/fooof` + +To then install the development version (without making changes to it), move into the directory you cloned and run: + +`$ pip install .` + +Otherwise, if you want to install an editable, development version, move into the directory you cloned and install with: + +`$ pip install -e .` ## Usage -FOOOF is object oriented. With a power spectrum loaded (with 'freqs' storing frequency values, and 'psd' storing power values, both as 1D arrays in linear space) FOOOF can be used as follows: +FOOOF is object oriented, and generally follows a similar approach as used in scikit-learn. + +With a power spectrum loaded (with 'freqs' storing frequency values, and 'spectrum' storing the power spectrum, both as 1D arrays in linear space) FOOOF can be used as follows: ```python from fooof import FOOOF # Initialize FOOOF object -foof_model = FOOOF() +fm = FOOOF() -# Define frequency range to model PSD +# Define frequency range across which to model the spectrum freq_range = [3, 40] -# Model the PSD with FOOOF -foof_model.model(freqs, psd, freq_range) +# Model the power spectrum with FOOOF, and print out a report +fm.report(freqs, spectrum, freq_range) ``` -FOOOF.model() fits the model, plots the original PSD with the associated model of the PSD, and prints out the parameters of the model fit for both background 1/f (offset, knee, exponent) and Gaussian parameters (center frequency, amplitude, and bandwidth) for any identified oscillations. +FOOOF.report() fits the model, plots the original power spectrum with the associated FOOOF model, and prints out the parameters of the model fit for both background 1/f and Gaussian parameters for any identified peaks. FOOOF also accepts parameters for fine-tuning the fit. For example: ```python -foof_model = FOOOF(bandwidth_limits=(1.0,15.0), max_n_oscs=6, min_amp=0.1, amp_std_thresh=2.0) +fm = FOOOF(peak_width_limits=[1.0, 8.0], max_n_peaks=6, min_peak_amplitude=0.1, peak_threshold=2.0) ``` -* _bandwidth_limits_ sets the possible lower- and upper-bounds for the fitted Gaussians bandwidths to 1.0 and 15.0 Hz, respectively. -* _max_n_oscs_ sets the maximum number of oscillations to find (in decreasing order of amplitude). Physiologically, rarely are there ever more than 5 or 6. This helps minimize overfitting. -* _min_amp_ sets a hard limit on the maximum amplitude (above background 1/f) for any oscillation; that is, bumps below this amplitude will be ignored. -* _amp_std_thresh_, similar to _min_amp_, sets a threshold above which oscillation amplitude must cross to be included in the model. However this parameter is in terms of standard deviation above the noise of the flattened spectrum. +* _peak_width_limits_ sets the possible lower- and upper-bounds for the fitted peak widths. +* _max_n_peaks_ sets the maximum number of peaks to fit (in decreasing order of amplitude). +* _min_peak_amp_ sets an absolute limit on the minimum amplitude (above background) for any extracted peak. +* _peak_threshold_, also sets a threshold above which a peak amplitude must cross to be included in the model. This parameter is in terms of standard deviation above the noise of the flattened spectrum. + +FOOOF also has convenience methods for running the FOOOF model across matrices of multiple power spectra, as well as functionality for saving and loading results, creating reports from FOOOF outputs, and utilities to further analize FOOOF results. + +An example workflow, with 'freqs' as 1D array of frequency values, and 'spectra' as a 2D array of power spectra. + +```python + +# Initialize a FOOOFGroup object, specifying some parameters +fg = FOOOFGroup(peak_width_limits=[1.0, 8.0], max_n_peaks=8) + +# Fit FOOOF model across the matrix of power spectra +fg.fit(freqs, spectra) + +# Create and save out a report summarizing the results across the group of power spectra +fg.save_report() + +# Save out FOOOF results for further analysis later +fg.save(file_name='fooof_group_results', save_results=True) +``` ## Output -Example output for MEG data: -![alt text](img/fooof_output.png "Example FOOOF output for MEG data") +Example output for a FOOOF fit of MEG data: + +!["fooof_report"](img/FOOOF_Report.png) + + +Example output for running FOOOF across a group of power spectra (with FOOOFGroup): + +!["fooof_group_report"](img/FOOOFGroup_Report.png) diff --git a/fooof/__init__.py b/fooof/__init__.py index 9353c73f..5ee434cb 100644 --- a/fooof/__init__.py +++ b/fooof/__init__.py @@ -1,2 +1,6 @@ +"""FOOOF - Fitting Oscillations & One-Over F""" + +from .version import __version__ + from .fit import FOOOF from .group import FOOOFGroup diff --git a/fooof/version.py b/fooof/version.py new file mode 100644 index 00000000..541f859d --- /dev/null +++ b/fooof/version.py @@ -0,0 +1 @@ +__version__ = '0.1.0' \ No newline at end of file diff --git a/img/FOOOFGroup_Report.png b/img/FOOOFGroup_Report.png new file mode 100644 index 00000000..b232bc85 Binary files /dev/null and b/img/FOOOFGroup_Report.png differ diff --git a/img/FOOOF_Report.png b/img/FOOOF_Report.png new file mode 100644 index 00000000..764ef6b3 Binary files /dev/null and b/img/FOOOF_Report.png differ diff --git a/img/fooof_output.png b/img/fooof_output.png deleted file mode 100644 index b9f7a62d..00000000 Binary files a/img/fooof_output.png and /dev/null differ diff --git a/setup.py b/setup.py index fba72c56..73933d0a 100644 --- a/setup.py +++ b/setup.py @@ -1,17 +1,69 @@ """FOOOF setup script.""" +import os from setuptools import setup, find_packages +# Get the current version number from inside the module +with open(os.path.join('fooof', 'version.py')) as vf: + exec(vf.read()) + +# Copy in long description. +# Note: this is a partial copy from the README +# Only update here in coordination with the README, to keep things consistent. +long_description = \ +""" +======================================== +FOOOF: Fitting Oscillations & One-Over F +======================================== + +FOOOF is a fast, efficient, physiologically-informed model to parameterize +neural power spectra, characterizing both the 1/f background, and overlying +peaks (putative oscillations). + +The model conceives of the neural power spectrum as consisting of two distinct functional processes: +- A 1/f background, modeled with an exponential fit, with: +- Band-limited peaks rising above this background (modeled as Gaussians). + +With regards to examing peaks in the frequency domain, as putative oscillations, the benefit +of the FOOOF approach is that these peaks are characterized in terms of their specific center +frequency, amplitude and bandwidth without requiring predefining specific bands of interest. +In particular, it separates these peaks from a dynamic, and independently interesting 1/f +background. This conception of the 1/f as potentially functional (and therefore worth carefully +modeling) is based on work from the Voytek lab and others that collectively shows that 1/f changes +across task demands and participant demographics, and that it may index underlying +excitation/inhibition (EI) balance. +""" + setup( name = 'fooof', - version = '0.1.0', + version = __version__, description = 'Fitting oscillations & one-over f', + long_description = long_description, author = 'The Voytek Lab', author_email = 'voyteklab@gmail.com', url = 'https://github.com/voytekresearch/fooof', - packages=find_packages(), - license='MIT', - download_url = '', + packages = find_packages(), + license = 'Apache License, 2.0', + classifiers = [ + 'Development Status :: 4 - Beta', + 'Intended Audience :: Science/Research', + 'Topic :: Scientific/Engineering', + 'License :: OSI Approved :: Apache Software License', + 'Operating System :: MacOS :: MacOS X', + 'Operating System :: Microsoft :: Windows', + 'Operating System :: POSIX :: Linux', + 'Operating System :: Unix', + 'Programming Language :: Python', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6' + ], + download_url = 'https://github.com/voytekresearch/fooof/releases', keywords = ['neuroscience', 'neural oscillations', 'power spectra', '1/f', 'electrophysiology'], - classifiers = [] + install_requires = ['numpy', 'scipy>=0.19.0'], + tests_require = ['pytest'], + extras_require = { + 'plot' : ['matplotlib'], + 'tests' : ['pytest'], + 'all' : ['matplotlib', 'pytest'] + } ) \ No newline at end of file