mne.minimum_norm.source_band_induced_power has a label param, but in principle it should be possible to return results for multiple labels. Looking at the algorithm, our implementation appears to be:
-
Compute inverse operator K restricted to a label:
|
K, sel, Vh, vertno, is_free_ori, noise_norm = _prepare_source_params( |
-
Compute PSDs for each epoch in parallel:
|
out = parallel( |
|
my_compute_source_tfrs( |
Within each parallel split, take the sum across each epochs:
|
power += power_e |
|
if with_plv: |
|
plv += plv_e |
-
Take the sum across parallel-splits-of-epochs:
|
power = sum(o[0] for o in out) |
|
power /= len(epochs_data) # average power over epochs |
-
Compute PLV if requested (only used by source_induced_power, never source_band_induced_power), noise normalize (i.e., multiply each vertex by some value), baseline correct, return power and plv.
So I think we should in principle be able to add support for list-of-labels for source_band_induced_power and probably also source_induced_power when return_plv=False by doing the following:
- Document the shape of the ndarray that is currently returned (something like
(n_vertices, n_freqs, n_times))
- Add
return_plv=True to source_induced_power (the way it acts currently)
- Fix documentation of
source_induced_power to note that it returns both power and plv (when return_plv=True) and add return shapes
- Fix documentation to use
(baseline_mode)s) or whatever rather than repeat all options.
- Add some small test that actually checks our
source_band_induced_power values (e.g., assert_allclose(phase_lock[some_indices], [[some_array_values]])). This will be useful for all following changes.
- Move
noise_norm inside the parallel function.
- Add support for list-of-label by computing a new
label_op where label_op @ K averages across vertices to convert the (n_vertices, n_channels) operator K to be shape (n_labels, n_channels). This should only be allowed when with_plv/return_plv=False, which is always the case for source_band_induced_power and will now optionally be possible for source_band_power by step (3) above.
- Add test that
label=label with a .mean(axis=0) gives the same result as label=[label] when baseline_mode is None or mean, and hopefully sufficiently similar for other modes (shouldn't have a huge impact).
For point (8), it would be nice if list-of-label with one element [label] returned an identical to the label=label (single label) in all cases, but it wouldn't with these changes. The baseline correction in the list-of-label case will be done after the power is averaged within the label, whereas in the label=label case (like in our existing example) the averaging is done over already baseline-corrected vertex values within the label. I'm not certain which way is better, but 1) I doubt it makes much difference and 2) I think in principle averaging first is probably better / higher SNR, as averaging across vertices within a label first should in principle increase SNR before something like a z-transform, if used, could otherwise amplify noisy values. But either way I think if we explain the difference clearly in Notes I think we're okay.
mne.minimum_norm.source_band_induced_power has a
labelparam, but in principle it should be possible to return results for multiple labels. Looking at the algorithm, our implementation appears to be:Compute inverse operator
Krestricted to a label:mne-python/mne/minimum_norm/time_frequency.py
Line 372 in 6a701d3
Compute PSDs for each epoch in parallel:
mne-python/mne/minimum_norm/time_frequency.py
Lines 396 to 397 in 6a701d3
Within each parallel split, take the sum across each epochs:
mne-python/mne/minimum_norm/time_frequency.py
Lines 292 to 294 in 9547c13
Take the sum across parallel-splits-of-epochs:
mne-python/mne/minimum_norm/time_frequency.py
Lines 412 to 413 in 6a701d3
Compute PLV if requested (only used by
source_induced_power, neversource_band_induced_power), noise normalize (i.e., multiply each vertex by some value), baseline correct, returnpowerandplv.So I think we should in principle be able to add support for list-of-labels for
source_band_induced_powerand probably alsosource_induced_powerwhenreturn_plv=Falseby doing the following:(n_vertices, n_freqs, n_times))return_plv=Truetosource_induced_power(the way it acts currently)source_induced_powerto note that it returns bothpowerandplv(whenreturn_plv=True) and add return shapes(baseline_mode)s)or whatever rather than repeat all options.source_band_induced_powervalues (e.g.,assert_allclose(phase_lock[some_indices], [[some_array_values]])). This will be useful for all following changes.noise_norminside the parallel function.label_opwherelabel_op @ Kaverages across vertices to convert the(n_vertices, n_channels)operatorKto be shape(n_labels, n_channels). This should only be allowed whenwith_plv/return_plv=False, which is always the case forsource_band_induced_powerand will now optionally be possible forsource_band_powerby step (3) above.label=labelwith a.mean(axis=0)gives the same result aslabel=[label]whenbaseline_modeisNoneormean, and hopefully sufficiently similar for other modes (shouldn't have a huge impact).For point (8), it would be nice if list-of-label with one element
[label]returned an identical to thelabel=label(single label) in all cases, but it wouldn't with these changes. The baseline correction in the list-of-label case will be done after the power is averaged within the label, whereas in thelabel=labelcase (like in our existing example) the averaging is done over already baseline-corrected vertex values within the label. I'm not certain which way is better, but 1) I doubt it makes much difference and 2) I think in principle averaging first is probably better / higher SNR, as averaging across vertices within a label first should in principle increase SNR before something like a z-transform, if used, could otherwise amplify noisy values. But either way I think if we explain the difference clearly inNotesI think we're okay.