Skip to content

Commit

Permalink
Merge pull request #17 from fooof-tools/dev
Browse files Browse the repository at this point in the history
Update fooof_mat to match FOOOF 1.0
  • Loading branch information
TomDonoghue committed Apr 28, 2020
2 parents 5b25794 + cd39963 commit 018b618
Show file tree
Hide file tree
Showing 13 changed files with 147 additions and 81 deletions.
102 changes: 61 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,76 +1,96 @@
# FOOOF - Matlab Wrapper

This repository offers a Matlab wrapper for [FOOOF](https://github.com/fooof-tools/fooof) with Matlab.
This repository offers a Matlab wrapper for [FOOOF](https://github.com/fooof-tools/fooof).

All [descriptions](https://github.com/fooof-tools/fooof/README.md) and [tutorials](https://github.com/fooof-tools/fooof/tutorial) for FOOOF are in the [main repository](https://github.com/fooof-tools/fooof), and a full description of the method is available in the [paper](https://www.biorxiv.org/content/early/2018/04/11/299859).
The main documentation for FOOOF is on the [documentation site](https://fooof-tools.github.io/fooof/).

This repository describes the Matlab wrapper, in which you call the Python implementation of FOOOF from Matlab, never having to interact directly with Python. An alternative approach is to use a primarily
This repository describes the Matlab wrapper, in which you call the Python implementation of FOOOF from Matlab, never having to interact directly with Python.

There are two approaches offered here for using FOOOF with Matlab - a Matlab Wrapper, and a Matlab->Python->Matlab workflow. Note that these options both still use the Python implementation under the hood, and so do require a working Python install - but that should be easy to do, and instructions are provided below to do so.

### Reference

If you use this code in your project, please cite:

Haller M, Donoghue T, Peterson E, Varma P, Sebastian P, Gao R, Noto T, Knight RT, Shestyuk A,
Voytek B (2018) Parameterizing Neural Power Spectra. bioRxiv, 299859.
doi: https://doi.org/10.1101/299859
Note that, as an alternative to using the wrapper from Matlab, you can also try the
[Matlab->Python->Matlab](https://github.com/fooof-tools/mat_py_mat)
approach, in which there are examples for using FOOOF, in Python, integrated into a primarily Matlab workflow.

## FOOOF_MAT

The Matlab wrapper, is Matlab code that calls the Python implementation of FOOOF. This requires that you have Python & FOOOF installed, but does not require you to ever use or write Python code yourself.
The Matlab wrapper is Matlab code that calls the Python implementation of FOOOF. This requires that you have Python & FOOOF installed, but does not require you to ever use or write Python code yourself.

To use the wrapper, first install Python & FOOOF - there are instructions to do so below. Then clone or download this repository, and then use the the provided matlab code to run FOOOF. The only function you need to run is 'fooof.m', which has documentation on inputs and outputs.
To use the wrapper, first install Python & FOOOF - there are instructions to do so below. Then clone or download this repository, and use the provided Matlab code to run FOOOF. Typically, the only function you will need to run is 'fooof.m', which has documentation on inputs and outputs.

Note that this is a very minimal wrapper - it provides access only to the core algorithm, and not to any of the extra utilities, such as plotting the model outputs. As the algorithm is really the purpose of FOOOF, you are not lacking any functionality in that sense - all the inputs settings and model outputs are available to you.
Note that this is a very minimal wrapper - it provides access only to the core algorithm, and does not offer access to most of the extra utilities in the Python module. However, since the algorithm is the core purpose of FOOOF, you do have full access to the model itself, including all inputs settings and model outputs.

#### Dependencies

This Matlab wrapper uses the Python support introduced by Matlab in 2014b, and as such requires that version, or higher, to run.

#### pyversion
## Installing Python & FOOOF

Both workflows above require that Python & FOOOF be installed. The easiest way to do this is as follows:

#### Install Python

To call Python from Matlab, you will need to have Python installed.

Once you have downloaded Python, you shouldn't need to do anything for Matlab to be able to call Python - as long as your Matlab is using the correct Python. If it's not working, this is likely the problem.
One option to install Python, as well as all the dependencies you need and including tools like Jupyter notebook, is to use the Anaconda distribution. To do so, go to the [Anaconda Website](https://www.anaconda.com/download/) and download the latest version Python3 version available. Install the file you download, and then you should be good to go!

Note that your computer may already have a version of Python, but you should still go an install a new one. This ensures that you can have a new version of Python where you can install new modules, without interfering with your system Python.

You can also install Python without using Anaconda, for example directly from [python.org](https://www.python.org/downloads/). This might be useful, as Matlab doesn't always seem to work well with the Anaconda distribution.

If you are having trouble getting Python set up with Matlab, this
[blog post](https://irenevigueguix.wordpress.com/2020/03/25/loading-python-into-matlab/)
also offers a step-by-step guide.

#### Install FOOOF

FOOOF can be installed through pip, meaning you just have to run the following from the command line:

`pip install fooof`

If you're on mac, 'command line' means terminal - after installing anaconda, just copy the above command into the terminal, and it should work. If you're on windows, you will need to run this in 'anaconda command prompt' which is basically a command line specifically for managing Python with Anaconda.

You can run `pyversion` to see which Python you are using. Note that you must do this _after installing Python and FOOOF_ (instruction to do so below).
#### Calling Python from Matlab

Once you have downloaded Python, you shouldn't need to do anything for Matlab to be able to
[call Python](https://www.mathworks.com/help/matlab/call-python-libraries.html).

The most common problem seems to be if Matlab is not using the correct version or location for Python. If calling Python is not working, then checking and setting where Matlab is calling Python from is likely the solution.

To check and update which Python Matlab is using, you can use
[pyversion](https://www.mathworks.com/help/matlab/ref/pyversion.html), or
[pyenv](https://www.mathworks.com/help/matlab/ref/pyenv.html)
if you are on a newer version of Matlab (>= R2019b).

For example, you can run `pyversion` to see which Python you are using, and update it if required.

Note that you must do this _after installing Python and FOOOF_.
```
% Check which python is being used.
% Check which python is being used
pyversion
% The print out from above should tell you which Python you are calling
% It should show that you are using Python version 3.6.
% It should also show that the 'home' of this python is in the anaconda folder
% It should show that you are using Python version 3.X
% If you are using anaconda, it should show your Python is in the anaconda folder
% If either of these things are not right, reset which Python you are using, as below
% Set python version to use
% Note: you must do this first thing after opening Matlab (relaunch if you need to)
% You should only ever have to run this at most, once.
% Note: you're anaconda folder might be `anaconda3` instead of `anaconda`
% You might need to change the path to where your python or anaconda install is
% For example, your anaconda folder might be `anaconda3` instead of `anaconda`
% or your anaconda path might be somewhere else, for example, '/opt/anaconda3/bin/python'
pyversion('/anaconda/bin/python')
```

## Installing Python & FOOOF

Both workflows above require that Python & FOOOF be installed. The easiest way to do this is as follows:

#### Install Python with Anaconda

To install Python, as well as all the dependencies you need and including tools like Jupyter notebook, go to the [Anaconda Website](https://www.anaconda.com/download/) and download the Python 3.6 version. Install the file you download, and then you're good to go!

Note that your computer may well have a version of Python - but you should still go an install a new one with Anaconda - this ensures that you have all the dependencies you need, and leaves your system Python alone, for safety.
## Reference

Once you've downloaded Anaconda, you will have a program called 'Anaconda Navigator', which you can use to launch Juypter notebooks, if you want. For more information on using notebooks, check out [project Jupyter](http://jupyter.org).

#### Install FOOOF

FOOOF can be installed through pip, meaning you just have to run the following from the command line:

`pip install fooof`
If you use this code in your project, please cite:

If you're on mac, 'command line' means terminal - after installing anaconda, just copy the above command into the terminal, and it should work. If you're on windows, you will need to run this in 'anaconda command prompt' which is basically a command line specifically for managing Python with Anaconda.
Haller M, Donoghue T, Peterson E, Varma P, Sebastian P, Gao R, Noto T, Knight RT, Shestyuk A,
Voytek B (2018) Parameterizing Neural Power Spectra. bioRxiv, 299859.
doi: https://doi.org/10.1101/299859

## Potential Matlab Implementation

The above workflows still use the Python implementation of FOOOF. This has some perks, in that running the exact same code means that there are no worries about maintaining and verying the consistency between multiple implementations of the code. However, it does still require this somewhat annoying coordinating between languages, if one wants to use Matlab. The only way to get around this would be to have a re-implemenation of the algorithm in Matlab, in which case it could be used in a stand-alone manner.
This Matlab wrapper still uses the Python implementation of FOOOF.

As of right now, there are no plans on our end for a full re-implementation of the algorithm in Matlab - it's non-trivial to re-write, test, confirm equivalence, and then continuously maintain two versions. That said, the code is open, so if want to try and do this yourself, go for it!
As of right now, there are no plans for us to create a full re-implementation of the algorithm in Matlab, as it is a large project to re-write, test, confirm equivalence, and then continuously maintain two versions.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
%% FOOOF Matlab Wrapper Example - Multiple PSDs
%
% This example computes example power spectra models on a group of
% power spectra, and prints out the results.
%

%% Run Example

% Load data
load('dat/ch_dat_one.mat');
load('dat/ch_dat_two.mat');
load('data/ch_dat_one.mat');
load('data/ch_dat_two.mat');

% Combine into a multi-channel data matrix
chs_dat = [ch_dat_one; ch_dat_two]';

% Calculate power spectra with Welch's method
[psds, freqs] = pwelch(chs_dat, 500, [], [], s_rate);

% Transpose, to make FOOOF inputs row vectors
% Transpose, to make inputs row vectors
freqs = freqs';

% FOOOF settings
settings = struct();
f_range = [1, 50];
f_range = [1, 30];

% Run FOOOF across a group of power spectra
fooof_results = fooof_group(freqs, psds, f_range, settings);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
%% FOOOF Matlab Wrapper Example - Single PSD
%
% This example computes an example power spectrum model for a single
% power spectrum, and prints out the results.
%

%% Run Example

% Load data
load('dat/ch_dat_one.mat');
load('data/ch_dat_one.mat');

% Calculate a power spectrum with Welch's method
[psd, freqs] = pwelch(ch_dat_one, 500, [], [], s_rate);

% Transpose, to make FOOOF inputs row vectors
% Transpose, to make inputs row vectors
freqs = freqs';
psd = psd';

% FOOOF settings
settings = struct(); % Use defaults
f_range = [1, 50];
f_range = [1, 30];

% Run FOOOF
fooof_results = fooof(freqs, psd, f_range, settings);
Expand Down
27 changes: 27 additions & 0 deletions examples/fooof_example_plot_model.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
%% FOOOF Matlab Wrapper Example - Plot a FOOOF model
%
% This example computes an example power spectrum model fit,
% and then plots the result.
%

%% Run Example

% Load data
load('data/ch_dat_one.mat');

% Calculate a power spectrum with Welch's method
[psd, freqs] = pwelch(ch_dat_one, 500, [], [], s_rate);

% Transpose, to make inputs row vectors
freqs = freqs';
psd = psd';

% FOOOF settings
settings = struct(); % Use defaults
f_range = [1, 30];

% Run FOOOF, also returning the model
fooof_results = fooof(freqs, psd, f_range, settings, true);

% Plot the resulting model
fooof_plot(fooof_results)
22 changes: 16 additions & 6 deletions fooof_mat/fooof.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@
% settings = fooof model settings, in a struct, including:
% settings.peak_width_limts
% settings.max_n_peaks
% settings.min_peak_amplitude
% settings.min_peak_height
% settings.peak_threshold
% settings.background_mode
% settings.aperiodic_mode
% settings.verbose
% return_model = boolean of whether to return the FOOOF model fit, optional
%
% Outputs:
% fooof_results = fooof model ouputs, in a struct, including:
% fooof_results.background_params
% fooof_results.aperiodic_params
% fooof_results.peak_params
% fooof_results.gaussian_params
% fooof_results.error
Expand All @@ -27,7 +27,7 @@
% fooof_results.freqs
% fooof_results.power_spectrum
% fooof_results.fooofed_spectrum
% fooof_results.bg_fit
% fooof_results.ap_fit
%
% Notes
% Not all settings need to be defined by the user.
Expand All @@ -47,9 +47,9 @@
% Initialize FOOOF object
fm = py.fooof.FOOOF(settings.peak_width_limits, ...
settings.max_n_peaks, ...
settings.min_peak_amplitude, ...
settings.min_peak_height, ...
settings.peak_threshold, ...
settings.background_mode, ...
settings.aperiodic_mode, ...
settings.verbose);

% Run FOOOF fit
Expand All @@ -58,6 +58,16 @@
% Extract outputs
fooof_results = fm.get_results();
fooof_results = fooof_unpack_results(fooof_results);

% Re-calculating r-squared
% r_squared doesn't seem to get computed properly (in NaN).
% It is unclear why this happens, other than the error can be traced
% back to the internal call to `np.cov`, and fails when this function
% gets two arrays as input.
% Therefore, we can simply recalculate r-squared
coefs = corrcoef(double(py.array.array('d', fm.power_spectrum)), ...
double(py.array.array('d', fm.fooofed_spectrum_)));
fooof_results.r_squared = coefs(2);

% Also return the actual model fit, if requested
% This will default to not return model, if variable not set
Expand Down
12 changes: 6 additions & 6 deletions fooof_mat/fooof_check_settings.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@
% settings = struct, can optionally include:
% settings.peak_width_limts
% settings.max_n_peaks
% settings.min_peak_amplitude
% settings.min_peak_height
% settings.peak_threshold
% settings.background_mode
% settings.aperiodic_mode
% settings.verbose
%
% Outputs:
% settings = struct, with all settings defined:
% settings.peak_width_limts
% settings.max_n_peaks
% settings.min_peak_amplitude
% settings.min_peak_height
% settings.peak_threshold
% settings.background_mode
% settings.aperiodic_mode
% settings.verbose
%
% Notes:
Expand All @@ -31,9 +31,9 @@
defaults = struct(...
'peak_width_limits', [0.5, 12], ...
'max_n_peaks', Inf, ...
'min_peak_amplitude', 0.0, ...
'min_peak_height', 0.0, ...
'peak_threshold', 2.0, ...
'background_mode', 'fixed', ...
'aperiodic_mode', 'fixed', ...
'verbose', true);

% Overwrite any non-existent or nan settings with defaults
Expand Down
4 changes: 2 additions & 2 deletions fooof_mat/fooof_get_model.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
% model_fit.freqs
% model_fit.power_spectrum
% model_fit.fooofed_spectrum
% model_fit.bg_fit
% model_fit.ap_fit
%
% Notes
% This function is mostly an internal function.
Expand All @@ -24,4 +24,4 @@
model_fit.freqs = double(py.array.array('d',fm.freqs));
model_fit.power_spectrum = double(py.array.array('d', fm.power_spectrum));
model_fit.fooofed_spectrum = double(py.array.array('d', fm.fooofed_spectrum_));
model_fit.bg_fit = double(py.array.array('d', py.getattr(fm, '_bg_fit')));
model_fit.ap_fit = double(py.array.array('d', py.getattr(fm, '_ap_fit')));
8 changes: 4 additions & 4 deletions fooof_mat/fooof_group.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
% fooof_group() run the fooof model on a group of neural power spectra
% fooof_group() - Run the fooof model on a group of neural power spectra.
%
% Usage:
% fooof_results = fooof_group(freqs, psds, f_range, settings);
Expand All @@ -10,14 +10,14 @@
% settings = fooof model settings, in a struct, including:
% settings.peak_width_limts
% settings.max_n_peaks
% settings.min_peak_amplitude
% settings.min_peak_height
% settings.peak_threshold
% settings.background_mode
% settings.aperiodic_mode
% settings.verbose
%
% Outputs:
% fooof_results = fooof model ouputs, in a struct, including:
% fooof_results.background_params
% fooof_results.aperiodic_params
% fooof_results.peak_params
% fooof_results.gaussian_params
% fooof_results.error
Expand Down
10 changes: 5 additions & 5 deletions fooof_mat/fooof_plot.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
% fooof_plot() - plot a FOOOF model output
% fooof_plot() - Plot a FOOOF model.
%
% Usage:
% >> fooof_plot(fooof_results)
Expand Down Expand Up @@ -40,13 +40,13 @@ function fooof_plot(fooof_results, log_freqs)
% Plot the full model fit
model = plot(plt_freqs, fooof_results.fooofed_spectrum, 'red');

% Plot the background fit
bg_fit = plot(plt_freqs, fooof_results.bg_fit, 'b--');
% Plot the aperiodic fit
ap_fit = plot(plt_freqs, fooof_results.ap_fit, 'b--');

%% Plot settings
%% Plot Settings

% Apply general plot settings
for plt = [data, model, bg_fit]
for plt = [data, model, ap_fit]
set(plt, 'LineWidth', lw);

% Set alpha value for model - in a wonky way, because Matlab
Expand Down
Loading

0 comments on commit 018b618

Please sign in to comment.