Using FOOOF from Syncopy

Syncopy supports parameterization of neural power spectra using the Fitting oscillations & one over f (FOOOF ) method described in the following publication (DOI link):

Donoghue T, Haller M, Peterson EJ, Varma P, Sebastian P, Gao R, Noto T, Lara AH, Wallis JD, Knight RT, Shestyuk A, & Voytek B (2020). Parameterizing neural power spectra into periodic and aperiodic components. Nature Neuroscience, 23, 1655-1665. DOI: 10.1038/s41593-020-00744-x

The FOOOF method requires as input an Syncopy AnalogData object, so time series data like for example a LFP signal. Applying FOOOF can then be seen as a post-processing of a multi-tapered Fourier Analysis.

Generating Example Data

Let us first prepare suitable data. FOOOF will typically be applied to trial-averaged data, as the method is quite sensitive to noise, so we generate an example data set consisting of 500 trials and a single channel here (see the Synthetic data tutorial for details on this):

 1import numpy as np
 2from syncopy import freqanalysis, get_defaults
 3from syncopy.tests.synth_data import AR2_network, phase_diffusion
 4
 5def get_signals(nTrials=500, nChannels = 1):
 6    nSamples = 1000
 7    samplerate = 1000
 8    ar1_part = AR2_network(AdjMat=np.zeros(1), nSamples=nSamples, alphas=[0.9, 0], nTrials=nTrials)
 9    pd1 = phase_diffusion(freq=30., eps=.1, fs=samplerate, nChannels=nChannels, nSamples=nSamples, nTrials=nTrials)
10    pd2 = phase_diffusion(freq=50., eps=.1, fs=samplerate, nChannels=nChannels, nSamples=nSamples, nTrials=nTrials)
11    signal = ar1_part + .8 * pd1 + 0.6 * pd2
12    return signal
13
14signals = get_signals()

The return value signals is of type AnalogData. Let’s have a look at the signal in the time domain first:

signals.singlepanelplot(trials = 0)
../_images/fooof_signal_time.png

Since FOOOF works on the power spectrum, we can perform an mtmfft and look at the results to get a better idea of how our data looks in the (un-fooofed) frequency domain. The spec data structure we obtain is of type SpectralData, and can also be plotted:

 1cfg = get_defaults(freqanalysis)
 2cfg.method = "mtmfft"
 3cfg.taper = "hann"
 4cfg.select = {"channel": 0}
 5cfg.keeptrials = False
 6cfg.output = "pow"
 7cfg.foilim = [10, 100]
 8
 9spec = freqanalysis(cfg, signals)
10spec.singlepanelplot()
../_images/fooof_signal_spectrum.png

By construction, we see two spectral peaks around 30Hz and 50Hz and a strong \(1/f\) like background.

Running FOOOF

Now that we have seen the more or less raw power spectrum, let us start FOOOF. The FOOOF method is accessible from the freqanalysis function by setting the output parameter to ‘fooof’:

1cfg.output = 'fooof'
2spec_fooof = freqanalysis(cfg, signals)
3spec_fooof.singlepanelplot()
../_images/fooof_out_first_try.png

FOOOF output types

In the example above, the spectrum returned is the full FOOOFed spectrum. This is typically what you want, but to better understand your results, you may be interested in the other options. The following ouput types are available:

  • fooof: the full fooofed spectrum

  • fooo_aperiodic: the aperiodic part of the spectrum

  • fooof_peaks: the detected peaks, with Gaussian fit to them

Here we request only the aperiodic (\(\sim 1/f\)) part and plot it:

1cfg.output = 'fooof_aperiodic'
2spec_fooof_aperiodic = freqanalysis(cfg, signals)
3spec_fooof_aperiodic.singlepanelplot()
../_images/fooof_out_aperiodic.png

You may want to use a combination of the different return types to inspect your results.

Knowing what your data and the FOOOF results like is important, because typically you will have to fine-tune the FOOOF method to get the results you are interested in.

With the data above, we were interested only in the 2 large peaks around 30 and 50 Hz, but 2 more minor peaks were detected by FOOOF, around 37 and 42 Hz. We will learn how to exclude these peaks in the next section.

Fine-tuning FOOOF

The FOOOF method can be adjusted using the fooof_opt parameter to freqanalyis. The full list of available options and defaults are explained in detail in the official FOOOF documentation.

From the results above, we see that some peaks were detected that we think (and actually know by construction) are noise. Increasing the minimal peak width is one method to exclude them:

1cfg.output = 'fooof'
2cfg.fooof_opt = {'peak_width_limits': (6.0, 12.0), 'min_peak_height': 0.2}
3spec_fooof_tuned = freqanalysis(cfg, signals)
4spec_fooof_tuned.singlepanelplot()

Once more, we look at the FOOOFed spectrum:

../_images/fooof_out_tuned.png

Note that the two tiny peaks have been removed.

This concludes the tutorial on using FOOOF from Syncopy. Please do not forget to cite Donoghue et al. 2020 when using FOOOF.