<jupyter_start><jupyter_text>FOOOFGroup This tutorial covers running FOOOF across multiple power spectra, using FOOOFGroup, an object derived from the base FOOOF object.For a more specific exploration of the base FOOOF object, and the underlying algorithm, go [here](02-FOOOF.ipynb).<jupyter_code># Set matplotlib for plotting in the notebook
%matplotlib inline

# FOOOF imports: get FOOOF & FOOOFGroup objects
from fooof import FOOOFGroup

# Import some utilities for synthesizing some test data
from fooof.synth import gen_group_power_spectra<jupyter_output><empty_output><jupyter_text>Synthesizing Power SpectraFOOOF includes some basic support for creating synthetic power-spectra, that mimic real data. Here we will use that functionality to create a matrix of power spectra to test with. By default, the 'gen_group_power_spectra' takes lists of options for background and Gaussian peak parameters, and generates a specified number of power spectra, each one choosing a random combination of parameters.If you would like to generate single power spectra, you can use 'gen_power_spectrum', also in fooof.synth.<jupyter_code># Set some options for background parameters
#  Generated spectra will have an offset of either [20, 50, 35], and corresponding slope of [2., 2.5, 1.5]
bgp_opts = [[20, 2], [50, 2.5], [35, 1.5]]

# Set some options for peak parameters
#  Generate power spectra will have either no peaks, a 10 Hz peak, or a 10 Hz & 20 Hz peak
gauss_opts = [[], [10, 0.5, 2], [10, 0.5, 2, 20, 0.3, 4]]
# Generate the group of synthetic spectra
freqs, spectra = gen_group_power_spectra(10, [3, 40], bgp_opts, gauss_opts)<jupyter_output><empty_output><jupyter_text>FOOOFGroupThe FOOOFGroup object is very similar to the FOOOF object (programmatically, it inherits from the FOOOF object), and can be used in the same way. The main difference is that instead of running across a single power spectrum, it operates across matrices of multiple power spectra. Note that by 'group' we mean merely to refer to a group of power-spectra, not necessarily to a group in terms of multiple subjects or similar. Most likely, a FOOOFGroup will be run across a collection of spectra from across channels, and/or across trials, within or across subjects. The main different with the FOOOFGroup object, is that it also contains a 'power_spectra' attribute, which stores the matrix of power-spectra to be fit, and collects fit results into a 'group_results' attribute. Otherwise, FOOOFGroup supports all the same functionality, accessed in the same way as the FOOOF object.Internally, it runs the exact same fitting procedure, per spectrum, as the FOOOF object.<jupyter_code># Initialize a FOOOFGroup object. It accepts all the same settings as FOOOF()
fg = FOOOFGroup(peak_width_limits=[1, 8], min_peak_amplitude=0.05, max_n_peaks=6)
# Fit a group of power spectra with the .fit() method
#  The key difference (compared to FOOOF) is that it takes a 2D array of spectra
#     This matrix should have the shape of [n_spectra, n_freqs]
fg.fit(freqs, spectra)
# Print out results
fg.print_results()
# Plot a summary of the results across the group
#  Note: given the simulations, we expect slopes at {1.5, 2.0. 2.5} and peaks around {10, 20}
fg.plot()<jupyter_output><empty_output><jupyter_text>Just as with the FOOOF object, you can call the convenience method 'report' to run the fitting, and print results & plots, printing out the same as above.<jupyter_code># You can also save out PDFs reports for FOOOFGroup fits, same as with FOOOF
fg.save_report()
# Check what the generated report looks like
from IPython.display import IFrame
IFrame("FOOOFGroup_Report.pdf", width=950, height=1200)<jupyter_output><empty_output><jupyter_text>FOOOFGroup DataFOOOFGroup collects fits across power spectra into a list of FOOOFResults objects.<jupyter_code># As it runs, FOOOFGroup collects each fit results in 'group_results'
#  'group_results' is a list of FOOOFResult objects
fg.group_results[0:2]<jupyter_output><empty_output><jupyter_text>get_all_dataTo collect data across all model fits, and to select specific data results from this data you can use the 'get_all_data' method. This method lets you extract specific results by specifying a field, as a string, and (optionally) a specific column of that data, also as a string (or, optionally, as an integer index).<jupyter_code># Extract aperiodic data
bgps = fg.get_all_data('background_params')
sls = fg.get_all_data('background_params', 'slope')

# Extract peak data
peaks = fg.get_all_data('peak_params')
cfs = fg.get_all_data('peak_params', 'CF')

# Extract metadata about the model fit
errors = fg.get_all_data('error')
r2s = fg.get_all_data('r_squared')
# The full list of data you can specify is available in the documentation of 'get_all_data'
fg.get_all_data?
# More information about the data you can extract is also documented in the FOOOFResults object

# Grab a particular FOOOFResults item
#  Note that as a shortcut, you can index the FOOOFGroup object directly to access 'group_results'
f_res = fg[0]

# Check the documentation for the FOOOFResults - with full descriptions of the resulting data.
print(f_res.__doc__)
# Check out the extracted slope values
#  Note that this extraction will return an array of length equal to the number of model fits
#    The model fit from which each data element originated is the index of this vector
print(sls)
# Check the fit center-frequencies and slope values
#  Note that when you extract peak data, an additional column is appended, specifying which model fit it came from
print(cfs)<jupyter_output>[[10.11172074  0.        ]
 [19.97456174  0.        ]
 [10.02571163  1.        ]
 [10.09745985  2.        ]
 [20.02220545  2.        ]
 [10.10307174  3.        ]
 [19.99867967  3.        ]
 [10.10697957  4.        ]
 [20.01296691  4.        ]
 [10.01734141  5.        ]
 [10.09615195  7.        ]
 [20.00172124  7.        ]
 [10.04578021  8.        ]
 [10.09598139  9.        ]
 [19.94589533  9.        ]]<jupyter_text>Saving & Loading with FOOOFGroupFOOOFGroup also support saving and loading, with same options as saving from FOOOF. The only difference in saving FOOOFGroup, is that it saves out a 'jsonlines' file, in which each line is a JSON object, saving the specified data and results for a single power spectrum.Note that saving settings together with results will save out duplicated settings to each line in the output file, corresponding to each individual spectrum in the group, and so is somewhat inefficient. It is more parsimonious to save out a single settings file, and a separate file that includes the results.<jupyter_code># Save out FOOOFGroup settings & results (separately)
fg.save('FG_settings', save_settings=True)
fg.save('FG_results', save_results=True)
# You can then reload this group data
nfg = FOOOFGroup()
nfg.load('FG_results')
# Print results to check that the loaded group 
nfg.print_results()<jupyter_output>==================================================================================================
                                                                                                  
                                       FOOOF - GROUP RESULTS                                      
                                                                                                  
                             Number of power spectra in the Group: 10                             
                                                                                                  
                        The model was run on the frequency range 3 - 40 Hz                        
                                 Frequency Resolution is 0.50 Hz                                  
                                                                                                  
                              Power spectra were fit without a knee.                              
          [...]<jupyter_text>Parallel SupportFOOOFGroup also has support for running in parallel.The fit method includes an optional parameter 'n_jobs', which if set at 1, will run FOOOF linearly, but otherwise will launch n_jobs number of jobs, in parallel. Setting n_jobs to -1 will launch in parallel across all available cores. Note, however, that running FOOOF in parallel does not gaurantee a quicker runtime overall. The computation time per FOOOF-fit scales with the frequency range fit over, and the 'complexity' of the power spectra, in terms of number of peaks. For relatively small numbers of power spectra (less than ~100), across relatively small frequency ranges (say ~3-40Hz), running in parallel may offer no appreciable speed up.<jupyter_code># Run FOOOF fit across a group of power spectra in parallel, using all cores
fg.fit(freqs, spectra, n_jobs=-1)<jupyter_output><empty_output><jupyter_text>Plotting Individual FitsWhen running FOOOF across a group of power spectra, results are stored as the FOOOFResults, which store (only) the results of the model fit, not the full model fits themselves. To examine individual model fits, FOOOFGroup can regenerate FOOOF objects for individual power spectra, with the full model available for visualization.<jupyter_code># Extract a particular spectrum, specified by index to a FOOOF object
#  Here we also specify to regenerate the the full model fit, from the results
fm = fg.get_fooof(ind=2, regenerate=True)
# Print results and plot extracted FOOOF model fit
fm.print_results()
fm.plot()<jupyter_output>==================================================================================================
                                                                                                  
                                   FOOOF - POWER SPECTRUM MODEL                                   
                                                                                                  
                        The model was run on the frequency range 3 - 40 Hz                        
                                 Frequency Resolution is 0.50 Hz                                  
                                                                                                  
                             Background Parameters (offset, slope):                               
                                         50.0561, 2.5276                                          
                                                                                                  
          [...]
{'fraction_non_alphanumeric': 0.07371428571428572, 'fraction_numerical': 0.026476190476190476, 'mean_word_length': 2.5073480293921175, 'pattern_counts': {'":': 0, '<': 34, '<?xml version=': 0, '>': 34, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 42, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '9', 'dir': 'jupyter-structured-clean-dedup', 'id': '46976', 'license': 'Apache-2.0', 'n_tokens_mistral': 2831, 'n_tokens_neox': 2569, 'n_words': 1190, 'path': 'tutorial/04-FOOOFGroup.ipynb', 'repo_name': 'GrantRVD/fooof', 'size': '10500'}