From 811e861d77b1148e26d0c4346d53b61377307562 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 21 Jun 2022 13:08:58 -0700 Subject: [PATCH 1/6] add plot to dynamic connectivity --- doc/references.bib | 17 ++++++++++ examples/dynamic/mne_var_connectivity.py | 38 +++++++++++++++++++++ mne_connectivity/base.py | 2 +- mne_connectivity/viz/circle.py | 42 ++++++++++++++++++++++-- 4 files changed, 96 insertions(+), 3 deletions(-) diff --git a/doc/references.bib b/doc/references.bib index 24b48327..204163f4 100644 --- a/doc/references.bib +++ b/doc/references.bib @@ -227,6 +227,23 @@ @article{ColcloughEtAl2015 pages = {439--448} } +@article{RecanatesiEtAl2022, + title = {Metastable attractors explain the variable timing of stable behavioral action sequences}, + volume = {110}, + issn = {08966273}, + url = {https://linkinghub.elsevier.com/retrieve/pii/S0896627321007790}, + doi = {10.1016/j.neuron.2021.10.011}, + language = {en}, + number = {1}, + urldate = {2022-03-19}, + journal = {Neuron}, + author = {Recanatesi, Stefano and Pereira-Obilinovic, Ulises and Murakami, Masayoshi and Mainen, Zachary and Mazzucato, Luca}, + month = jan, + year = {2022}, + pages = {139--153.e9}, + file = {Full Text:/Users/alexrockhill/Zotero/storage/PX9Q65RP/Recanatesi et al. - 2022 - Metastable attractors explain the variable timing .pdf:application/pdf}, +} + @article{StamEtAl2012, title={Go with the flow: Use of a directed phase lag index (dPLI) to characterize patterns of phase relations in a large-scale model of brain dynamics}, volume={62}, diff --git a/examples/dynamic/mne_var_connectivity.py b/examples/dynamic/mne_var_connectivity.py index 37c8e552..585beb2d 100644 --- a/examples/dynamic/mne_var_connectivity.py +++ b/examples/dynamic/mne_var_connectivity.py @@ -149,6 +149,44 @@ ).squeeze(0) rescov = np.cov(sampled_residuals) +# %% +# Estimate significant connections using a time-shuffled null distribution +# ------------------------------------------------------------------------ +# We can maintain autocorrelations by shuffling the channels in time relative +# to one another as explained in :ref:`RecanatesiEtAl2022`. The pairwise +# correlations will then be an estimate of connectivity under a null model +# of uncorrelated neural data. + +null_dist = list() +data = epochs.get_data() +rng = np.random.default_rng(99) +for niter in range(20): # 1000 or more would be reasonable for a real analysis + print(f'Computing null connectivity {niter}') + for epo_idx in range(data.shape[0]): + for ch_idx in range(data.shape[1]): + # pick a random starting time for each epoch and channel + start_idx = np.round(rng.random() * data.shape[2]).astype(int) + data[epo_idx, ch_idx] = np.concatenate( + [data[epo_idx, ch_idx, start_idx:], + data[epo_idx, ch_idx, :start_idx]]) + null_dist.append(vector_auto_regression( + data=data, times=times, names=ch_names).get_data()) + +# %% +# Visualize significant connections over time with animation +# ---------------------------------------------------------- +# Let's animate over time to visualize the significant connections at each +# epoch. + +con_data = conn.get_data() +# collapse across epochs since this is inter-ictal/resting state, +# bonferroni correct across epochs +threshes = np.quantile(abs(np.array(null_dist)), + 1 - (0.05 / con_data.shape[0]), + axis=(0, 1)) +n_lines = np.sum(abs(con_data) > threshes, axis=(1, 2)) +anim, ax = conn.plot_circle(n_lines=n_lines) + # Next, we visualize the covariance of residuals. # Here we will see that because we use ordinary # least-squares as an estimation method, the residuals diff --git a/mne_connectivity/base.py b/mne_connectivity/base.py index 491e4b1c..89b99f9f 100644 --- a/mne_connectivity/base.py +++ b/mne_connectivity/base.py @@ -774,7 +774,7 @@ def rename_nodes(self, mapping): @copy_function_doc_to_method_doc(plot_connectivity_circle) def plot_circle(self, **kwargs): - plot_connectivity_circle( + return plot_connectivity_circle( self.get_data(), node_names=self.names, indices=self.indices, **kwargs) diff --git a/mne_connectivity/viz/circle.py b/mne_connectivity/viz/circle.py index 2e3b71e0..185c5361 100644 --- a/mne_connectivity/viz/circle.py +++ b/mne_connectivity/viz/circle.py @@ -5,8 +5,10 @@ # # License: Simplified BSD +import numpy as np from mne.utils import warn from mne.viz.circle import _plot_connectivity_circle +from matplotlib.animation import FuncAnimation def plot_connectivity_circle(con, node_names, indices=None, n_lines=None, @@ -20,7 +22,7 @@ def plot_connectivity_circle(con, node_names, indices=None, n_lines=None, fontsize_title=12, fontsize_names=8, fontsize_colorbar=8, padding=6., ax=None, fig=None, subplot=None, interactive=True, - node_linewidth=2., show=True): + node_linewidth=2., anim_time=3, show=True): """Visualize connectivity as a circular graph. Parameters @@ -100,12 +102,14 @@ def plot_connectivity_circle(con, node_names, indices=None, n_lines=None, node. Right-click shows all connections. node_linewidth : float Line with for nodes. + anim_time : float + The time length for animated plots with connectivity over time. show : bool Show figure if True. Returns ------- - fig : instance of matplotlib.figure.Figure + fig : instance of matplotlib.figure.Figure | instance of matplotlib.animation.FuncAnimation # noqa E501 The figure handle. ax : instance of matplotlib.projections.polar.PolarAxes The subplot handle. @@ -141,6 +145,40 @@ def plot_connectivity_circle(con, node_names, indices=None, n_lines=None, subplot = (subplot,) ax = plt.subplot(*subplot, polar=True) + if con.ndim == 3: + if ax is None: + fig, ax = plt.subplots(subplot_kw=dict(projection='polar'), + facecolor='black') + else: + fig = ax.figure + + def update_connectivity(i): + ax.clear() + these_n_lines = n_lines[i] if isinstance(n_lines, np.ndarray) \ + else n_lines + _plot_connectivity_circle( + con=con[i], node_names=node_names, indices=indices, + n_lines=these_n_lines, node_angles=node_angles, + node_width=node_width, node_height=node_height, + node_colors=node_colors, facecolor=facecolor, + textcolor=textcolor, node_edgecolor=node_edgecolor, + linewidth=linewidth, colormap=colormap, vmin=vmin, vmax=vmax, + colorbar=(i == 0 and colorbar), title=title, + colorbar_size=colorbar_size, + colorbar_pos=colorbar_pos, fontsize_title=fontsize_title, + fontsize_names=fontsize_names, + fontsize_colorbar=fontsize_colorbar, padding=padding, ax=ax, + interactive=interactive, node_linewidth=node_linewidth, + show=show) + # circle is size 10 + ax.text(3 * np.pi / 4, 20, f't = {i}', color='white', + clip_on=False) + + anim = FuncAnimation(fig, update_connectivity, + frames=con.shape[0], + blit=False, repeat=False) + return anim, ax + return _plot_connectivity_circle( con=con, node_names=node_names, indices=indices, n_lines=n_lines, node_angles=node_angles, node_width=node_width, From e61f6b5ffaea562c25aa1c0243b936d93ea4af50 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 21 Jun 2022 13:09:43 -0700 Subject: [PATCH 2/6] cruft --- mne_connectivity/viz/circle.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/mne_connectivity/viz/circle.py b/mne_connectivity/viz/circle.py index 185c5361..4a99f713 100644 --- a/mne_connectivity/viz/circle.py +++ b/mne_connectivity/viz/circle.py @@ -22,7 +22,7 @@ def plot_connectivity_circle(con, node_names, indices=None, n_lines=None, fontsize_title=12, fontsize_names=8, fontsize_colorbar=8, padding=6., ax=None, fig=None, subplot=None, interactive=True, - node_linewidth=2., anim_time=3, show=True): + node_linewidth=2., show=True): """Visualize connectivity as a circular graph. Parameters @@ -102,8 +102,6 @@ def plot_connectivity_circle(con, node_names, indices=None, n_lines=None, node. Right-click shows all connections. node_linewidth : float Line with for nodes. - anim_time : float - The time length for animated plots with connectivity over time. show : bool Show figure if True. @@ -171,7 +169,7 @@ def update_connectivity(i): interactive=interactive, node_linewidth=node_linewidth, show=show) # circle is size 10 - ax.text(3 * np.pi / 4, 20, f't = {i}', color='white', + ax.text(3 * np.pi / 4, 25, f't = {i}', color='white', clip_on=False) anim = FuncAnimation(fig, update_connectivity, From e754fa6f69fd265e9a3281a9dab16aab1258a479 Mon Sep 17 00:00:00 2001 From: Adam Li Date: Wed, 31 Aug 2022 10:45:43 -0400 Subject: [PATCH 3/6] Apply suggestions from code review --- doc/references.bib | 11 +++++++++++ examples/dynamic/mne_var_connectivity.py | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/doc/references.bib b/doc/references.bib index 204163f4..9e38ab90 100644 --- a/doc/references.bib +++ b/doc/references.bib @@ -227,6 +227,17 @@ @article{ColcloughEtAl2015 pages = {439--448} } +@Article{LiAdam2021, + author = {Adam Li and Chester Huynh and Zachary Fitzgerald and Iahn Cajigas and Damian Brusko and Jonathan Jagid and Angel O. Claudio and Andres M. Kanner and Jennifer Hopp and Stephanie Chen and Jennifer Haagensen and Emily Johnson and William Anderson and Nathan Crone and Sara Inati and Kareem A. Zaghloul and Juan Bulacio and Jorge Gonzalez-Martinez and Sridevi V. Sarma}, + doi = {10.1038/s41593-021-00901-w}, + issn = {15461726}, + issue = {10}, + journal = {Nature Neuroscience}, + title = {Neural fragility as an EEG marker of the seizure onset zone}, + volume = {24}, + year = {2021}, +} + @article{RecanatesiEtAl2022, title = {Metastable attractors explain the variable timing of stable behavioral action sequences}, volume = {110}, diff --git a/examples/dynamic/mne_var_connectivity.py b/examples/dynamic/mne_var_connectivity.py index 585beb2d..4b525908 100644 --- a/examples/dynamic/mne_var_connectivity.py +++ b/examples/dynamic/mne_var_connectivity.py @@ -176,7 +176,7 @@ # Visualize significant connections over time with animation # ---------------------------------------------------------- # Let's animate over time to visualize the significant connections at each -# epoch. +# epoch. As mentioned in https://openneuro.org/datasets/ds003029 and :footcite:`LiAdam2021`, the ``AST`` channel region was considered epileptic by clinicians for this sample subject and surgically resected. It is interesting to therefore see that there are consistently strong connections from this region to others. con_data = conn.get_data() # collapse across epochs since this is inter-ictal/resting state, From c32658459beec7060b6d5ed7766946f6000f2c7e Mon Sep 17 00:00:00 2001 From: Adam Li Date: Wed, 31 Aug 2022 10:46:05 -0400 Subject: [PATCH 4/6] Update doc/references.bib --- doc/references.bib | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/references.bib b/doc/references.bib index 9e38ab90..7de23e5d 100644 --- a/doc/references.bib +++ b/doc/references.bib @@ -252,7 +252,6 @@ @article{RecanatesiEtAl2022 month = jan, year = {2022}, pages = {139--153.e9}, - file = {Full Text:/Users/alexrockhill/Zotero/storage/PX9Q65RP/Recanatesi et al. - 2022 - Metastable attractors explain the variable timing .pdf:application/pdf}, } @article{StamEtAl2012, From 244e7aedac6079adcd2fb6908d7b423d160c249a Mon Sep 17 00:00:00 2001 From: Adam Li Date: Wed, 31 Aug 2022 10:46:44 -0400 Subject: [PATCH 5/6] Update examples/dynamic/mne_var_connectivity.py --- examples/dynamic/mne_var_connectivity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/dynamic/mne_var_connectivity.py b/examples/dynamic/mne_var_connectivity.py index 4b525908..bed912f9 100644 --- a/examples/dynamic/mne_var_connectivity.py +++ b/examples/dynamic/mne_var_connectivity.py @@ -153,7 +153,7 @@ # Estimate significant connections using a time-shuffled null distribution # ------------------------------------------------------------------------ # We can maintain autocorrelations by shuffling the channels in time relative -# to one another as explained in :ref:`RecanatesiEtAl2022`. The pairwise +# to one another as explained in :footcite:`RecanatesiEtAl2022`. The pairwise # correlations will then be an estimate of connectivity under a null model # of uncorrelated neural data. From 019e008e3fa0a4f1fe2d68c695721c208fefb41d Mon Sep 17 00:00:00 2001 From: Alex Rockhill Date: Fri, 21 Oct 2022 13:01:34 -0700 Subject: [PATCH 6/6] clean up --- doc/references.bib | 4 +- examples/dynamic/mne_var_connectivity.py | 136 ++++++++-------------- examples/sensor_connectivity.py | 10 +- mne_connectivity/viz/circle.py | 2 +- mne_connectivity/viz/tests/test_circle.py | 6 + 5 files changed, 59 insertions(+), 99 deletions(-) diff --git a/doc/references.bib b/doc/references.bib index 7de23e5d..2a4c04be 100644 --- a/doc/references.bib +++ b/doc/references.bib @@ -203,7 +203,7 @@ @article{NolteEtAl2004 year = {2004} } -@INPROCEEDINGS{li_linear_2017, +@INPROCEEDINGS{LiEtAl2017, author = {Li, Adam and Gunnarsdottir, Kristin M. and Inati, Sara and Zaghloul, Kareem and Gale, John and Bulacio, Juan and Martinez-Gonzalez, Jorge and Sarma, Sridevi V.}, booktitle = {2017 39th Annual International Conference of the IEEE Engineering in Medicine and Biology Society (EMBC)}, title = {Linear time-varying model characterizes invasive EEG signals generated from complex epileptic networks}, @@ -227,7 +227,7 @@ @article{ColcloughEtAl2015 pages = {439--448} } -@Article{LiAdam2021, +@Article{LiEtAl2021, author = {Adam Li and Chester Huynh and Zachary Fitzgerald and Iahn Cajigas and Damian Brusko and Jonathan Jagid and Angel O. Claudio and Andres M. Kanner and Jennifer Hopp and Stephanie Chen and Jennifer Haagensen and Emily Johnson and William Anderson and Nathan Crone and Sara Inati and Kareem A. Zaghloul and Juan Bulacio and Jorge Gonzalez-Martinez and Sridevi V. Sarma}, doi = {10.1038/s41593-021-00901-w}, issn = {15461726}, diff --git a/examples/dynamic/mne_var_connectivity.py b/examples/dynamic/mne_var_connectivity.py index bed912f9..3cde0e7d 100644 --- a/examples/dynamic/mne_var_connectivity.py +++ b/examples/dynamic/mne_var_connectivity.py @@ -6,21 +6,21 @@ =================================================== Compute a VAR (linear system) model from time-series -activity :footcite:`li_linear_2017` using a -continuous iEEG recording. +activity :footcite:`LiEtAl2017`. In this example, we will demonstrate how to compute a VAR model with different statistical assumptions. """ # Authors: Adam Li +# Alex Rockhill # # License: BSD (3-clause) + import numpy as np import mne -from mne import make_fixed_length_epochs -from mne_bids import BIDSPath, read_raw_bids +from mne.datasets import sample import matplotlib.pyplot as plt @@ -29,80 +29,31 @@ # %% # Load the data # ------------- -# Here, we first download an ECoG dataset that was recorded from a patient with -# epilepsy. To facilitate loading the data, we use `mne-bids -# `_. +# Here, we first download a somatosensory dataset. # # Then, we will do some basic filtering and preprocessing using MNE-Python. -# paths to mne datasets - sample ECoG -bids_root = mne.datasets.epilepsy_ecog.data_path() - -# first define the BIDS path -bids_path = BIDSPath(root=bids_root, subject='pt1', session='presurgery', - task='ictal', datatype='ieeg', extension='vhdr') +data_path = sample.data_path() +raw_fname = data_path / 'MEG' / 'sample' / \ + 'sample_audvis_filt-0-40_raw.fif' +event_fname = data_path / 'MEG' / 'sample' / \ + 'sample_audvis_filt-0-40_raw-eve.fif' -# Then we'll use it to load in the sample dataset. Here we use a format (iEEG) -# that is only available in MNE-BIDS 0.7+, so it will emit a warning on -# versions <= 0.6 -raw = read_raw_bids(bids_path=bids_path, verbose=False) +# Setup for reading the raw data +raw = mne.io.read_raw_fif(raw_fname) +events = mne.read_events(event_fname) -line_freq = raw.info['line_freq'] -print(f'Data has a power line frequency at {line_freq}.') +# Add a bad channel +raw.info['bads'] += ['MEG 2443'] -# Pick only the ECoG channels, removing the ECG channels -raw.pick_types(ecog=True) +# Pick MEG gradiometers +picks = mne.pick_types(raw.info, meg='grad', eeg=False, stim=False, eog=True, + exclude='bads') -# Load the data -raw.load_data() - -# Then we remove line frequency interference -raw.notch_filter(line_freq) - -# drop bad channels -raw.drop_channels(raw.info['bads']) - -# %% -# Crop the data for this example -# ------------------------------ -# -# We find the onset time of the seizure and remove all data after that time. -# In this example, we are only interested in analyzing the interictal -# (non-seizure) data period. -# -# One might be interested in analyzing the seizure period also, which we -# leave as an exercise for our readers! - -# Find the annotated events -events, event_id = mne.events_from_annotations(raw) - -# get sample at which seizure starts -onset_id = event_id['onset'] -onset_idx = np.argwhere(events[:, 2] == onset_id) -onset_sample = events[onset_idx, 0].squeeze() -onset_sec = onset_sample / raw.info['sfreq'] - -# remove all data after the seizure onset -raw = raw.crop(tmin=0, tmax=onset_sec, include_tmax=False) - -# %% -# Create Windows of Data (Epochs) Using MNE-Python -# ------------------------------------------------ -# We have a continuous iEEG snapshot that is about 60 seconds long -# (after cropping). We would like to estimate a VAR model over a sliding window -# of 500 milliseconds with a 250 millisecond step size. -# -# We can use `mne.make_fixed_length_epochs` to create an Epochs data structure -# representing this sliding window. - -epochs = make_fixed_length_epochs(raw=raw, duration=0.5, overlap=0.25) -times = epochs.times -ch_names = epochs.ch_names - -print(epochs) -print(epochs.times) -print(epochs.event_id) -print(epochs.events) +# Create epochs for the visual condition +event_id, tmin, tmax = 3, -0.2, 1.5 # need a long enough epoch for 5 cycles +epochs = mne.Epochs(raw, events, event_id, tmin, tmax, picks=picks, + baseline=(None, 0), reject=dict(grad=4000e-13, eog=150e-6)) # %% @@ -114,7 +65,7 @@ # time-varying linear system. conn = vector_auto_regression( - data=epochs.get_data(), times=times, names=ch_names) + data=epochs.get_data(), times=epochs.times, names=epochs.ch_names) # this returns a connectivity structure over time print(conn) @@ -149,6 +100,15 @@ ).squeeze(0) rescov = np.cov(sampled_residuals) +# Next, we visualize the covariance of residuals. +# Here we will see that because we use ordinary +# least-squares as an estimation method, the residuals +# should come with low covariances. +fig, ax = plt.subplots() +cax = fig.add_axes([0.27, 0.8, 0.5, 0.05]) +im = ax.imshow(rescov, cmap='viridis', aspect='equal', interpolation='none') +fig.colorbar(im, cax=cax, orientation='horizontal') + # %% # Estimate significant connections using a time-shuffled null distribution # ------------------------------------------------------------------------ @@ -160,7 +120,7 @@ null_dist = list() data = epochs.get_data() rng = np.random.default_rng(99) -for niter in range(20): # 1000 or more would be reasonable for a real analysis +for niter in range(10): # 1000 or more would be reasonable for a real analysis print(f'Computing null connectivity {niter}') for epo_idx in range(data.shape[0]): for ch_idx in range(data.shape[1]): @@ -170,31 +130,27 @@ [data[epo_idx, ch_idx, start_idx:], data[epo_idx, ch_idx, :start_idx]]) null_dist.append(vector_auto_regression( - data=data, times=times, names=ch_names).get_data()) + data=data, times=epochs.times, names=epochs.ch_names).get_data()) + +null_dist = np.array(null_dist) # %% # Visualize significant connections over time with animation # ---------------------------------------------------------- # Let's animate over time to visualize the significant connections at each -# epoch. As mentioned in https://openneuro.org/datasets/ds003029 and :footcite:`LiAdam2021`, the ``AST`` channel region was considered epileptic by clinicians for this sample subject and surgically resected. It is interesting to therefore see that there are consistently strong connections from this region to others. +# epoch. con_data = conn.get_data() -# collapse across epochs since this is inter-ictal/resting state, -# bonferroni correct across epochs -threshes = np.quantile(abs(np.array(null_dist)), - 1 - (0.05 / con_data.shape[0]), + +# to bonferroni correct across epochs, use the following: +threshes = np.quantile(abs(null_dist), 1 - (0.05 / con_data.shape[0]), axis=(0, 1)) -n_lines = np.sum(abs(con_data) > threshes, axis=(1, 2)) -anim, ax = conn.plot_circle(n_lines=n_lines) -# Next, we visualize the covariance of residuals. -# Here we will see that because we use ordinary -# least-squares as an estimation method, the residuals -# should come with low covariances. -fig, ax = plt.subplots() -cax = fig.add_axes([0.27, 0.8, 0.5, 0.05]) -im = ax.imshow(rescov, cmap='viridis', aspect='equal', interpolation='none') -fig.colorbar(im, cax=cax, orientation='horizontal') +# now, plot the connectivity as it changes for each epoch +n_lines = np.sum(abs(con_data) > threshes, axis=(1, 2)) +fig, ax = plt.subplots(subplot_kw=dict(projection='polar'), figsize=(10, 10)) +anim, ax = conn.plot_circle(n_lines=n_lines, fontsize_names=4, + fig=fig, ax=ax) # %% # Compute one VAR model using all epochs @@ -205,7 +161,7 @@ # epochs. conn = vector_auto_regression( - data=epochs.get_data(), times=times, names=ch_names, + data=epochs.get_data(), times=epochs.times, names=epochs.ch_names, model='avg-epochs') # this returns a connectivity structure over time diff --git a/examples/sensor_connectivity.py b/examples/sensor_connectivity.py index f49ea27c..ecb8a26f 100644 --- a/examples/sensor_connectivity.py +++ b/examples/sensor_connectivity.py @@ -12,8 +12,6 @@ # # License: BSD (3-clause) -import os.path as op - import mne from mne_connectivity import spectral_connectivity_epochs from mne.datasets import sample @@ -24,10 +22,10 @@ ############################################################################### # Set parameters data_path = sample.data_path() -raw_fname = op.join(data_path, 'MEG', 'sample', - 'sample_audvis_filt-0-40_raw.fif') -event_fname = op.join(data_path, 'MEG', 'sample', - 'sample_audvis_filt-0-40_raw-eve.fif') +raw_fname = data_path / 'MEG' / 'sample' / \ + 'sample_audvis_filt-0-40_raw.fif' +event_fname = data_path / 'MEG' / 'sample' / \ + 'sample_audvis_filt-0-40_raw-eve.fif' # Setup for reading the raw data raw = mne.io.read_raw_fif(raw_fname) diff --git a/mne_connectivity/viz/circle.py b/mne_connectivity/viz/circle.py index 4a99f713..0e34827c 100644 --- a/mne_connectivity/viz/circle.py +++ b/mne_connectivity/viz/circle.py @@ -169,7 +169,7 @@ def update_connectivity(i): interactive=interactive, node_linewidth=node_linewidth, show=show) # circle is size 10 - ax.text(3 * np.pi / 4, 25, f't = {i}', color='white', + ax.text(3 * np.pi / 4, 25, f'epoch = {i}', color='white', clip_on=False) anim = FuncAnimation(fig, update_connectivity, diff --git a/mne_connectivity/viz/tests/test_circle.py b/mne_connectivity/viz/tests/test_circle.py index a3fe667b..9807ced1 100644 --- a/mne_connectivity/viz/tests/test_circle.py +++ b/mne_connectivity/viz/tests/test_circle.py @@ -10,6 +10,7 @@ import matplotlib.pyplot as plt from mne.viz import circular_layout +from mne_connectivity import EpochConnectivity from mne_connectivity.viz import plot_connectivity_circle @@ -92,4 +93,9 @@ def test_plot_connectivity_circle(): group_boundaries=[-1]) pytest.raises(ValueError, circular_layout, label_names, node_order, group_boundaries=[20, 0]) + + # test animation + conn = EpochConnectivity(data=np.repeat(con.flatten()[None], 10, axis=0), + n_nodes=68) + anim, ax = conn.plot_circle(n_lines=3) plt.close('all')