Skip to content

Commit 64bbf4b

Browse files
tweak loop
1 parent 212b625 commit 64bbf4b

File tree

2 files changed

+119
-89
lines changed

2 files changed

+119
-89
lines changed

R/03-R_GroupPSDs.Rmd

Lines changed: 66 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ from fooof import FOOOF,FOOOFGroup
3737
# Import useful parameterization related utilities and plot functions
3838
from fooof.bands import Bands
3939
from fooof.analysis import get_band_peak_fg
40+
from fooof.utils import trim_spectrum
41+
from fooof.data import FOOOFSettings
42+
from fooof.plts import plot_spectrum
4043
from fooof.plts.periodic import plot_peak_fits, plot_peak_params
4144
from fooof.plts.aperiodic import plot_aperiodic_params, plot_aperiodic_fits
4245
@@ -66,6 +69,12 @@ print(freqs.shape)
6669
print(spectra.shape)
6770
```
6871

72+
```{python, message = FALSE}
73+
# Get the number of participants
74+
n_subjs = spectra.shape[0]
75+
print('There are {:d} participants'.format(n_subjs))
76+
```
77+
6978
### Fit power spectra
7079
```{python, message = FALSE}
7180
# Define `peak_width_limit` setting
@@ -166,24 +175,29 @@ plt.show()
166175
There are no strict guidelines about optimal parameters that will be appropriate across data sets and recording modalities. We suggest applying a data-driven approach to tune model fitting for optimal performance, while taking into account your expectations about periodic and aperiodic activity given the data, the question of interest, and prior findings.
167176

168177
One option is to parameterize a subset of data to evaluate the appropriateness of model fit settings prior to fitting each power spectrum in the data set. Here, we test parameters on a randomly selected 10% of the data.
169-
```{python, message = FALSE}
170-
# Create new data frame object
171-
spectra_sub = pd.DataFrame(spectra)
172178

173-
# Randomly sample rows, set seed
174-
spectra_sub = spectra_sub.sample(frac = 0.10, random_state = 1)
179+
#### Subsample spectra to compare between models
180+
First, lets randomly sub-sample 10% of the power spectra that we will use to test model settings.
181+
```{python, message = FALSE}
182+
# Set random seed
183+
np.random.seed(1)
184+
```
175185

176-
# Save object as array for specparam fitting
177-
spectra_sub_array = spectra_sub.to_numpy()
186+
```{python, message = FALSE}
187+
# Define settings for subsampling a selection of power spectra
188+
subsample_frac = 0.10
189+
n_sample = int(n_subjs * subsample_frac)
178190
```
179191

180-
Here, we define settings for two models to be fit and compared. A user could also use the `trim_spectrum` to define the frequency ranges for each model; see the [specparam website](https://fooof-tools.github.io/fooof/generated/fooof.utils.trim_spectrum.html#fooof.utils.trim_spectrum) for details on how to use this utility.
181192
```{python, message = FALSE}
182-
# Create frequency vector for model #1 ("m1", 2-20 Hz) and model #2 ("m2", 3-40 Hz)
183-
# NOTE: Python uses zero indexing
184-
m1_freq = freqs[2:39]
185-
m2_freq = freqs[4:79]
193+
# Select a random selection of spectra to explore
194+
select = np.random.choice(n_subjs, int(n_subjs * subsample_frac), replace = False)
195+
spectra_subsample = spectra[select, :]
196+
```
186197

198+
#### Define settings for each model
199+
Here, we define settings for two models to be fit and compared.
200+
```{python, message = FALSE}
187201
# Define `peak_width_limit` for each model
188202
m1_peak_width = [2, 5]
189203
m2_peak_width = [1, 8]
@@ -195,63 +209,65 @@ m2_n_peaks = 6
195209
# Define `min_peak_height` for each model
196210
m1_peak_height = 0.05
197211
m2_peak_height = 0.10
212+
```
198213

214+
#### Set frequency ranges for each model
215+
To sub-select frequency ranges, we will use the `trim_spectrum` function to extract frequency ranges of interest for each model.
216+
```{python, message = FALSE}
199217
# Define frequency range for each model
200218
m1_PSD_range = [2, 20]
201219
m2_PSD_range = [3, 40]
220+
221+
# Sub-select frequency ranges
222+
m1_freq, m1_spectra = trim_spectrum(freqs, spectra_subsample, m1_PSD_range)
223+
m2_freq, m2_spectra = trim_spectrum(freqs, spectra_subsample, m2_PSD_range)
202224
```
203225

226+
#### Fit models, with different settings
204227
```{python, message = FALSE}
205-
# Loop through each row to fit model for each participant in the subset
206-
for index, row in spectra_sub.iterrows():
207-
208-
# Tidy the single PSD under consideration
209-
spectrum = spectra_sub.loc[[index]].transpose()[2:39]
210-
spectrum = spectrum.to_numpy()
211-
spectrum = np.ravel(spectrum)
212-
213-
# Initialize a model object for spectral parameterization, with some settings
214-
fm = FOOOF(peak_width_limits = m1_peak_width, max_n_peaks = m1_n_peaks, min_peak_height = m1_peak_height)
215-
216-
# Fit individual PSD over 2 to 20 Hz range
217-
fm.fit(m1_freq, spectrum, m1_PSD_range)
218-
219-
# Save out individual results for further consideration
220-
fm.save_report(file_name = 'EOP_' + str(index) + '_fm1_settings', file_path = '../Output')
228+
# Fit a model object with model 1 settings
229+
fg1 = FOOOFGroup(peak_width_limits = m1_peak_width, max_n_peaks = m1_n_peaks, min_peak_height = m1_peak_height)
230+
fg1.fit(m1_freq, m1_spectra)
231+
232+
# Create individual reports for model 1 settings
233+
for ind in range(len(fg1)):
234+
temp_model = fg1.get_fooof(ind, regenerate = True)
235+
temp_model.save_report(file_name = 'EOP_' + str(ind) + '_fm1_settings', file_path = '../Output')
221236
)
222237
```
223238

224239
```{python, message = FALSE}
225-
# Loop through each row to fit model for each participant in the subset
226-
for index, row in spectra_sub.iterrows():
227-
228-
# Tidy the single PSD under consideration
229-
spectrum = spectra_sub.loc[[index]].transpose()[4:79]
230-
spectrum = spectrum.to_numpy()
231-
spectrum = np.ravel(spectrum)
232-
233-
# Initialize a model object for spectral parameterization, with some settings
234-
fm = FOOOF(peak_width_limits = m2_peak_width, max_n_peaks = m2_n_peaks, min_peak_height = m2_peak_height)
235-
236-
# Fit individual PSD over 3 to 40 Hz range
237-
fm.fit(m2_freq, spectrum, m2_PSD_range)
238-
239-
# Save out individual results for further consideration
240-
fm.save_report(file_name = 'EOP_' + str(index) + '_fm2_settings', file_path = '../Output')
240+
# Fit a model object with model 2 settings
241+
fg2 = FOOOFGroup(peak_width_limits = m2_peak_width, max_n_peaks = m2_n_peaks, min_peak_height = m2_peak_height)
242+
fg2.fit(m2_freq, m2_spectra)
243+
244+
# Create individual reports for the model 2 settings
245+
for ind in range(len(fg2)):
246+
temp_model = fg2.get_fooof(ind, regenerate = True)
247+
temp_model.save_report(file_name = 'EOP_' + str(ind) + '_fm2_settings', file_path = '../Output')
241248
)
242249
```
243250

244-
Alternatively, you can fit the subset of data with `FOOOFGroup`, with different settings for each model object.
251+
#### Other ways to manage settings
252+
Another way to manage model settings is with the `FOOOFSettings` object. Here we will redefine group model objects (`FOOOFGroup`), again using different settings for each one.
245253
```{python, message = FALSE}
246-
# Initialize model objects for spectral parameterization, with some settings
247-
fg1 = FOOOFGroup(peak_width_limits = m1_peak_width, max_n_peaks = m1_n_peaks, min_peak_height = m1_peak_height)
248-
fg2 = FOOOFGroup(peak_width_limits = m2_peak_width, max_n_peaks = m2_n_peaks, min_peak_height = m2_peak_height)
254+
# Define settings for model 1
255+
settings1 = FOOOFSettings(peak_width_limits= m1_peak_width, max_n_peaks = m1_n_peaks,
256+
min_peak_height = m1_peak_height, peak_threshold = 2.,
257+
aperiodic_mode = 'fixed')
258+
259+
# Define settings for model 2
260+
settings2 = FOOOFSettings(peak_width_limits = m2_peak_width, max_n_peaks = m2_n_peaks,
261+
min_peak_height = m2_peak_height, peak_threshold = 2.,
262+
aperiodic_mode = 'fixed')
249263
```
250264

265+
#### Fit models with group model object
266+
Note that when fitting power spectra, you can specify a fit range to fit the model to, so you don't have to explicitly trim the spectra. Here we will refit the example data, specifying the fit range, and then save the group reports.
251267
```{python, message = FALSE}
252268
# Fit group PSD over the 2-20 Hz and 3-40 Hz ranges, respectively
253-
fg1.fit(freqs, spectra_sub_array, m1_PSD_range)
254-
fg2.fit(freqs, spectra_sub_array, m2_PSD_range)
269+
fg1.fit(freqs, spectra_subsample, m1_PSD_range)
270+
fg2.fit(freqs, spectra_subsample, m2_PSD_range)
255271
```
256272

257273
```{python, message = FALSE}

R/04-R_ExampleAnalysis.Rmd

Lines changed: 53 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,14 @@ from fooof import FOOOF, FOOOFGroup
3333
# Import useful parameterization related utilities and plot functions
3434
from fooof.bands import Bands
3535
from fooof.analysis import get_band_peak_fg
36+
from fooof.utils import trim_spectrum
37+
from fooof.data import FOOOFSettings
38+
from fooof.plts import plot_spectrum
3639
from fooof.plts.periodic import plot_peak_fits, plot_peak_params
3740
from fooof.plts.aperiodic import plot_aperiodic_params, plot_aperiodic_fits
3841
39-
# Import utility functions that manipulate FOOOF objects
40-
from fooof.objs.utils import average_fg
42+
# # Import utility functions that manipulate FOOOF objects
43+
# from fooof.objs.utils import average_fg
4144
4245
# Import functions to examine frequency-by-frequency error of model fits
4346
from fooof.analysis.error import compute_pointwise_error_fg
@@ -54,20 +57,28 @@ print(freqs.shape)
5457
print(spectra.shape)
5558
```
5659

57-
### Test model fit settings on subset (10%) of data
5860
```{python, message = FALSE}
59-
spectra_sub = pd.DataFrame(spectra)
60-
spectra_sub = spectra_sub.sample(frac = 0.10, random_state = 2)
61-
62-
spectra_sub_array = spectra_sub.to_numpy()
61+
# Get the number of participants
62+
n_subjs = spectra.shape[0]
63+
print('There are {:d} participants'.format(n_subjs))
6364
```
6465

66+
#### Subsample (10%) spectra to compare between models
6567
```{python, message = FALSE}
66-
# Create frequency vector for model #1 ("m1", 2-20 Hz) and model #2 ("m2", 3-40 Hz)
67-
# NOTE: Python uses zero indexing
68-
m1_freq = freqs[2:39]
69-
m2_freq = freqs[4:79]
68+
# Set random seed
69+
np.random.seed(2)
70+
71+
# Define settings for subsampling a selection of power spectra
72+
subsample_frac = 0.10
73+
n_sample = int(n_subjs * subsample_frac)
74+
75+
# Select a random selection of spectra to explore
76+
select = np.random.choice(n_subjs, int(n_subjs * subsample_frac), replace = False)
77+
spectra_subsample = spectra[select, :]
78+
```
7079

80+
#### Define settings for each model
81+
```{python, message = FALSE}
7182
# Define `peak_width_limit` for each model
7283
m1_peak_width = [2, 5]
7384
m2_peak_width = [1, 8]
@@ -79,60 +90,63 @@ m2_n_peaks = 6
7990
# Define `min_peak_height` for each model
8091
m1_peak_height = 0.05
8192
m2_peak_height = 0.10
93+
```
8294

95+
#### Set frequency ranges for each model
96+
To sub-select frequency ranges, we will use the `trim_spectrum` function to extract frequency ranges of interest for each model.
97+
```{python, message = FALSE}
8398
# Define frequency range for each model
8499
m1_PSD_range = [2, 20]
85100
m2_PSD_range = [3, 40]
86-
```
87101
88-
```{python, message = FALSE}
89-
for index, row in spectra_sub.iterrows():
90-
spectrum = spectra_sub.loc[[index]].transpose()[2:39]
91-
spectrum = spectrum.to_numpy()
92-
spectrum = np.ravel(spectrum)
93-
94-
fm = FOOOF(peak_width_limits = m1_peak_width, max_n_peaks = m1_n_peaks, min_peak_height = m1_peak_height)
95-
fm.fit(m1_freq, spectrum, m1_PSD_range)
96-
97-
fm.save_report(file_name = 'ECL_' + str(index) + '_fm1_settings', file_path = '../Output')
98-
)
102+
# Sub-select frequency ranges
103+
m1_freq, m1_spectra = trim_spectrum(freqs, spectra_subsample, m1_PSD_range)
104+
m2_freq, m2_spectra = trim_spectrum(freqs, spectra_subsample, m2_PSD_range)
99105
```
100106

107+
#### Fit models, with different settings
101108
```{python, message = FALSE}
102-
for index, row in spectra_sub.iterrows():
103-
spectrum = spectra_sub.loc[[index]].transpose()[4:79]
104-
spectrum = spectrum.to_numpy()
105-
spectrum = np.ravel(spectrum)
106-
107-
fm = FOOOF(peak_width_limits = m2_peak_width, max_n_peaks = m2_n_peaks, min_peak_height = m2_peak_height)
108-
fm.fit(m2_freq, spectrum, m2_PSD_range)
109-
110-
fm.save_report(file_name = 'ECL_' + str(index) + '_fm2_settings', file_path = '../Output')
109+
# Fit a model object with model 1 settings
110+
fg1 = FOOOFGroup(peak_width_limits = m1_peak_width, max_n_peaks = m1_n_peaks, min_peak_height = m1_peak_height)
111+
fg1.fit(m1_freq, m1_spectra)
112+
113+
# Create individual reports for model 1 settings
114+
for ind in range(len(fg1)):
115+
temp_model = fg1.get_fooof(ind, regenerate = True)
116+
temp_model.save_report(file_name = 'ECL_' + str(ind) + '_fm1_settings', file_path = '../Output')
111117
)
112118
```
113119

114120
```{python, message = FALSE}
115-
# Initialize model objects for spectral parameterization, with some settings
116-
fg1 = FOOOFGroup(peak_width_limits = m1_peak_width, max_n_peaks = m1_n_peaks, min_peak_height = m1_peak_height)
121+
# Fit a model object with model 2 settings
117122
fg2 = FOOOFGroup(peak_width_limits = m2_peak_width, max_n_peaks = m2_n_peaks, min_peak_height = m2_peak_height)
123+
fg2.fit(m2_freq, m2_spectra)
124+
125+
# Create individual reports for the model 2 settings
126+
for ind in range(len(fg2)):
127+
temp_model = fg2.get_fooof(ind, regenerate = True)
128+
temp_model.save_report(file_name = 'ECL_' + str(ind) + '_fm2_settings', file_path = '../Output')
129+
)
118130
```
119131

132+
#### Fit models with group model object
133+
Note that when fitting power spectra, you can specify a fit range to fit the model to, so you don't have to explicitly trim the spectra. Here we will refit the example data, specifying the fit range, and then save the group reports.
120134
```{python, message = FALSE}
121135
# Fit group PSD over the 2-20 Hz and 3-40 Hz ranges, respectively
122-
fg1.fit(freqs, spectra_sub_array, m1_PSD_range)
123-
fg2.fit(freqs, spectra_sub_array, m2_PSD_range)
136+
fg1.fit(freqs, spectra_subsample, m1_PSD_range)
137+
fg2.fit(freqs, spectra_subsample, m2_PSD_range)
124138
```
125139

126140
```{python, message = FALSE}
127-
# Print and save subset results and plots of fit parameters, for further examination
141+
# Save subset results and plots of fit parameters, for further examination
128142
fg1.save_report(file_name = 'ECL_' + 'fg1_settings', file_path = '../Output')
129143
fg2.save_report(file_name = 'ECL_' + 'fg2_settings', file_path = '../Output')
130144
```
131145

132-
### Inspect group power spectra
146+
### Fit power spectra with selected settings
133147
Based on model fit results from the subset of participants, published findings, and our expectations of data in this sample, we will move forward with fitting the group power spectra over the 3-40 Hz frequency range with settings from the *fg2* model object.
134148
```{python, message = FALSE}
135-
# Initialize a model object for spectral parameterization, with some settings
149+
# Initialize a model object for spectral parameterization
136150
fg = FOOOFGroup(peak_width_limits = m2_peak_width, max_n_peaks = m2_n_peaks, min_peak_height = m2_peak_height)
137151
```
138152

0 commit comments

Comments
 (0)