summaryrefslogtreecommitdiff
path: root/doc/examples/resting_state_fmri.py
diff options
context:
space:
mode:
Diffstat (limited to 'doc/examples/resting_state_fmri.py')
-rw-r--r--doc/examples/resting_state_fmri.py366
1 files changed, 366 insertions, 0 deletions
diff --git a/doc/examples/resting_state_fmri.py b/doc/examples/resting_state_fmri.py
new file mode 100644
index 0000000..acf29ea
--- /dev/null
+++ b/doc/examples/resting_state_fmri.py
@@ -0,0 +1,366 @@
+"""
+
+.. _resting-state:
+
+===============================
+Coherency analysis of fMRI data
+===============================
+
+The fMRI data-set analyzed in the following examples was contributed by Beth
+Mormino. The data is taken from a single subject in a "resting-state" scan, in
+which subjects are fixating on a cross and maintaining alert wakefulness, but
+not performing any other behavioral task.
+
+The data was pre-processed and time-series of BOLD responses were extracted
+from different regions of interest (ROIs) in the brain. The data is organized
+in csv file, where each column corresponds to an ROI and each row corresponds
+to a sampling point.
+
+In the following, we will demonstrate some simple time-series analysis and
+visualization techniques which can be applied to this kind of data.
+
+
+We start by importing the necessary modules/functions, defining the
+sampling_interval of the data (TR, or repetition time) and the frequency band
+of interest:
+
+"""
+
+import os
+
+#Import from other libraries:
+import numpy as np
+import matplotlib.pyplot as plt
+from matplotlib.mlab import csv2rec
+
+import nitime
+#Import the time-series objects:
+from nitime.timeseries import TimeSeries
+#Import the analysis objects:
+from nitime.analysis import CorrelationAnalyzer, CoherenceAnalyzer
+#Import utility functions:
+from nitime.utils import percent_change
+from nitime.viz import drawmatrix_channels, drawgraph_channels, plot_xcorr
+
+#This information (the sampling interval) has to be known in advance:
+TR = 1.89
+f_lb = 0.02
+f_ub = 0.15
+
+"""
+
+We use csv2rec to read the data in from file to a recarray:
+
+"""
+
+data_path = os.path.join(nitime.__path__[0], 'data')
+
+data_rec = csv2rec(os.path.join(data_path, 'fmri_timeseries.csv'))
+
+"""
+This data structure contains in its dtype a field 'names', which contains the
+first row in each column. In this case, that is the labels of the ROIs from
+which the data in each column was extracted. The data from the recarray is
+extracted into a 'standard' array and, for each ROI, it is normalized to
+percent signal change, using the utils.percent_change function.
+
+"""
+
+#Extract information:
+roi_names = np.array(data_rec.dtype.names)
+n_samples = data_rec.shape[0]
+
+
+#Make an empty container for the data
+data = np.zeros((len(roi_names), n_samples))
+
+for n_idx, roi in enumerate(roi_names):
+ data[n_idx] = data_rec[roi]
+
+#Normalize the data:
+data = percent_change(data)
+
+
+"""
+
+We initialize a TimeSeries object from the normalized data:
+
+"""
+
+T = TimeSeries(data, sampling_interval=TR)
+T.metadata['roi'] = roi_names
+
+
+"""
+
+First, we examine the correlations between the time-series extracted from
+different parts of the brain. The following script extracts the data (using the
+draw_matrix function, displaying the correlation matrix with the ROIs labeled.
+
+"""
+
+#Initialize the correlation analyzer
+C = CorrelationAnalyzer(T)
+
+#Display the correlation matrix
+fig01 = drawmatrix_channels(C.corrcoef, roi_names, size=[10., 10.], color_anchor=0)
+
+
+"""
+
+.. image:: fig/resting_state_fmri_01.png
+
+Notice that setting the color_anchor input to this function to 0 makes sure
+that the center of the color map (here a blue => white => red) is at 0. In this
+case, positive values will be displayed as red and negative values in blue.
+
+We notice that the left caudate nucleus (labeled 'lcau') has an interesting
+pattern of correlations. It has a high correlation with both the left putamen
+('lput', which is located nearby) and also with the right caudate nucleus
+('lcau'), which is the homologous region in the other hemisphere. Are these two
+correlation values related to each other? The right caudate and left putamen
+seem to have a moderately low correlation value. One way to examine this
+question is by looking at the temporal structure of the cross-correlation
+functions. In order to do that, from the CorrelationAnalyzer object, we extract
+the normalized cross-correlation function. This results in another TimeSeries`
+object, which contains the full time-series of the cross-correlation between
+any combination of time-series from the different channels in the time-series
+object. We can pass the resulting object, together with a list of indices to
+the viz.plot_xcorr function, which visualizes the chosen combinations of
+series:
+
+"""
+
+xc = C.xcorr_norm
+
+idx_lcau = np.where(roi_names == 'lcau')[0]
+idx_rcau = np.where(roi_names == 'rcau')[0]
+idx_lput = np.where(roi_names == 'lput')[0]
+idx_rput = np.where(roi_names == 'rput')[0]
+
+fig02 = plot_xcorr(xc,
+ ((idx_lcau, idx_rcau),
+ (idx_lcau, idx_lput)),
+ line_labels=['rcau', 'lput'])
+
+
+"""
+
+.. image:: fig/resting_state_fmri_02.png
+
+
+Note that the correlation is normalized, so that the the value of the
+cross-correlation functions at the zero-lag point (time = 0 sec) is equal to
+the Pearson correlation between the two time-series. We observe that there are
+correlations larger than the zero-lag correlation occurring at other
+time-points preceding and following the zero-lag. This could arise because of a
+more complex interplay of activity between two areas, which is not captured by
+the correlation and can also arise because of differences in the
+characteristics of the HRF in the two ROIs. One method of analysis which can
+mitigate these issues is analysis of coherency between time-series
+[Sun2005]_. This analysis computes an equivalent of the correlation in the
+frequency domain:
+
+.. math::
+
+ R_{xy} (\lambda) = \frac{f_{xy}(\lambda)}
+ {\sqrt{f_{xx} (\lambda) \cdot f_{yy}(\lambda)}}
+
+Because this is a complex number, this computation results in two
+quantities. First, the magnitude of this number, also referred to as
+"coherence":
+
+.. math::
+
+ Coh_{xy}(\lambda) = |{R_{xy}(\lambda)}|^2 =
+ \frac{|{f_{xy}(\lambda)}|^2}{f_{xx}(\lambda) \cdot f_{yy}(\lambda)}
+
+This is a measure of the pairwise coupling between the two time-series. It can
+vary between 0 and 1, with 0 being complete independence and 1 being complete
+coupling. A time-series would have a coherence of 1 with itself, but not only:
+since this measure is independent of the relative phase of the two time-series,
+the coherence between a time-series and any phase-shifted version of itself
+will also be equal to 1.
+
+However, the relative phase is another quantity which can be derived from this
+computation:
+
+.. math::
+
+ \phi(\lambda) = arg [R_{xy} (\lambda)] = arg [f_{xy} (\lambda)]
+
+
+This value can be used in order to infer which area is leading and which area
+is lagging (according to the sign of the relative phase) and, can be used to
+compute the temporal delay between activity in one ROI and the other.
+
+First, let's look at the pair-wise coherence between all our ROIs. This can be
+done by creating a CoherenceAnalyzer object.
+
+"""
+
+C = CoherenceAnalyzer(T)
+
+"""
+
+Once this object is initialized with the TimeSeries object, the mid-frequency
+of the frequency bands represented in the spectral decomposition of the
+time-series can be accessed in the 'frequencies' attribute of the object. The
+spectral resolution of this representation is the same one used in the
+computation of the coherence.
+
+Since the fMRI BOLD data contains data in frequencies which are not
+physiologically relevant (presumably due to machine noise and fluctuations in
+physiological measures unrelated to neural activity), we focus our analysis on
+a band of frequencies between 0.02 and 0.15 Hz. This is easily achieved by
+determining the values of the indices in :attr:`C.frequencies` and using those
+indices in accessing the data in :attr:`C.coherence`. The coherence is then
+averaged across all these frequency bands.
+
+"""
+
+freq_idx = np.where((C.frequencies > f_lb) * (C.frequencies < f_ub))[0]
+
+"""
+The C.coherence attribute is an ndarray of dimensions $n_{ROI}$ by $n_{ROI}$ by
+$n_{frequencies}$.
+
+We extract the coherence in that frequency band, average across the frequency
+bands of interest and pass that to the visualization function:
+
+"""
+
+
+coh = np.mean(C.coherence[:, :, freq_idx], -1) # Averaging on the last dimension
+fig03 = drawmatrix_channels(coh, roi_names, size=[10., 10.], color_anchor=0)
+
+"""
+
+.. image:: fig/resting_state_fmri_03.png
+
+We can also focus in on the ROIs we were interested in. This requires a little
+bit more manipulation of the indices into the coherence matrix:
+
+"""
+
+idx = np.hstack([idx_lcau, idx_rcau, idx_lput, idx_rput])
+idx1 = np.vstack([[idx[i]] * 4 for i in range(4)]).ravel()
+idx2 = np.hstack(4 * [idx])
+
+coh = C.coherence[idx1, idx2].reshape(4, 4, C.frequencies.shape[0])
+
+"""
+
+Extract the coherence and average across the same frequency bands as before:
+
+"""
+
+
+coh = np.mean(coh[:, :, freq_idx], -1) # Averaging on the last dimension
+
+"""
+
+Finally, in this case, we visualize the adjacency matrix, by creating a network
+graph of these ROIs (this is done by using the function drawgraph_channels
+which relies on `networkx <http://networkx.lanl.gov>`_):
+
+"""
+
+fig04 = drawgraph_channels(coh, roi_names[idx])
+
+"""
+
+.. image:: fig/resting_state_fmri_04.png
+
+This shows us that there is a stronger connectivity between the left putamen and
+the left caudate than between the homologous regions in the other
+hemisphere. In particular, in contrast to the relatively high correlation
+between the right caudate and the left caudate, there is a rather low coherence
+between the time-series in these two regions, in this frequency range.
+
+Note that the connectivity described by coherency (and other measures of
+functional connectivity) could arise because of neural connectivity between the
+two regions, but also due to a common blood supply, or common fluctuations in
+other physiological measures which affect the BOLD signal measured in both
+regions. In order to be able to differentiate these two options, we would have
+to conduct a comparison between two different behavioral states that affect the
+neural activity in the two regions, without affecting these common
+physiological factors, such as common blood supply (for an in-depth discussion
+of these issues, see [Silver2010]_). In this case, we will simply assume that
+the connectivity matrix presented represents the actual neural connectivity
+between these two brain regions.
+
+We notice that there is indeed a stronger coherence between left putamen and the
+left caudate than between the left caudate and the right caudate. Next, we
+might ask whether the moderate coherence between the left putamen and the right
+caudate can be accounted for by the coherence these two time-series share with
+the time-series derived from the left caudate. This kind of question can be
+answered using an analysis of partial coherency. For the time series $x$ and
+$y$, the partial coherence, given a third time-series $r$, is defined as:
+
+.. math::
+
+ Coh_{xy|r} = \frac{|{R_{xy}(\lambda) - R_{xr}(\lambda)
+ R_{ry}(\lambda)}|^2}{(1-|{R_{xr}}|^2)(1-|{R_{ry}}|^2)}
+
+
+In this case, we extract the partial coherence between the three regions,
+excluding common effects of the left caudate. In order to do that, we generate
+the partial-coherence attribute of the :class:`CoherenceAnalyzer` object, while
+indexing on the additional dimension which this object had (the coherence
+between time-series $x$ and time-series $y$, *given* time series $r$):
+
+"""
+
+
+idx3 = np.hstack(16 * [idx_lcau])
+coh = C.coherence_partial[idx1, idx2, idx3].reshape(4, 4, C.frequencies.shape[0])
+coh = np.mean(coh[:, :, freq_idx], -1)
+
+"""
+
+
+Again, we visualize the result, using both the :func:`viz.drawgraph_channels`
+and the :func:`drawmatrix_channels` functions:
+
+
+"""
+
+fig05 = drawgraph_channels(coh, roi_names[idx])
+fig06 = drawmatrix_channels(coh, roi_names[idx], color_anchor=0)
+
+"""
+
+.. image:: fig/resting_state_fmri_05.png
+
+
+.. image:: fig/resting_state_fmri_06.png
+
+
+As can be seen, the resulting partial coherence between left putamen and right
+caudate, given the activity in the left caudate is smaller than the coherence
+between these two areas, suggesting that part of this coherence can be
+explained by their common connection to the left caudate.
+
+XXX Add description of calculation of temporal delay here.
+
+
+We call plt.show() in order to display the figures:
+
+"""
+
+plt.show()
+
+
+"""
+
+.. [Sun2005] F.T. Sun and L.M. Miller and M. D'Esposito(2005). Measuring
+ temporal dynamics of functional networks using phase spectrum of
+ fMRI data. Neuroimage, 28: 227-37.
+
+.. [Silver2010] M.A Silver, AN Landau, TZ Lauritzen, W Prinzmetal, LC
+ Robertson(2010) Isolating human brain functional connectivity associated
+ with a specific cognitive process, in Human Vision and Electronic Imaging
+ XV, edited by B.E. Rogowitz and T.N. Pappas, Proceedings of SPIE, Volume
+ 7527, pp. 75270B-1 to 75270B-9
+"""