MRG, BUG: Fix bugs with envcorr#8658
Conversation
| label_data_orth_std = data_mag_std[li] | ||
| else: | ||
| label_data_orth = (label_data * data_conj_scaled).imag | ||
| np.abs(label_data_orth, out=label_data_orth) |
There was a problem hiding this comment.
First change: when orthogonalizing, use abs of the orthogonalized data rather than just (data * data_conj_scaled).imag). Not 100% sure this is right but conceptually the thing we're correlating with is abs(data) so it seems to make sense to take the abs here, too...
I think the original operated on np.log10(label_data_orth ** 2) so an abs was implicitly taken by the squaring operation.
There was a problem hiding this comment.
I am not sure about the log10, but they do square and take the log-transform:
Citation from their supplementary information:
"The signal Y(t,f) is orthogonalized in the complex plane with respect to X(t,f). This results in a positive number |Y_orth_X(t,f)| which is then squared and log-transformed, and correlated with [|X(t,f)|." Source
There was a problem hiding this comment.
log10 is the same as log up to a scale factor, and that scale factor goes away in the correlation so it doesn't really matter. But np.log is faster than np.log10 so why not!
There was a problem hiding this comment.
... but that text does make it clear that it should result in a positive number even before the square and log transform, so I think this is correct now (and will continue to be on the next push where I change np.log10 to np.log)
There was a problem hiding this comment.
I think abs was missing in the original code, because of the squaring in the next step.
| # correlation is dot product divided by variances | ||
| corr[li] = np.dot(label_data_orth, data_mag_nomean[li]) | ||
| corr[li] /= data_mag_std[li] | ||
| corr[li] = np.sum(label_data_orth * data_mag_nomean, axis=1) |
There was a problem hiding this comment.
|
@SherazKhan @dengemann it's your code originally. Can you take a minute to have a lookt? thanks |
|
Thank you everyone for looking into this. This work is currently not loaded into my working memory. What I can say though is that I always had a feeling since our working session at Biomag2018 that the MNE implementation does something different than the prototype. |
mne/connectivity/envelope.py
Outdated
| if absolute: | ||
| corr = np.abs(corr) | ||
| corr = (corr.T + corr) / 2. | ||
| corr.flat[::corr.shape[0] + 1] = 0. |
There was a problem hiding this comment.
Perhaps consider adding a comment for Python newcomers that this updates the diagonal efficiently.
|
@SherazKhan can you also take a look? |
| # This is the version of the code by Sheraz and Denis. | ||
| # For this version (epochs, labels, time) must be -> (labels, time, epochs) | ||
| data = np.transpose(data, (1, 2, 0)) | ||
| corr_mats = np.empty((data.shape[0], data.shape[0], data.shape[2])) | ||
| for index, label_data in enumerate(data): | ||
| label_data_orth = np.imag(label_data * (data.conj() / np.abs(data))) | ||
| label_data_orig = np.abs(label_data) | ||
| label_data_cont = np.transpose( | ||
| np.dstack((label_data_orig, np.transpose(label_data_orth, | ||
| (1, 2, 0)))), (1, 2, 0)) | ||
| corr_mats[index] = np.array([np.corrcoef(dat) | ||
| for dat in label_data_cont])[:, 0, 1:].T | ||
| corr_mats = np.transpose(corr_mats, (2, 0, 1)) | ||
| corr = np.mean(np.array([(np.abs(corr_mat) + np.abs(corr_mat).T) / 2. | ||
| for corr_mat in corr_mats]), axis=0) |
There was a problem hiding this comment.
I always had a feeling since our working session at Biomag2018 that the MNE implementation does something different than the prototype.
@dengemann IIRC the code here was the prototype that I used to code a more efficient version in MNE, so it should have replicated it properly. Unfortunately I had to change this prototype code to do something different in order to get it to follow FieldTrip and the paper definitions...
There was a problem hiding this comment.
No problem, If we can come up with a more flexible set of options this is good for research on this method.
The comparisons in the previous discussion suggest that difference may be rather subtle.
There was a problem hiding this comment.
I second, differences will be subtle and additional options will be worth comparing.
| y_orth_x = (y * x_conj_scaled).imag | ||
| y_orth_x_mag = np.abs(y_orth_x) | ||
| # Estimate correlation | ||
| corr[ii, jj] += np.abs(np.corrcoef(x_mag, y_orth_x_mag)[0, 1]) |
There was a problem hiding this comment.
@SherazKhan the actual critical change in the code below is from data_mag_nomean[li] which only used the lith label's data to correlate with all N labels label_data_orth series, and this code now uses data_mag_nomean, i.e., all N labels data to correlate with all N labels label_data_orth series.
It's probably easier to see if you look at the "simplified" version here. The old/master version of the code does the correlation with the jj original (magnitude) data that you could call y_mag:
corr[ii, jj] += np.abs(np.corrcoef(np.abs(epoch_data[jj]), y_orth_x)[0, 1])
Instead of this correlation, with (magnitude) of the iith data that is called x_mag here, which is now implemented in this PR, equivalently written as:
corr[ii, jj] += np.abs(np.corrcoef(np.abs(epoch_data[ii]), y_orth_x)[0, 1])
There was a problem hiding this comment.
@larsoner indeed, this is more closer to the original Hipp version.
I agree the change looks reasonable. |
|
@larsoner yeah it's not a blocker, it was the same concern on master. |
|
@dengemann and I was able to reproduce something similar using cam-can data |
|
@SherazKhan I think we should repeat those benchmarks soon :) |
|
@SherazKhan to my eye the clearest result is |
@larsoner I agree. |
|
Thanks for looking, discussing, and testing @dengemann @SherazKhan ! |
* upstream/master: (42 commits) MRG, ENH: Add DICS bias tests (mne-tools#8610) MRG, BUG, ENH: Add window option (mne-tools#8662) BUG: Fix alpha for volumes (mne-tools#8663) MRG, BUG: Fix bugs with envcorr (mne-tools#8658) MRG, ENH: Progressbar for csd_morlet (mne-tools#8608) Render is necessary now (mne-tools#8657) VIZ: Fix head size (mne-tools#8651) MRG, MAINT: bump sphinxcontrib-bitex version (mne-tools#8653) MRG, MAINT: Improve server env (mne-tools#8656) BUG: Mayavi center (mne-tools#8644) VIZ, ENH: allow show/hide annotations by label (mne-tools#8624) Add regression test for EEGLAB data with a chanlocs struct (mne-tools#8647) FIX: scalar_bar (mne-tools#8643) MRG: Small fix to tutorial; rename plot_events ordinate label to "Event id"; improve some SSP docstrings (mne-tools#8612) MRG, ENH: make plot alignment use defaults for colors (mne-tools#8553) BUG: Fix passing of channel type (mne-tools#8638) FIX: fixed loop over norm PSF/CTF options (mne-tools#8636) MRG, BUG: Pass kwargs (mne-tools#8630) DOC: Clearer error message (mne-tools#8631) BUG: Fix number of labels (mne-tools#8629) ...
* upstream/master: (38 commits) MRG, ENH: Add DICS bias tests (mne-tools#8610) MRG, BUG, ENH: Add window option (mne-tools#8662) BUG: Fix alpha for volumes (mne-tools#8663) MRG, BUG: Fix bugs with envcorr (mne-tools#8658) MRG, ENH: Progressbar for csd_morlet (mne-tools#8608) Render is necessary now (mne-tools#8657) VIZ: Fix head size (mne-tools#8651) MRG, MAINT: bump sphinxcontrib-bitex version (mne-tools#8653) MRG, MAINT: Improve server env (mne-tools#8656) BUG: Mayavi center (mne-tools#8644) VIZ, ENH: allow show/hide annotations by label (mne-tools#8624) Add regression test for EEGLAB data with a chanlocs struct (mne-tools#8647) FIX: scalar_bar (mne-tools#8643) MRG: Small fix to tutorial; rename plot_events ordinate label to "Event id"; improve some SSP docstrings (mne-tools#8612) MRG, ENH: make plot alignment use defaults for colors (mne-tools#8553) BUG: Fix passing of channel type (mne-tools#8638) FIX: fixed loop over norm PSF/CTF options (mne-tools#8636) MRG, BUG: Pass kwargs (mne-tools#8630) DOC: Clearer error message (mne-tools#8631) BUG: Fix number of labels (mne-tools#8629) ...





Closes #8649.
@SherazKhan @dengemann can you look? This suggests the code I adapted originally has some bugs. It doesn't drastically change results or anything, in general it looks a bit cleaner now.