Skip to content

Commit

Permalink
Merge branch 'updates' into plts
Browse files Browse the repository at this point in the history
  • Loading branch information
TomDonoghue authored Jan 13, 2020
2 parents 4646940 + 3d0c388 commit bfe00e1
Show file tree
Hide file tree
Showing 51 changed files with 1,112 additions and 396 deletions.
12 changes: 6 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Thank you for your interest in contributing to FOOOF!

This documents is a set of guidelines for contributing to FOOOF on GitHub. This guide is meant to make it easy for you to get involved. Before contributing, please take a moment to read over this page to get a sense of the scope of the toolbox, and the preferred procedures for requesting features, reporting issues, and making contributions.
This documents is a set of guidelines for contributing to FOOOF on GitHub. This guide is meant to make it easy for you to get involved. Before contributing, please take a moment to read over this page to get a sense of the scope of the toolbox, and the preferred procedures for requesting features, reporting issues, and making contributions.

* [Participation guidelines](#participation-guidelines)
* [How to report bugs](#how-to-report-bugs)
Expand All @@ -12,7 +12,7 @@ This documents is a set of guidelines for contributing to FOOOF on GitHub. This

## Participation guidelines

Please note that this project adheres to a [code of conduct](https://github.com/fooof-tools/fooof/blob/master/CODE_OF_CONDUCT.md), that you are expected to uphold when participating in this project.
Please note that this project adheres to a [code of conduct](https://github.com/fooof-tools/fooof/blob/master/CODE_OF_CONDUCT.md), that you are expected to uphold when participating in this project.

## How to report bugs

Expand All @@ -25,15 +25,15 @@ If you are reporting a bug, please submit it to our [issue tracker](https://gith

## How to suggest changes or updates

If there is a feature you would like to see, please submit it as an [issue](https://github.com/fooof-tools/fooof/issues), with a brief description of what you would like to see added / changed, and why. If it is feature that you would be willing to implement, please indicate that and, and follow the guidelines below for making a contribution.
If there is a feature you would like to see, please submit it as an [issue](https://github.com/fooof-tools/fooof/issues), with a brief description of what you would like to see added / changed, and why. If it is feature that you would be willing to implement, please indicate that and, and follow the guidelines below for making a contribution.

Note that, in terms of scope, FOOOF is quite specifically focused on it's core functionality of parameterizing neural power spectra, and helper utilities to visualize and work with model results, as well as tools to create synthetic power spectra. Procedures and utilities that do not deal with operating upon power spectra or FOOOF outputs directly will most likely be considered out of scope, and won't be added into FOOOF.

## How to submit code

If there is a feature you would like to add, or an issue you saw that you think you can help with, you are ready to make a code submission to the project!

If you are working on a feature, please indicate so in the relevant issue, so that we can keep track of who is working on what.
If you are working on a feature, please indicate so in the relevant issue, so that we can keep track of who is working on what.

Once you're ready to start working on your contribution, do the following:

Expand All @@ -46,14 +46,14 @@ If it's your first time contributing to open source, check out this free resourc

## Style guidelines

All code that is to be added to FOOOF must follow the code conventions of the project.
All code that is to be added to FOOOF must follow the code conventions of the project.

FOOOF follows the following conventions:
- Code style should follow [PEP8](https://www.python.org/dev/peps/pep-0008/)
- Merge candidate code will be checked using [pylint](https://www.pylint.org)
- Max line length is 100 characters
- All functions should be unit tested, using [pytest](https://docs.pytest.org/en/latest/)
- Merge candidates must pass all existing tests, and add new tests such as to not reduce test coverage
- All code shouod be documented, following the [numpy docs](https://numpydoc.readthedocs.io/en/latest/format.html#docstring-standard) format
- All code should be documented, following the [numpy docs](https://numpydoc.readthedocs.io/en/latest/format.html#docstring-standard) format

For more guidelines on how to write well formated and organized code, check out the [Python API Checklist](http://python.apichecklist.com).
217 changes: 145 additions & 72 deletions doc/faq.rst

Large diffs are not rendered by default.

32 changes: 16 additions & 16 deletions examples/plot_mne_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
###################################################################################################

# Creating Power Spectra Densities
spectra, freqs = mne.time_frequency.psd_welch(epochs, fmin=1., fmax=50., n_fft=2000,
spectra, freqs = mne.time_frequency.psd_welch(epochs, fmin=1., fmax=50., n_fft=2000,
n_overlap=250, n_per_seg=500)

###################################################################################################
Expand All @@ -95,8 +95,8 @@

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

# Selecting the first epoch of data to FOOOF
spectra = np.squeeze(spectra[0,:,:])
# Selecting the first epoch of data to FOOOF
spectra = np.squeeze(spectra[0,:,:])
n_channels, n_freq = spectra.shape
num_blocks = len(mne.read_events(event_fname))

Expand All @@ -115,30 +115,30 @@

# Define bands of interest
bands = {'theta': [3, 7],
'alpha': [7, 14],
'alpha': [7, 14],
'beta': [15, 30]}

# Create dictionaries to store all the periodic properties across frequencies
# Create dictionaries to store all the periodic properties across frequencies
results = {}
for band_name in bands.keys():
results[band_name] = np.zeros(shape=[num_blocks, n_channels, len(feats)])

# Creating dictionaries to store all the aperiodic properties across frequencies
# Creating dictionaries to store all the aperiodic properties across frequencies
exponent_results = np.zeros(shape=[num_blocks, n_channels, len(aperiodic_feats)])

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

# Populating periodic and aperiodic values
# Populating periodic and aperiodic values
for block in range(0, num_blocks):
for ind, res in enumerate(fg):
exponent_results[block, ind, :] = res.aperiodic_params
for band_label, band_range in bands.items():
results[band_label][block, ind, :] = get_band_peak(res.peak_params, band_range, True)

###################################################################################################
# Plotting Topographies
# Plotting Topographies
# ---------------------
#
#
# Now we can plot the extracted FOOOF features across all channels.
#

Expand Down Expand Up @@ -169,14 +169,14 @@ def plot_topo_colorbar(vmin, vmax, label):
topo_dat = np.mean(cur_data,0)

###################################################################################################
# Looking at the alpha center frequeuncy
print('CURRENT FEATURE:', feats[0])

# Looking at the alpha center frequency
print('CURRENT FEATURE:', feats[0])
disp_dat = topo_dat[:,0]

inds = np.where(np.isnan(disp_dat))
disp_dat[inds] = np.nanmean(disp_dat)

vbuffer = 0.1 * (disp_dat.max() - disp_dat.min())
vmin, vmax, = disp_dat.min() - vbuffer, disp_dat.max() + vbuffer

Expand All @@ -190,13 +190,13 @@ def plot_topo_colorbar(vmin, vmax, label):
cur_data = exponent_results

topo_dat = np.mean(cur_data,0)
print('CURRENT FEATURE:', aperiodic_feats[1])

print('CURRENT FEATURE:', aperiodic_feats[1])
disp_dat = topo_dat[:,1]

inds = np.where(np.isnan(disp_dat))
disp_dat[inds] = np.nanmean(disp_dat)

vbuffer = 0.1 * (disp_dat.max() - disp_dat.min())
vmin, vmax, = disp_dat.min() - vbuffer, disp_dat.max() + vbuffer

Expand Down
2 changes: 1 addition & 1 deletion examples/plot_sim_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@
# The :func:`param_iter` function can be used to create iterators that can 'step' across
# a range of parameter values to be simulated.
#
# The :class:`Stepper` object needs to be used in conjuction with :func:`param_iter`,
# The :class:`Stepper` object needs to be used in conjunction with :func:`param_iter`,
# as it specifies the values to be iterated across.
#

Expand Down
2 changes: 1 addition & 1 deletion examples/plot_simulated_power_spectra.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@
###################################################################################################
#
# Note that when you simulate a group of power spectra, FOOOF returns SimParam objects that
# keep track of the simulations. This, and other utilties to manage parameters and provide
# keep track of the simulations. This, and other utilities to manage parameters and provide
# parameter definitions for simulating groups of power spectra are covered in the
# `Simulated Parameters` example.
#
17 changes: 5 additions & 12 deletions fooof/bands.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def __getitem__(self, label):
return self.bands[label]
except KeyError:
message = "The label '{}' was not found in the defined bands.".format(label)
raise BandNotDefinedError(message) from None
raise ValueError(message) from None

def __getattr__(self, label):

Expand Down Expand Up @@ -108,25 +108,18 @@ def _check_band(label, band_definition):
Raises
------
InconsistentDataError
ValueError
If band definition is not properly formatted.
"""

# Check that band name is a string
if not isinstance(label, str):
raise InconsistentDataError('Band name definition is not a string.')
raise ValueError("Band name definition is not a string.")

# Check that band limits has the right size
if not len(band_definition) == 2:
raise InconsistentDataError('Band limit definition is not the right size.')
raise ValueError("Band limit definition is not the right size.")

# Safety check that limits are in correct order
if not band_definition[0] < band_definition[1]:
raise InconsistentDataError('Band limit definitions are invalid.')


class BandNotDefinedError(Exception):
pass

class InconsistentDataError(Exception):
pass
raise ValueError("Band limit definitions are invalid.")
106 changes: 106 additions & 0 deletions fooof/checks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
"""Functions to check FOOOF models."""

import numpy as np

from fooof.sim.gen import gen_model
from fooof.plts.spectra import plot_spectrum_error
from fooof.core.errors import ModelNotFitError, NoDataError

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

def compute_pointwise_error(model, data):
"""Calculate the frequency by frequency error of the model fit.
Parameters
----------
model : 1d array
The model instantiation.
data : 1d array
The original data that is being modeled.
Returns
-------
1d array
Calculated values of the difference between the data and the model.
"""

return np.abs(model - data)


def compute_pointwise_error_fm(fm, plot_errors=True, return_errors=False, **plt_kwargs):
"""Calculate the frequency by frequency error of the model fit from a FOOOF object.
Parameters
----------
fm : FOOOF
A FOOOF object containing the data and model.
plot_errors : bool, optional, default: True
Whether to plot the errors across frequencies.
return_errors : bool, optional, default: False
Whether to return the calculated errors.
**plt_kwargs
Keyword arguments to be passed to the plot function.
Returns
-------
errors : 1d array
Calculated values of the difference between the data and the model.
"""

if not fm.has_data:
raise NoDataError("Data must be available in the object to calculate errors.")
if not fm.has_model:
raise ModelNotFitError("No model is available to use, can not proceed.")

errors = compute_pointwise_error(fm.fooofed_spectrum_, fm.power_spectrum)

if plot_errors:
plot_spectrum_error(fm.freqs, errors, **plt_kwargs)

if return_errors:
return errors


def compute_pointwise_error_fg(fg, plot_errors=True, return_errors=False, **plt_kwargs):
"""Calculate the frequency by frequency error of the model fit from a FOOOFGroup object.
Parameters
----------
fg : FOOOFGroup
A FOOOFGroup object containing the data and model.
plot_errors : bool
Whether to plot the errors across frequencies.
return_errors : bool
Whether to return the calculated errors.
**plt_kwargs
Keyword arguments to be passed to the plot function.
Returns
-------
errors : 2d array
Calculated values of the difference between the data and the models.
"""

if not fg.has_model:
raise ModelNotFitError("No model is available to use, can not proceed.")
if not np.any(fg.power_spectra):
raise NoDataError("Data must be available in the object to calculate errors.")

errors = np.zeros_like(fg.power_spectra)

for ind, (res, data) in enumerate(zip(fg, fg.power_spectra)):

model = gen_model(fg.freqs, res.aperiodic_params, res.gaussian_params)
errors[ind, :] = np.abs(model - data)

mean = np.mean(errors, 0)
standard_dev = np.std(errors, 0)

if plot_errors:
plot_spectrum_error(fg.freqs, mean, standard_dev, **plt_kwargs)

if return_errors:
return errors


25 changes: 25 additions & 0 deletions fooof/core/errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""Custom error definitions for FOOOF."""

class FOOOFError(Exception):
"""Base class for errors in the FOOOF module."""
pass

class NoDataError(FOOOFError):
"""Error for if data is missing."""
pass

class DataError(FOOOFError):
"""Error if there is a problem with the data."""
pass

class InconsistentDataError(FOOOFError):
"""Error for if the data is inconsistent."""
pass

class IncompatibleSettingsError(FOOOFError):
"""Error for if settings are incompatible."""
pass

class ModelNotFitError(FOOOFError):
"""Error for if the model is not fit."""
pass
Loading

0 comments on commit bfe00e1

Please sign in to comment.