Ver Fonte

Add tutorials to introduce users to resource usage

To give prospective users an introduction to the resource usage and
how the resource data can be interfaced with analysis toolkits, some
initial tutorials are added. Currently, there are not helpful to make
any inferences. These will be expanded as analyses progress to include
insightful analyses strategies.
girish há 4 meses atrás
pai
commit
0c4cf84b65

+ 29 - 0
tutorials/README.md

@@ -0,0 +1,29 @@
+# Tutorials
+
+The tutorials are a collection of working code samples demonstrating
+simple analysis and visualization using the CALM-Brain resource. The
+examples lack a narrative explanation but are well-commented with
+appropriate variable names to make the it self-explanatory. In case
+you haven't already, please checkout the [user
+guide](/wiki/Home). These examples are a way to get familiar with
+using [almirah](https://girishmm.github.io/almirah/) and the
+CALM-Brain resource. The sole purpose of these tutorials is to
+acquaint the user with the components of the resource and not provide
+data related insights.
+
+## Introduction
+
+* [Exploring with almirah](intro/exploring-with-almirah.md)
+
+## Working with different modalities
+
+* [Accessing clinical records](modality/accessing-clinical-records.md)
+* [Reading structural MRI with nibabel](modality/reading-mri-with-nibabel.md)
+* [Processing EEG with MNE-Python](modality/processing-eeg-with-mne.md)
+* [Importing Eye tracking data with MNE-Python](modality/importing-eyetrack-with-mne.md)
+* [Processing NIRS data with MNE-Python](modality/processing-nirs-with-mne.md)
+
+## Cross-modal analysis workflow
+
+* [Average EEG signal across various disorders](multimodal/average-eeg-across-disorders.md)
+

BIN
tutorials/images/eeg/ica-plot.png


BIN
tutorials/images/eeg/psd-plot.png


BIN
tutorials/images/eeg/raw-plot.png


BIN
tutorials/images/eeg/spectrum-topo-plot.png


BIN
tutorials/images/eyetrack/eye-position-plot.png


BIN
tutorials/images/mri/reading-with-nibabel.png


BIN
tutorials/images/multimodal/average-eeg-across-disorders.png


BIN
tutorials/images/nirs/deoxyhemoglobin-plot.png


BIN
tutorials/images/nirs/optical-density-plot.png


BIN
tutorials/images/nirs/oxyhemoglobin-plot.png


BIN
tutorials/images/nirs/raw-haemo-plot.png


BIN
tutorials/images/nirs/raw-haemo-psd-after-filtering.png


BIN
tutorials/images/nirs/raw-haemo-psd-before-filtering.png


BIN
tutorials/images/nirs/raw-plot.png


BIN
tutorials/images/nirs/scalp-coupling-index-plot.png


+ 197 - 0
tutorials/intro/exploring-with-almirah.md

@@ -0,0 +1,197 @@
+# Exploring the CALM Brain Resource with almirah
+
+## Load the dataset
+
+
+```python
+from almirah import Dataset
+```
+```python
+Dataset.options()
+```
+
+    [<Dataset name: 'calm-brain'>]
+    
+```python
+ds = Dataset(name="calm-brain")
+ds.components
+```
+
+    [<Layout root: '/path/to/data'>,
+     <Layout root: '/path/to/genome'>,
+     <Database url: 'request:calm-brain@https://www.calm-brain.ncbs.res.in/db-request/'>]
+
+## Quering layouts
+
+```python
+lay = ds.components[0]
+print(lay)
+len(lay.files)
+```
+
+    <Layout root: '/path/to/data'>
+
+    42652
+
+```python
+from almirah import Tag
+
+tags = Tag.options()
+len(tags)
+```
+
+    1589
+    
+```python
+tags_names_possible = {tag.name for tag in tags}
+tags_names_possible
+```
+
+    {'acquisition',
+     'datatype',
+     'direction',
+     'extension',
+     'run',
+     'sample',
+     'session',
+     'space',
+     'subject',
+     'suffix',
+     'task'}
+
+```python
+Tag.options(name="datatype")
+```
+
+    [<Tag datatype: 'anat'>,
+     <Tag datatype: 'dwi'>,
+     <Tag datatype: 'eeg'>,
+     <Tag datatype: 'eyetrack'>,
+     <Tag datatype: 'fmap'>,
+     <Tag datatype: 'func'>,
+     <Tag datatype: 'genome'>,
+     <Tag datatype: 'nirs'>]
+
+```python
+files = lay.query(datatype="eeg")
+len(files)
+```
+
+    15821
+
+```python
+file = files[0]
+file.rel_path
+```
+
+    'sub-D0828/ses-101/eeg/sub-D0828_ses-101_task-auditoryPCP_run-01_events.json'
+
+```python
+file.tags
+```
+
+    {'datatype': 'eeg', 'extension': '.json', 'run': '01', 'session': '101', 'subject': 'D0828', 'suffix': 'events', 'task': 'auditoryPCP'}
+
+## Querying databases
+
+```python
+db = ds.components[2]
+db
+```
+
+    <Database url: 'request:calm-brain@https://www.calm-brain.ncbs.res.in/db-request/'>
+
+```python
+db.connect("username", "password")
+df = db.query(table="presenting_disorders")
+df[["subject", "session", "addiction"]].head()
+```
+
+<div>
+<style scoped>
+    .dataframe tbody tr th:only-of-type {
+        vertical-align: middle;
+    }
+    .dataframe tbody tr th {
+        vertical-align: top;
+    }
+    .dataframe thead th {
+        text-align: right;
+    }
+</style>
+<table border="1" class="dataframe">
+  <thead>
+    <tr style="text-align: right;">
+      <th></th>
+      <th>subject</th>
+      <th>session</th>
+      <th>addiction</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <th>0</th>
+      <td>D0019</td>
+      <td>101</td>
+      <td>0</td>
+    </tr>
+    <tr>
+      <th>1</th>
+      <td>D0019</td>
+      <td>111</td>
+      <td>0</td>
+    </tr>
+    <tr>
+      <th>2</th>
+      <td>D0020</td>
+      <td>101</td>
+      <td>0</td>
+    </tr>
+    <tr>
+      <th>3</th>
+      <td>D0020</td>
+      <td>111</td>
+      <td>&lt;NA&gt;</td>
+    </tr>
+    <tr>
+      <th>4</th>
+      <td>D0021</td>
+      <td>101</td>
+      <td>0</td>
+    </tr>
+  </tbody>
+</table>
+</div>
+
+## Generating summaries
+
+```python
+anat_subject_tags = ds.query(returns="subject", datatype="anat")
+anat_subjects = {subject for t in anat_subject_tags for subject in t}
+len(anat_subjects)
+```
+
+    699
+
+```python
+eyetrack_subject_tags = ds.query(returns="subject", datatype="eyetrack")
+eyetrack_subjects = {subject for t in eyetrack_subject_tags for subject in t}
+len(eyetrack_subjects)
+```
+
+    1075    
+
+```python
+df = db.query(table="subjects")
+len(df)
+```
+
+    2276
+
+```python
+df = db.query(table="modified_kuppuswamy_socioeconomic_scale")
+len(df)
+```
+
+    1444
+

+ 90 - 0
tutorials/modality/accessing-clinical-records.md

@@ -0,0 +1,90 @@
+# Accessing clinical records using almirah
+
+```python
+from almirah import Database
+```
+
+```python
+db = Database(name="calm-brain", host="https://www.calm-brain.ncbs.res.in/db-request/" , backend="request")
+db
+```
+
+    <Database url: 'request:calm-brain@https://www.calm-brain.ncbs.res.in/db-request/'>
+
+```python
+db.connect("username", "password")
+df = db.query(table="presenting_disorders")
+df[["subject", "session", "addiction"]].head()
+```
+
+<div>
+<style scoped>
+    .dataframe tbody tr th:only-of-type {
+        vertical-align: middle;
+    }
+
+    .dataframe tbody tr th {
+        vertical-align: top;
+    }
+
+    .dataframe thead th {
+        text-align: right;
+    }
+</style>
+<table border="1" class="dataframe">
+  <thead>
+    <tr style="text-align: right;">
+      <th></th>
+      <th>subject</th>
+      <th>session</th>
+      <th>addiction</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <th>0</th>
+      <td>D0019</td>
+      <td>101</td>
+      <td>0</td>
+    </tr>
+    <tr>
+      <th>1</th>
+      <td>D0019</td>
+      <td>111</td>
+      <td>0</td>
+    </tr>
+    <tr>
+      <th>2</th>
+      <td>D0020</td>
+      <td>101</td>
+      <td>0</td>
+    </tr>
+    <tr>
+      <th>3</th>
+      <td>D0020</td>
+      <td>111</td>
+      <td>&lt;NA&gt;</td>
+    </tr>
+    <tr>
+      <th>4</th>
+      <td>D0021</td>
+      <td>101</td>
+      <td>0</td>
+    </tr>
+  </tbody>
+</table>
+</div>
+
+```python
+len(df)
+```
+
+    2561
+
+```python
+len(df[df["addiction"] == 1])
+```
+
+    522
+
+

+ 131 - 0
tutorials/modality/importing-eyetrack-with-mne.md

@@ -0,0 +1,131 @@
+# Importing Eye tracking data with MNE-Python
+
+```python
+import mne
+import matplotlib.pyplot as plt
+
+from almirah import Layout
+
+mne.set_log_level(False)
+```
+
+```python
+lay = Layout(root="/path/to/data", specification_name="bids")
+lay
+```
+
+    <Layout root: '/path/to/data'>
+
+```python
+files = lay.query(datatype="eyetrack", extension=".asc")
+len(files)
+```
+
+    3632
+
+```python
+file = lay.query(subject="D0019", datatype="eyetrack", task="FIX", extension=".asc")[0]
+
+print(file.rel_path)
+```
+
+    sub-D0019/ses-111/eyetrack/sub-D0019_ses-111_task-FIX_run-01_eyetrack.asc
+
+```python
+raw = mne.io.read_raw_eyelink(file.path, create_annotations=["blinks"])
+custom_scalings = dict(eyegaze=1e3)
+raw.pick(picks="eyetrack").plot(scalings=custom_scalings)
+plt.close()
+```
+    
+![png](../images/eyetrack/eye-position-plot.png)
+    
+```python
+raw
+```
+
+<details open>
+    <summary><strong>General</strong></summary>
+    <table class="table table-hover table-striped table-sm table-responsive small">
+        <tr>
+            <th>Measurement date</th>
+            <td>January 01, 2009  00:03:33 GMT</td>
+        </tr>
+        <tr>
+            <th>Experimenter</th>
+            <td>Unknown</td>
+        </tr>
+        <tr>
+            <th>Participant</th>
+            <td>Unknown</td>
+        </tr>
+    </table>
+    </summary>
+</details>
+<details open>
+    <summary><strong>Channels</strong></summary>
+    <table class="table table-hover table-striped table-sm table-responsive small">
+	<tr>
+	    <th>Digitized points</th>
+	    <td>Not available</td>
+	</tr>
+	<tr>
+	    <th>Good channels</th>
+	    <td>2 Eye-tracking (Gaze position), 1 Eye-tracking (Pupil size)</td>
+	</tr>
+	<tr>
+	    <th>Bad channels</th>
+	    <td>None</td>
+	</tr>
+	<tr>
+	    <th>EOG channels</th>
+	    <td>Not available</td>
+	</tr>
+	<tr>
+	    <th>ECG channels</th>
+	    <td>Not available</td>
+	</tr>
+    </table>
+    </summary>
+</details>
+<details open>
+    <summary><strong>Data</strong></summary>
+    <table class="table table-hover table-striped table-sm table-responsive small">
+	<tr>
+	    <th>Sampling frequency</th>
+	    <td>1000.00 Hz</td>
+	</tr>
+	<tr>
+	    <th>Highpass</th>
+	    <td>0.00 Hz</td>
+	</tr>
+	<tr>
+	    <th>Lowpass</th>
+	    <td>500.00 Hz</td>
+	</tr>
+	<tr>
+	    <th>Filenames</th>
+	    <td>sub-D0019_ses-111_task-FIX_run-01_eyetrack.asc</td>
+	</tr>
+	<tr>
+	    <th>Duration</th>
+	    <td>00:01:11 (HH:MM:SS)</td>
+	</tr>
+    </table>
+    </summary>
+</details>
+
+```python
+raw.ch_names
+```
+
+    ['xpos_left', 'ypos_left', 'pupil_left']
+
+```python
+raw["xpos_left"]
+```
+
+    (array([[510.2, 510.1, 509.9, ..., 454.4, 454.8, 455.5]]),
+     array([0.0000e+00, 1.0000e-03, 2.0000e-03, ..., 7.0556e+01, 7.0557e+01,
+            7.0558e+01]))
+

+ 169 - 0
tutorials/modality/processing-eeg-with-mne.md

@@ -0,0 +1,169 @@
+# Processing EEG with MNE-Python
+
+```python
+import mne
+import warnings
+import numpy as np
+import matplotlib.pyplot as plt
+
+from almirah import Layout
+
+mne.set_log_level(False)
+warnings.filterwarnings('ignore')
+```
+
+```python
+lay = Layout(root="/path/to/data", specification_name="bids")
+lay
+```
+
+    <Layout root: '/path/to/data'>
+    
+```python
+lay = Layout.get(specification_name='bids')
+```
+
+```python
+files = lay.query(datatype="eeg", extension=".vhdr")
+len(files)
+```
+
+    2223
+
+```python
+vhdr_file = lay.query(subject="D0019", session="101", datatype="eeg", task="rest", extension =".vhdr")[0]
+eeg_file = lay.query(subject="D0019", session="101", datatype="eeg", task="rest", extension=".eeg")[0]
+montage_file = lay.query(subject="D0019", session="101", space="CapTrak", suffix="electrodes")[0]
+
+print(vhdr_file.rel_path)
+```
+
+    sub-D0019/ses-101/eeg/sub-D0019_ses-101_task-rest_run-01_eeg.vhdr
+
+```python
+eeg_file.download()
+```
+
+```python
+montage = mne.channels.read_custom_montage(montage_file.path)
+raw = mne.io.read_raw_brainvision(vhdr_file.path, preload=True)
+raw.set_montage(montage)
+raw.info["bads"] = ["VREF"]
+
+raw.info
+```
+
+<details open>
+    <summary><strong>General</strong></summary>
+    <table class="table table-hover table-striped table-sm table-responsive small">
+        <tr>
+            <th>Measurement date</th>
+            <td>Unknown</td>
+        </tr>
+        <tr>
+            <th>Experimenter</th>
+            <td>Unknown</td>
+        </tr>
+        <tr>
+            <th>Participant</th>
+            <td>Unknown</td>
+        </tr>
+    </table>
+    </summary>
+</details>
+<details open>
+    <summary><strong>Channels</strong></summary>
+    <table class="table table-hover table-striped table-sm table-responsive small">
+	<tr>
+	    <th>Digitized points</th>
+	    <td>132 points</td>
+	</tr>
+	<tr>
+	    <th>Good channels</th>
+	    <td>128 EEG, 5 misc</td>
+	</tr>
+	<tr>
+	    <th>Bad channels</th>
+	    <td>VREF</td>
+	</tr>
+	<tr>
+	    <th>EOG channels</th>
+	    <td>Not available</td>
+	</tr>
+	<tr>
+	    <th>ECG channels</th>
+	    <td>Not available</td>
+	</tr>
+    </table>
+    </summary>
+</details>
+<details open>
+    <summary><strong>Data</strong></summary>
+    <table class="table table-hover table-striped table-sm table-responsive small">
+	<tr>
+	    <th>Sampling frequency</th>
+	    <td>1000.00 Hz</td>
+	</tr>
+	<tr>
+	    <th>Highpass</th>
+	    <td>0.00 Hz</td>
+	</tr>
+	<tr>
+	    <th>Lowpass</th>
+	    <td>500.00 Hz</td>
+	</tr>
+    </table>
+    </summary>
+</details>
+
+# Preprocessing
+
+```python
+# Apply a band-pass filter
+raw.filter(1, 40, fir_design="firwin")
+
+# Plot the raw data
+raw.plot(n_channels=30, duration=30, scalings="auto")
+plt.close()
+```
+
+![png](../images/eeg/raw-plot.png)
+    
+# Artifact removal using ICA
+
+```python
+# Setup ICA
+ica = mne.preprocessing.ICA(n_components=15, random_state=97)
+ica.fit(raw)
+
+# Plot ICA components
+ica.plot_components()
+
+# Select bad components manually (usually by visual inspection)
+ica.exclude = [0, 6, 14]  # Example: components 0 and 1 are eye blinks and heartbeats
+
+# Apply ICA removal
+raw_clean = ica.apply(raw.copy())
+```
+    
+![png](../images/eeg/ica-plot.png)
+    
+# Power Spectral Analysis
+
+```python
+# Compute and plot Power Spectral Density (PSD)
+raw_clean.compute_psd().plot(exclude="bads", amplitude=False)
+plt.show()
+```
+    
+![png](../images/eeg/psd-plot.png)
+    
+
+```python
+raw_clean.compute_psd().plot_topomap(ch_type="eeg", agg_fun=np.median)
+plt.close()
+```
+    
+![png](../images/eeg/spectrum-topo-plot.png)
+    
+

+ 202 - 0
tutorials/modality/processing-nirs-with-mne.md

@@ -0,0 +1,202 @@
+# Reading and processing NIRS data with MNE-Python
+
+```python
+import mne
+import warnings
+import numpy as np
+import matplotlib.pyplot as plt
+from itertools import compress
+
+from almirah import Layout
+
+mne.set_log_level(False)
+warnings.filterwarnings('ignore')
+```
+
+```python
+lay = Layout(root="/path/to/data", specification_name="bids")
+print(lay)
+```
+
+    <Layout root: '/path/to/data'>
+
+```python
+files = lay.query(datatype="nirs", extension=".snirf")
+len(files)
+```
+
+    1315
+
+```python
+file = lay.query(subject="D0019", task="rest", datatype="nirs", extension=".snirf")[0]
+print(file.rel_path)
+```
+
+    sub-D0019/ses-111/nirs/sub-D0019_ses-111_task-rest_run-01_nirs.snirf
+
+```python
+file.download()
+```
+
+```python
+raw = mne.io.read_raw_snirf(file.path)
+raw.load_data()
+```
+
+<details open>
+    <summary><strong>General</strong></summary>
+    <table class="table table-hover table-striped table-sm table-responsive small">
+        <tr>
+            <th>Measurement date</th>
+            <td>November 12, 1917  00:00:00 GMT</td>
+
+        </tr>
+        <tr>
+            <th>Experimenter</th>
+            <td>Unknown</td>
+
+        </tr>
+        <tr>
+            <th>Participant</th>
+            <td>mne_anonymize</td>
+        </tr>
+    </table>
+    </details>
+    <details open>
+        <summary><strong>Channels</strong></summary>
+        <table class="table table-hover table-striped table-sm table-responsive small">
+            <tr>
+                <th>Digitized points</th>
+                <td>21 points</td>
+            </tr>
+            <tr>
+                <th>Good channels</th>
+                <td>36 fNIRS (CW amplitude)</td>
+            </tr>
+            <tr>
+                <th>Bad channels</th>
+                <td>None</td>
+            </tr>
+            <tr>
+                <th>EOG channels</th>
+                <td>Not available</td>
+            </tr>
+            <tr>
+                <th>ECG channels</th>
+                <td>Not available</td>
+            </tr>
+        </table>
+        </details>
+        <details open>
+            <summary><strong>Data</strong></summary>
+            <table class="table table-hover table-striped table-sm table-responsive small">
+                <tr>
+                    <th>Sampling frequency</th>
+                    <td>15.62 Hz</td>
+                </tr>
+                <tr>
+                    <th>Highpass</th>
+                    <td>0.00 Hz</td>
+                </tr>
+                <tr>
+                    <th>Lowpass</th>
+                    <td>7.81 Hz</td>
+                </tr>
+                <tr>
+                    <th>Filenames</th>
+                    <td>sub-D0019_ses-111_task-rest_run-01_nirs.snirf</td>
+                </tr>
+                <tr>
+                    <th>Duration</th>
+                    <td>00:10:03 (HH:MM:SS)</td>
+                </tr>
+            </table>
+            </details>
+	    
+```python
+print(raw.info)
+```
+
+    <Info | 9 non-empty values
+     bads: []
+     ch_names: S1_D1 760, S1_D1 850, S1_D2 760, S1_D2 850, S2_D2 760, S2_D2 ...
+     chs: 36 fNIRS (CW amplitude)
+     custom_ref_applied: False
+     dig: 21 items (3 Cardinal, 18 EEG)
+     highpass: 0.0 Hz
+     lowpass: 7.8 Hz
+     meas_date: 1917-11-12 00:00:00 UTC
+     nchan: 36
+     projs: []
+     sfreq: 15.6 Hz
+     subject_info: 4 items (dict)
+    >
+
+```python
+picks = mne.pick_types(raw.info, meg=False, fnirs=True)
+dists = mne.preprocessing.nirs.source_detector_distances(raw.info, picks=picks)
+raw.pick(picks[dists > 0.01])
+raw.plot(n_channels=len(raw.ch_names), duration=500, show_scrollbars=False)
+plt.show()
+```
+    
+![png](../images/nirs/raw-plot.png)
+    
+```python
+raw_od = mne.preprocessing.nirs.optical_density(raw)
+raw_od.plot(n_channels=len(raw_od.ch_names), duration=500, show_scrollbars=False)
+plt.show()
+```
+
+![png](../images/nirs/optical-density-plot.png)
+    
+```python
+sci = mne.preprocessing.nirs.scalp_coupling_index(raw_od)
+fig, ax = plt.subplots()
+ax.hist(sci)
+ax.set(xlabel="Scalp Coupling Index", ylabel="Count", xlim=[0, 1])
+plt.show()
+```
+
+![png](../images/nirs/scalp-coupling-index-plot.png)
+    
+```python
+raw_od.info["bads"] = list(compress(raw_od.ch_names, sci < 0.5))
+```
+
+```python
+raw_haemo = mne.preprocessing.nirs.beer_lambert_law(raw_od, ppf=0.1)
+raw_haemo.plot(n_channels=len(raw_haemo.ch_names), duration=500, show_scrollbars=False)
+plt.show()
+```
+    
+![png](../images/nirs/raw-haemo-plot.png)
+    
+```python
+fig = raw_haemo.compute_psd().plot(average=True, amplitude=False)
+fig.suptitle("Before filtering", weight="bold", size="x-large")
+raw_haemo = raw_haemo.filter(0.05, 0.7, h_trans_bandwidth=0.2, l_trans_bandwidth=0.02)
+fig = raw_haemo.compute_psd().plot(average=True)
+fig.suptitle("After filtering", weight="bold", size="x-large")
+plt.show()
+```
+
+![png](../images/nirs/raw-haemo-psd-before-filtering.png)
+        
+![png](../images/nirs/raw-haemo-psd-after-filtering.png)
+    
+```python
+epochs = mne.make_fixed_length_epochs(raw_haemo, duration=30, preload=False)
+```
+
+```python
+epochs.plot_image(combine="mean", vmin=-30, vmax=30,
+                             ts_args=dict(ylim=dict(hbo=[-15, 15],
+                                                    hbr=[-15, 15])))
+plt.show()
+```
+    
+![png](../images/nirs/deoxyhemoglobin-plot.png)
+        
+![png](../images/nirs/oxyhemoglobin-plot.png)
+    

+ 114 - 0
tutorials/modality/reading-mri-with-nibabel.md

@@ -0,0 +1,114 @@
+# Reading Structural MRI with nibabel
+
+```python
+import nibabel as nib
+import nilearn as nil
+
+from almirah import Layout
+```
+
+```python
+lay = Layout(root="/path/to/data", specification_name="bids")
+lay
+```
+
+    <Layout root: '/path/to/data'>
+
+```python
+files = lay.query(datatype="anat", extension=".nii.gz")
+```
+
+```python
+file = lay.query(subject="D0020", datatype="anat", suffix="T1w", extension=".nii.gz")[0]
+print(file.rel_path)
+```
+
+    sub-D0020/ses-101/anat/sub-D0020_ses-101_T1w.nii.gz
+
+```python
+file.download()
+```
+
+    get(ok): sub-D0020/ses-101/anat/sub-D0020_ses-101_T1w.nii.gz (file) [from origin...]
+
+```python
+raw = nib.load(file.path)
+type(raw)
+```
+
+    nibabel.nifti1.Nifti1Image
+
+```python
+print(raw.header)
+```
+
+    <class 'nibabel.nifti1.Nifti1Header'> object, endian='<'
+    sizeof_hdr      : 348
+    data_type       : b''
+    db_name         : b''
+    extents         : 0
+    session_error   : 0
+    regular         : b'r'
+    dim_info        : 54
+    dim             : [  3 192 256 256   1   1   1   1]
+    intent_p1       : 0.0
+    intent_p2       : 0.0
+    intent_p3       : 0.0
+    intent_code     : none
+    datatype        : int16
+    bitpix          : 16
+    slice_start     : 0
+    pixdim          : [1.        1.        1.        1.        0.0064721 0.        0.
+     0.       ]
+    vox_offset      : 0.0
+    scl_slope       : nan
+    scl_inter       : nan
+    slice_end       : 0
+    slice_code      : unknown
+    xyzt_units      : 10
+    cal_max         : 0.0
+    cal_min         : 0.0
+    slice_duration  : 0.0
+    toffset         : 0.0
+    glmax           : 0
+    glmin           : 0
+    descrip         : b'TE=2.9;Time=103137.087'
+    aux_file        : b''
+    qform_code      : scanner
+    sform_code      : scanner
+    quatern_b       : 0.0
+    quatern_c       : -0.018800024
+    quatern_d       : 0.0
+    qoffset_x       : -95.44943
+    qoffset_y       : -132.33757
+    qoffset_z       : -134.27122
+    srow_x          : [ 9.9929786e-01  0.0000000e+00 -3.7593402e-02 -9.5449432e+01]
+    srow_y          : [   0.         1.         0.      -132.33757]
+    srow_z          : [ 3.7593581e-02  0.0000000e+00  9.9929309e-01 -1.3427122e+02]
+    intent_name     : b''
+    magic           : b'n+1'
+
+```python
+raw_data = raw.get_fdata()
+type(raw_data)
+```
+
+    numpy.ndarray
+
+```python
+raw_data.shape
+```
+
+    (192, 256, 256)
+
+```python
+from nilearn import plotting
+
+plotting.plot_img(raw)
+```
+
+    <nilearn.plotting.displays._slicers.OrthoSlicer at 0x310f73b30>
+    
+![png](../images/mri/reading-with-nibabel.png)
+    
+

+ 245 - 0
tutorials/multimodal/average-eeg-across-disorders.md

@@ -0,0 +1,245 @@
+# Average EEG signal across various disorders
+
+```python
+import mne
+import pandas as pd
+import seaborn as sns
+
+from almirah import Dataset
+
+mne.set_log_level(False)
+```
+
+```python
+ds = Dataset(name="calm-brain")
+eeg_header_files = ds.query(datatype="eeg", task="rest", extension=".vhdr")
+eeg_data_files = ds.query(datatype="eeg", task="rest", extension=".eeg")
+len(eeg_data_files)
+```
+
+    1120
+
+```python
+for file in eeg_data_files:
+    file.download()
+```
+
+```python
+db = ds.components[2]
+db.connect("username", "password")
+df = ds.query(table="presenting_disorders")
+df[["subject", "session", "addiction"]].head()
+```
+
+<div>
+<style scoped>
+    .dataframe tbody tr th:only-of-type {
+        vertical-align: middle;
+    }
+
+    .dataframe tbody tr th {
+        vertical-align: top;
+    }
+
+    .dataframe thead th {
+        text-align: right;
+    }
+</style>
+<table border="1" class="dataframe">
+  <thead>
+    <tr style="text-align: right;">
+      <th></th>
+      <th>subject</th>
+      <th>session</th>
+      <th>addiction</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <th>0</th>
+      <td>D0019</td>
+      <td>101</td>
+      <td>0</td>
+    </tr>
+    <tr>
+      <th>1</th>
+      <td>D0019</td>
+      <td>111</td>
+      <td>0</td>
+    </tr>
+    <tr>
+      <th>2</th>
+      <td>D0020</td>
+      <td>101</td>
+      <td>0</td>
+    </tr>
+    <tr>
+      <th>3</th>
+      <td>D0020</td>
+      <td>111</td>
+      <td>&lt;NA&gt;</td>
+    </tr>
+    <tr>
+      <th>4</th>
+      <td>D0021</td>
+      <td>101</td>
+      <td>0</td>
+    </tr>
+  </tbody>
+</table>
+</div>
+
+```python
+def get_eeg_mean(file):
+    raw = mne.io.read_raw_brainvision(file.path)
+    return raw.get_data().mean()
+
+def get_disorders(file):
+    disorders = []
+    subject, session = file.tags["subject"], file.tags["session"]
+    filtered_df = df[(df["subject"] == subject) & (df["session"] == session)]
+
+    if filtered_df.empty:
+        print(subject, session)
+        return None
+    
+    for column in ["addiction", "bipolar", "dementia", "ocd", "schizophrenia"]:
+        presence = filtered_df.iloc[0][column]
+        if not pd.isna(presence) and presence:
+            disorders.append(column)
+    
+    return disorders if disorders else ["healthy"]
+
+def file_func(file):
+    mean_eeg, disorders = get_eeg_mean(file), get_disorders(file)
+
+    if not disorders:
+        return pd.DataFrame()
+        
+    mean_df = pd.DataFrame({"mean": [mean_eeg] * len(disorders), "disorder": disorders})
+    return mean_df.dropna()
+```
+
+```python
+mean_dfs = list(map(file_func, eeg_header_files))
+mean_dfs = [df for df in mean_dfs if not df.empty]
+mean_df = pd.concat(mean_dfs, sort=False)
+mean_df.head()
+```
+
+<div>
+<style scoped>
+    .dataframe tbody tr th:only-of-type {
+        vertical-align: middle;
+    }
+
+    .dataframe tbody tr th {
+        vertical-align: top;
+    }
+
+    .dataframe thead th {
+        text-align: right;
+    }
+</style>
+<table border="1" class="dataframe">
+  <thead>
+    <tr style="text-align: right;">
+      <th></th>
+      <th>mean</th>
+      <th>disorder</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <th>0</th>
+      <td>-0.008766</td>
+      <td>healthy</td>
+    </tr>
+    <tr>
+      <th>1</th>
+      <td>0.000457</td>
+      <td>addiction</td>
+    </tr>
+    <tr>
+      <th>2</th>
+      <td>-0.006335</td>
+      <td>healthy</td>
+    </tr>
+    <tr>
+      <th>3</th>
+      <td>-0.002764</td>
+      <td>healthy</td>
+    </tr>
+    <tr>
+      <th>4</th>
+      <td>-0.008269</td>
+      <td>ocd</td>
+    </tr>
+  </tbody>
+</table>
+</div>
+
+```python
+mean_df.groupby("disorder").mean()
+```
+
+<div>
+<style scoped>
+    .dataframe tbody tr th:only-of-type {
+        vertical-align: middle;
+    }
+
+    .dataframe tbody tr th {
+        vertical-align: top;
+    }
+
+    .dataframe thead th {
+        text-align: right;
+    }
+</style>
+<table border="1" class="dataframe">
+  <thead>
+    <tr style="text-align: right;">
+      <th></th>
+      <th>mean</th>
+    </tr>
+    <tr>
+      <th>disorder</th>
+      <th></th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <th>addiction</th>
+      <td>0.003414</td>
+    </tr>
+    <tr>
+      <th>bipolar</th>
+      <td>0.001613</td>
+    </tr>
+    <tr>
+      <th>dementia</th>
+      <td>0.010485</td>
+    </tr>
+    <tr>
+      <th>healthy</th>
+      <td>0.002449</td>
+    </tr>
+    <tr>
+      <th>ocd</th>
+      <td>-0.000875</td>
+    </tr>
+    <tr>
+      <th>schizophrenia</th>
+      <td>0.005444</td>
+    </tr>
+  </tbody>
+</table>
+</div>
+
+```python
+ax = sns.violinplot(data=mean_df, x="mean", hue="disorder")
+sns.move_legend(ax, "upper left", bbox_to_anchor=(1, 1))
+```
+    
+![png](../images/multimodal/average-eeg-across-disorders.png)