Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Phase-slope index using spectral_connectivity_time instead of spectral_connectivity_epochs #210

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

seqasim
Copy link

@seqasim seqasim commented Jun 28, 2024

Thanks for contributing. If this is your first time,
make sure to read contributing.md

PR Description

Currently, the function for computing phase-slope index is hard-coded to be computed over epochs to produce time-resolved PSI, and there's no option for computing the phase-slope index over time to produce PSI per epoch. This would be useful if you think the average directionality between two signals over the entire timecourse is informative. You could compute this by binning your trials into categories (i.e. good trials v. bad trials) and then computing PSI, but there's no way to compute PSI per epoch and relate it to continuous trial-level predictors.

Here, I add a new function phase_slope_index_time to mne_connectivity.effective that compute PSI over time. I should note that I wasn't able to test this function directly as I'm having trouble getting the forked repo installed, in editable mode, with all necessary dependencies, but have written a test function in tests.test_effective to facilitate this.

Merge checklist

Maintainer, please confirm the following before merging:

  • All comments resolved
  • This is not your own PR
  • All CIs are happy
  • PR title starts with [MRG]
  • whats_new.rst is updated
  • PR description includes phrase "closes <#issue-number>"

@tsbinns
Copy link
Collaborator

tsbinns commented Aug 1, 2024

Hi @seqasim, thanks for the PR and sorry for not getting back to you sooner on this. I have a few comments/suggestions which I'll post below, but it's a really good start!

If this is something you're still interested in working on, your contributions are of course very welcome, however I realise it's been a while. If you're not able to invest time into this anymore and you don't want your hard work to go to waste, I can pick things up and we can make sure the changes are added.

Just let us know. Cheers!

@tsbinns
Copy link
Collaborator

tsbinns commented Aug 1, 2024

Just need to make sure that the new cohy method is described in the docstring for spectral_connectivity_time():

method : str | list of str
Connectivity measure(s) to compute. These can be ``['coh', 'cacoh',
'mic', 'mim', 'plv', 'ciplv', 'pli', 'wpli', 'gc', 'gc_tr']``. These
are:


And also an equation entry like for coh:

**Supported Connectivity Measures**
The connectivity method(s) is specified using the ``method`` parameter. The
following methods are supported (note: ``E[]`` denotes average over
epochs). Multiple measures can be computed at once by using a list/tuple,
e.g., ``['coh', 'pli']`` to compute coherence and PLI.
'coh' : Coherence given by::
| E[Sxy] |
C = ---------------------
sqrt(E[Sxx] * E[Syy])

@tsbinns
Copy link
Collaborator

tsbinns commented Aug 1, 2024

A more general comment for the new phase_slope_index_time: Even if we are not averaging over epochs in the call to spectral_connectivity_time(), do you think it makes sense to add an average option which would allow averaging over epochs after PSI has been computed?

Copy link
Collaborator

@tsbinns tsbinns left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So just pretty minor comments. There are a couple formatting things (mainly line length) that I would have thought pre-commit would sort (if you've set that up). Would recommend doing so otherwise getting that CI check to pass will be very tedious...

@@ -559,7 +559,7 @@ def spectral_connectivity_time(
conn_patterns = dict()
for m in method:
# CaCoh complex-valued, all other methods real-valued
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's update the comment to keep it accurate

Suggested change
# CaCoh complex-valued, all other methods real-valued
# Cohy and CaCoh complex-valued, all other methods real-valued

Comment on lines +1237 to +1260
def _cohy(s_xx, s_yy, s_xy):
"""Compute coherencey given the cross spectral density and PSD.

Parameters
----------
s_xx : array-like, shape (n_freqs, n_times)
The PSD of channel 'x'.
s_yy : array-like, shape (n_freqs, n_times)
The PSD of channel 'y'.
s_xy : array-like, shape (n_freqs, n_times)
The cross PSD between channel 'x' and channel 'y' across
frequency and time points.

Returns
-------
cohy : array-like, shape (n_freqs, n_times)
The estimated COHY.
"""
con_num = s_xy.mean(axis=-1, keepdims=True)
con_den = np.sqrt(
s_xx.mean(axis=-1, keepdims=True) * s_yy.mean(axis=-1, keepdims=True)
)
cohy = con_num / con_den
return cohy
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!

Comment on lines +10 to +14
from .base import (
EpochSpectralConnectivity,
SpectralConnectivity,
SpectroTemporalConnectivity,
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Formatting stuff

Suggested change
from .base import (
EpochSpectralConnectivity,
SpectralConnectivity,
SpectroTemporalConnectivity,
)
from .base import (
EpochSpectralConnectivity, SpectralConnectivity, SpectroTemporalConnectivity
)

padding=0,
n_jobs=1,
):
"""Compute the Phase Slope Index (PSI) connectivity measure across time rather than epochs.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would need to shorten this. How about something like "Compute the Phase Slope Index (PSI) connectivity measure across time."?

Comment on lines +283 to +284
This function computes PSI over time from epoched data.
The data may consist of a single epoch.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this, keeps things consistent with spectral_connectivity_time(), but what about also having this be the first thing in the docstring, like in the spectral connectivity function?
That would help make it clearer sooner how this func differs to phase_slope_index().

Comment on lines +335 to +337
See Also
--------
mne_connectivity.EpochSpectralConnectivity
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if it's good to reiterate that spectral_connectivity_time() is a relevant function. Though I realise you based this on the existing function docstring which doesn't link to spec_conn_epochs().


Returns
-------
conn : instance of Connectivity
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#214 made a lot of these typehints more explicit (i.e. EpochSpectralConnectivity instead).

Suggested change
conn : instance of Connectivity
conn : instance of EpochSpectralConnectivity

Comment on lines +252 to +267
@verbose
@fill_doc
def phase_slope_index_time(data,
names=None,
indices=None,
sfreq=2 * np.pi,
mode="multitaper",
fmin=None,
fmax=np.inf,
mt_bandwidth=None,
freqs=None,
n_cycles=7,
padding=0,
n_jobs=1,
):
"""Compute the Phase Slope Index (PSI) connectivity measure across time rather than epochs.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just make sure you add a verbose parameter. That should get the tests running.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also you shouldn't need both verbose and fill_doc decorators. Verbose will trigger the same things as fill_doc


Parameters
----------
data : array-like, shape=(n_epochs, n_signals, n_times)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should also include Epochs (again, I realise you took this from the existing function which doesn't have this, so should change there too).

Comment on lines +44 to +45
def test_psi_time():
"""Test Phase Slope Index (PSI) estimation across time."""
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From a quick glance the test looks good to me, let's see if it passes once the verbose param is added to the new function.

@seqasim
Copy link
Author

seqasim commented Aug 2, 2024

Hi @seqasim, thanks for the PR and sorry for not getting back to you sooner on this. I have a few comments/suggestions which I'll post below, but it's a really good start!

If this is something you're still interested in working on, your contributions are of course very welcome, however I realise it's been a while. If you're not able to invest time into this anymore and you don't want your hard work to go to waste, I can pick things up and we can make sure the changes are added.

Just let us know. Cheers!

Hey,

Thanks for reviewing things! It's super illuminating for me to see the details I missed. I sadly won't have time to follow through on this for a few months, so if you feel you can wrap it up quickly, please don't wait for me! But if not, I'm happy to pick it up with all your helpful comments later in the year.

@tsbinns
Copy link
Collaborator

tsbinns commented Aug 2, 2024

But if not, I'm happy to pick it up with all your helpful comments later in the year.

Yeah sounds good, there's no rush!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants