-
Notifications
You must be signed in to change notification settings - Fork 34
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
[ENH] Add support for ragged connections with multivariate methods with padding #142
Merged
Merged
Changes from 8 commits
Commits
Show all changes
48 commits
Select commit
Hold shift + click to select a range
73792c3
added support for ragged connections
tsbinns a3354cd
added author
tsbinns 63f2c39
bug fix ragged indices comparison
tsbinns d2f3de8
bug fix ragged indices comparison
tsbinns ec36983
bug fix ragged indices comparison
tsbinns f70ed11
Merge branch 'main' into pr-mvc_padding
adam2392 02f5ad0
added extra multivariate indices unit test
tsbinns 684e35f
Merge branch 'main' into pr-mvc_padding
tsbinns c229d4e
updated utils tests and docs
tsbinns ffca93f
bug fix utils doc update
tsbinns c9855b7
bug fix utils doc update
tsbinns 8c35917
bug fix utils doc update
tsbinns 9bd2513
Merge branch 'main' into pr-mvc_padding
tsbinns 409c2c6
updated spectral tests
tsbinns fe0fe68
added note for refactoring
tsbinns b7fcf12
updated spectral tests
tsbinns 96a0dcf
Update ignore words
adam2392 f837889
added error message
tsbinns ff33e56
Added formatting suggestions
tsbinns 95cd3c2
added max_n_chans suggestion
tsbinns 42085e1
Merge branch 'pr-mvc_padding' of https://github.com/tsbinns/mne-conne…
tsbinns 402cfaa
updated epochs docstring
tsbinns ebe0a69
added test suggestion
tsbinns 1aa45d4
fixed style errors
tsbinns 5497b55
Merge branch 'mne-tools:main' into pr-mvc_padding
tsbinns 5bad5fb
Squashed commit of the following:
tsbinns 238c0de
try fix ci error
tsbinns 6fe682f
bug fix missing refactoring for example
tsbinns 921cf9d
switch to masked arrays for indices
tsbinns 27fac57
fix spelling error
tsbinns e0ebf2d
try fix codespell error
tsbinns 8799ee2
Merge branch 'main' into pr-mvc_padding
larsoner f54cecc
Revert "bug fix missing refactoring for example"
tsbinns 610ab7d
Revert "Squashed commit of the following:"
tsbinns f106d07
Revert "Squashed commit of the following:"
tsbinns c985886
switched to masked indices for multivariate conn
tsbinns a6a58e4
Revert "switch to masked arrays for indices"
tsbinns 034dafe
Revert "bug fix missing refactoring for example"
tsbinns 4907106
Revert "Squashed commit of the following:"
tsbinns 736e642
Merge branch 'pr-mvc_padding_revert' into pr-mvc_padding
tsbinns cd69d65
updated time
tsbinns d6ba398
switched to masked indices for multivariate conn
tsbinns 0732d22
removed redundant ignored word
tsbinns cd0b90f
removed redundant list creation
tsbinns 883e816
updated default non-zero rank tolerance
tsbinns ff3b9e9
Merge branch 'pr-mvc_padding' of https://github.com/tsbinns/mne-conne…
tsbinns f5c1c3e
switched to array indices & added inline comments
tsbinns bac5000
fixed grammar mistake
tsbinns File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
""" | ||
========================================================= | ||
Working with ragged indices for multivariate connectivity | ||
========================================================= | ||
|
||
This example demonstrates how multivariate connectivity involving different | ||
numbers of seeds and targets can be handled in MNE-Connectivity. | ||
""" | ||
|
||
# Author: Thomas S. Binns <t.s.binns@outlook.com> | ||
# License: BSD (3-clause) | ||
|
||
# %% | ||
|
||
import numpy as np | ||
|
||
from mne_connectivity import spectral_connectivity_epochs | ||
|
||
############################################################################### | ||
# Background | ||
# ---------- | ||
# | ||
# With multivariate connectivity, interactions between multiple signals can be | ||
# considered together, and the number of signals designated as seeds and | ||
# targets does not have to be equal within or across connections. Issues can | ||
# arise from this when storing information associated with connectivity in | ||
# arrays, as the number of entries within each dimension can vary within and | ||
# across connections depending on the number of seeds and targets. Such arrays | ||
# are 'ragged', and support for ragged arrays is limited in NumPy to the | ||
# ``object`` datatype. Not only is working with ragged arrays is cumbersome, | ||
# but saving arrays with ``dtype='object'`` is not supported by the h5netcdf | ||
# engine used to save connectivity objects. The workaround used in | ||
# MNE-Connectivity is to pad ragged arrays with some known values according to | ||
# the largest number of entries in each dimension, such that there is an equal | ||
# amount of information across and within connections for each dimension of the | ||
# arrays. | ||
# | ||
# As an example, consider we have 5 channels and want to compute 2 connections: | ||
# the first between channels in indices 0 and 1 with those in indices 2, 3, | ||
# and 4; and the second between channels 0, 1, 2, and 3 with channel 4. The | ||
# seed and target indices can be written as such:: | ||
# | ||
# seeds = [[0, 1 ], [0, 1, 2, 3]] | ||
# targets = [[2, 3, 4], [4 ]] | ||
# | ||
# The ``indices`` parameter passed to | ||
# :func:`~mne_connectivity.spectral_connectivity_epochs` and | ||
# :func:`~mne_connectivity.spectral_connectivity_time` must be a tuple of | ||
# array-likes, meaning | ||
# that the indices can be passed as a tuple of: lists; tuples; or NumPy arrays. | ||
# Examples of how ``indices`` can be formed are shown below:: | ||
# | ||
# # tuple of lists | ||
# ragged_indices = ([[0, 1 ], [0, 1, 2, 3]], | ||
# [[2, 3, 4], [4 ]]) | ||
# | ||
# # tuple of tuples | ||
# ragged_indices = (((0, 1 ), (0, 1, 2, 3)), | ||
# ((2, 3, 4), (4 ))) | ||
# | ||
# # tuple of arrays | ||
# ragged_indices = (np.array([[0, 1 ], [0, 1, 2, 3]], dtype='object'), | ||
# np.array([[2, 3, 4], [4 ]], dtype='object')) | ||
# | ||
# **N.B. Note that when forming ragged arrays in NumPy, dtype='object' must be | ||
# specified.** | ||
# | ||
# Just as for bivariate connectivity, the length of ``indices[0]`` and | ||
# ``indices[1]`` is equal (i.e. the number of connections), however information | ||
# about the multiple channel indices for each connection is stored in a nested | ||
# array. Importantly, these indices are ragged, as the first connection will be | ||
# computed between 2 seed and 3 target channels, and the second connection | ||
# between 4 seed and 1 target channel. The connectivity functions will | ||
# recognise the indices as being ragged, and pad them accordingly to make them | ||
# easier to work with and compatible with the h5netcdf saving engine. The known | ||
# value used to pad the arrays is ``-1``, an invalid channel index. The above | ||
# indices would be padded to:: | ||
# | ||
# padded_indices = (np.array([[0, 1, -1, -1], [0, 1, 2, 3]]), | ||
# np.array([[2, 3, 4, -1], [4, -1, -1, -1]])) | ||
# | ||
# These indices are what is stored in the connectivity object, and is also the | ||
# format of indices returned from the helper functions | ||
# :func:`~mne_connectivity.check_multivariate_indices` and | ||
# :func:`~mne_connectivity.seed_target_multivariate_indices`. It is also | ||
# possible to pass the padded indices to the connectivity functions directly. | ||
# | ||
# For the connectivity results themselves, the methods available in | ||
# MNE-Connectivity combine information across the different channels into a | ||
# single (time-)frequency-resolved connectivity spectrum, regardless of the | ||
# number of seed and target channels, so ragged arrays are not a concern here. | ||
# However, the maximised imaginary part of coherency (MIC) method also returns | ||
# spatial patterns of connectivity, which show the contribution of each channel | ||
# to the dimensionality-reduced connectivity estimate (explained in more detail | ||
# in :doc:`mic_mim`). Because these patterns are returned for each channel, | ||
# their shape can vary depending on the number of seeds and targets in each | ||
# connection, making them ragged. To avoid this, the patterns are padded along | ||
# the channel axis with the known and invalid entry ``np.nan``, in line with | ||
# that applied to ``indices``. Extracting only the valid spatial patterns from | ||
# the connectivity object is trivial, as shown below: | ||
|
||
# %% | ||
|
||
# create random data | ||
data = np.random.randn(10, 5, 200) # epochs x channels x times | ||
sfreq = 50 | ||
ragged_indices = ([[0, 1], [0, 1, 2, 3]], # seeds | ||
[[2, 3, 4], [4]]) # targets | ||
|
||
# compute connectivity | ||
con = spectral_connectivity_epochs( | ||
data, method='mic', indices=ragged_indices, sfreq=sfreq, fmin=10, fmax=30, | ||
verbose=False) | ||
patterns = np.array(con.attrs['patterns']) | ||
padded_indices = con.indices | ||
n_freqs = con.get_data().shape[-1] | ||
n_cons = len(ragged_indices[0]) | ||
max_n_chans = max( | ||
[len(inds) for inds in ([*ragged_indices[0], *ragged_indices[1]])]) | ||
|
||
# show that the padded indices entries are all -1 | ||
assert np.count_nonzero(padded_indices[0][0] == -1) == 2 # 2 padded channels | ||
assert np.count_nonzero(padded_indices[1][0] == -1) == 1 # 1 padded channels | ||
assert np.count_nonzero(padded_indices[0][1] == -1) == 0 # 0 padded channels | ||
assert np.count_nonzero(padded_indices[1][1] == -1) == 3 # 3 padded channels | ||
|
||
# patterns have shape [seeds/targets x cons x max channels x freqs (x times)] | ||
assert patterns.shape == (2, n_cons, max_n_chans, n_freqs) | ||
|
||
# show that the padded patterns entries are all np.nan | ||
assert np.all(np.isnan(patterns[0, 0, 2:])) # 2 padded channels | ||
assert np.all(np.isnan(patterns[1, 0, 3:])) # 1 padded channels | ||
assert not np.any(np.isnan(patterns[0, 1])) # 0 padded channels | ||
assert np.all(np.isnan(patterns[1, 1, 1:])) # 3 padded channels | ||
|
||
# extract patterns for first connection using the ragged indices | ||
seed_patterns_con1 = patterns[0, 0, :len(ragged_indices[0][0])] | ||
target_patterns_con1 = patterns[1, 0, :len(ragged_indices[1][0])] | ||
|
||
# extract patterns for second connection using the padded indices (pad = -1) | ||
seed_patterns_con2 = ( | ||
patterns[0, 1, :np.count_nonzero(padded_indices[0][1] != -1)]) | ||
target_patterns_con2 = ( | ||
patterns[1, 1, :np.count_nonzero(padded_indices[1][1] != -1)]) | ||
|
||
# show that shapes of patterns are correct | ||
assert seed_patterns_con1.shape == (2, n_freqs) # channels (0, 1) | ||
assert target_patterns_con1.shape == (3, n_freqs) # channels (2, 3, 4) | ||
assert seed_patterns_con2.shape == (4, n_freqs) # channels (0, 1, 2, 3) | ||
assert target_patterns_con2.shape == (1, n_freqs) # channels (4) | ||
|
||
print('Assertions completed successfully!') | ||
|
||
# %% |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want, you could add this to
expected_failing_examples
then you don't have to try/except at all here. Just let it actually fail and sphinx-gallery will print a nicely formatted traceback for you.https://sphinx-gallery.github.io/stable/configuration.html#dont-fail-exit
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The traceback in the file works really nicely, but is it possible to overwrite the "BROKEN" thumbnail?
E.g. setting
# sphinx_gallery_thumbnail_path = '_static/granger_causality_gallery_thumbnail.png'
within the example did not have an effect.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm not at the moment. Feel free to open an issue at https://github.com/sphinx-gallery/sphinx-gallery about adding this possibility
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Opened! sphinx-gallery/sphinx-gallery#1220
If the others agree, I would stick with the try-except approach until this behaviour in sphinx gallery is changed, so as not to give the impression from the thumbnail that the entire example is about failing code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is fine as is already, no need to wait to merge for this. It's an easy follow up PR after this is merged and SG has the machinery it needs