Building a brain object

Brain objects are supereeg’s fundamental data structure for a single subject’s ECoG data. To create one at minimum you’ll need a matrix of neural recordings (time samples by electrodes), electrode locations, and a sample rate. Additionally, you can include information about separate recording sessions and store custom meta data. In this tutorial, we’ll build a brain object from scratch and get familiar with some of the methods.

Load in the required libraries

import supereeg as se
import numpy as np
import warnings
warnings.simplefilter("ignore")
%matplotlib inline

Simulate some data

First, we’ll use supereeg’s built in simulation functions to simulate some data and electrodes. By default, the simulate_data function will return a 1000 samples by 10 electrodes matrix, but you can specify the number of time samples with n_samples and the number of electrodes with n_elecs. If you want further information on simulating data, check out the simulate tutorial!

# simulate some data
bo_data = se.simulate_bo(n_samples=1000, sessions=2, n_elecs=10)

# plot it
bo_data.plot_data()

# get just data
data = bo_data.get_data()
../_images/brain_objects_4_0.png

We’ll also simulate some electrode locations

locs = se.simulate_locations()
print(locs)
    x   y   z
0 -31   1 -18
1 -26  34 -24
2 -11   9 -12
3  -9  -8 -28
4   6  38 -41
5  10 -42  -3
6  29  47  36
7  32   3 -42
8  41 -18 -16
9  47 -34  32

Creating a brain object

To construct a new brain objects, simply pass the data and locations to the Brain class like this:

bo = se.Brain(data=data, locs=locs, sample_rate=100)

To view a summary of the contents of the brain object, you can call the info function:

bo.info()
Number of electrodes: 10
Recording time in seconds: [10.]
Sample Rate in Hz: [100]
Number of sessions: 1
Date created: Fri Jul 27 16:19:15 2018
Meta data: {}

Optionally, you can pass a sessions parameter, which is can be a numpy array or list the length of your data with a unique identifier for each session. For example:

sessions = np.array([1]*(data.shape[0]/2)+[2]*(data.shape[0]/2))
bo = se.Brain(data=data, locs=locs, sample_rate=1000, sessions=sessions)
bo.info()
Number of electrodes: 10
Recording time in seconds: [0.5 0.5]
Sample Rate in Hz: [1000, 1000]
Number of sessions: 2
Date created: Fri Jul 27 16:19:15 2018
Meta data: {}

You can also add custom meta data to the brain object to help keep track of its contents. meta is a dictionary comprised of whatever you want:

meta = {
    'subjectID' : '123',
    'Investigator' : 'Andy',
    'Hospital' : 'DHMC'
}
bo = se.Brain(data=data, locs=locs, sample_rate=1000, sessions=sessions, meta=meta)
bo.info()
Number of electrodes: 10
Recording time in seconds: [0.5 0.5]
Sample Rate in Hz: [1000, 1000]
Number of sessions: 2
Date created: Fri Jul 27 16:19:15 2018
Meta data: {'Hospital': 'DHMC', 'subjectID': '123', 'Investigator': 'Andy'}

Initialize brain objects

Brain objects can be initialized by passing a any of the following to the Brain class instance initialization function: - A path to a saved Brain object (ending in .bo) - An existing Brain object (this creates a copy of the object) - A path to or instance of any other supported toolbox type (Model objects or .mo files, or Nifti objects or .nii files)

In addition, Brain objects may be created via load by specifying return_type='bo'.

For example:

nii_bo = se.Brain('example_nifti')

Or:

nii_bo = se.load('example_nifti', return_type='bo')

Another feature, which can be particularly useful when working with large files, is loading only a subfield by specifiying field. For example, if you only want to load locations:

bo_locs = se.load('example_data', field='locs')

The structure of a brain object

Inside the brain object, the ECoG data are stored in a Pandas DataFrame that can be accessed with the get_data function:

bo.get_data().head()
0 1 2 3 4 5 6 7 8 9
0 0.209741 -0.512392 -0.720984 -0.754342 -1.726174 -0.443516 -1.782753 -0.713155 -0.284680 -0.068902
1 -1.055684 -0.810194 -0.673013 0.483223 0.008181 -0.190715 2.239795 -0.028814 -0.301845 -1.022902
2 -0.346641 -0.239482 -0.370357 0.246060 -0.578913 -0.565974 -0.946681 -0.358038 -0.616623 -0.124095
3 -0.610393 0.130029 -0.152837 -1.142612 -1.017601 -0.409920 -1.533653 -0.806856 -0.701596 -0.249572
4 0.290803 0.699244 0.240235 1.122073 0.716814 0.532778 0.992918 1.079572 0.397130 -0.019875

Similarly, the electrode locations are stored as a Pandas DataFrame, and can be retrieved using the get_locs method:

bo.get_locs().head()
x y z
0 -31 1 -18
1 -26 34 -24
2 -11 9 -12
3 -9 -8 -28
4 6 38 -41

Brain objects are iterable, so you index a brain object like this:

#return first time sample
bo[0]
#return first 3 time samples
bo[:3]
#return first electrode
bo[:, 0]
#returns first 3 timesamples/elecs
bo_i = bo[:3, :3]
bo_i.get_data()
0 1 2
0 0.209741 -0.512392 -0.720984
1 -1.055684 -0.810194 -0.673013
2 -0.346641 -0.239482 -0.370357

You can also pass a list of indices for either times (sample numbers) or locs to the get_slice method and return a subset of the brain object.

bo_s = bo.get_slice(sample_inds=[0,1,2], loc_inds=[0,1,2])
bo_s.get_data()
0 1 2
0 0.209741 -0.512392 -0.720984
1 -1.055684 -0.810194 -0.673013
2 -0.346641 -0.239482 -0.370357

You can resample your data by specifying a new sample rate

bo.resample(64)
bo.info()
Number of electrodes: 10
Recording time in seconds: [0.5 0.5]
Sample Rate in Hz: [64, 64]
Number of sessions: 2
Date created: Fri Jul 27 16:19:15 2018
Meta data: {'Hospital': 'DHMC', 'subjectID': '123', 'Investigator': 'Andy'}

You can also plot both the data and the electrode locations:

bo.plot_data()
../_images/brain_objects_32_0.png
bo.plot_locs()
../_images/brain_objects_33_0.png

The other pieces of the brain object are listed below:

# array of session identifiers for each timepoint
sessions = bo.sessions

# number of sessions
n_sessions = bo.n_sessions

# sample rate
sample_rate = bo.sample_rate

# number of electrodes
n_elecs = bo.n_elecs

# length of each recording session in seconds
n_seconds = bo.dur

# the date and time that the bo was created
date_created = bo.date_created

# kurtosis of each electrode
kurtosis = bo.kurtosis

# meta data
meta = bo.meta

# label delinieating observed and reconstructed locations
label = bo.label

Brain object methods

There are a few other useful methods on a brain object

bo.info()

This method will give you a summary of the brain object:

bo.info()
Number of electrodes: 10
Recording time in seconds: [0.5 0.5]
Sample Rate in Hz: [64, 64]
Number of sessions: 2
Date created: Fri Jul 27 16:19:15 2018
Meta data: {'Hospital': 'DHMC', 'subjectID': '123', 'Investigator': 'Andy'}

bo.apply_filter()

This method will return a filtered copy of the brain object.

bo_f = bo.apply_filter()

bo.get_data()

data_array = bo.get_data()

bo.get_zscore_data()

This method will return a numpy array of the zscored data:

zdata_array = bo.get_zscore_data()

bo.get_locs()

This method will return a numpy array of the electrode locations:

locs = bo.get_locs()

bo.get_slice()

This method allows you to slice out time and locations from the brain object, and returns a brain object. This can occur in place if you set the flag inplace=True.

bo_slice = bo.get_slice(sample_inds=None, loc_inds=None, inplace=False)

bo.resample()

This method allows you resample a brain object in place.

bo.resample(resample_rate=None)
<supereeg.brain.Brain at 0x116c298d0>

bo.plot_data()

This method normalizes and plots data from brain object:

bo.plot_data()
../_images/brain_objects_52_0.png

bo.plot_locs()

This method plots electrode locations from brain object:

bo_f = se.load('example_filter')
bo_f.plot_locs()
../_images/brain_objects_55_0.png

bo.to_nii()

This method converts the brain object into supereeg’s nifti class (a subclass of the nibabel nifti class). If filepath is specified, the nifti file will be saved. You can also specify a nifti template with the template argument. If no template is specified, it will use the gray matter masked MNI 152 brain downsampled to 6mm.

# convert to nifti
nii = bo.to_nii(template='gray', vox_size=6)

# plot first timepoint
nii.plot_glass_brain()

# save the file
# nii = bo.to_nii(filepath='/path/to/file/brain')

# specify a template and resolution
# nii = bo.to_nii(template='/path/to/nifti/file.nii', vox_size=20)
../_images/brain_objects_57_0.png

bo.save(fname='something')

This method will save the brain object to the specified file location. The data will be saved as a ‘bo’ file, which is a dictionary containing the elements of a brain object saved in the hd5 format using deepdish.

#bo.save(fname='brain_object')