Skip to content

Tutorial 2: Analyzing your first electrophysiology experiment with NDI

This is a beginner's tutorial for NDI. This tutorial is designed for users who have some basic familiarity with coding or Matlab. If you're a Matlab and coding newbie, please use Tutorial 3.1, the detailed version.

2.1: Reading an example dataset

We will start with learning to read an example dataset into NDI. We assume you have already installed NDI and taken the introductory tutorial of the NDI model. These data are available in a compressed folder here. You can put the folder anywhere, but we will assume that you put them in your MATLAB/Documents/NDI folder, where MATLAB is your normal user path in Matlab (usually /Users/username/Documents/MATLAB on a Mac). Normally, we'd use some helper functions to open our data to make this process even easier, but this tutorial takes the user through the full manual process for training purposes.

2.1.1 Introduction to the experiment

These data are single neuron recordings made with electrodes in the anesthetized tree shrew visual as a part of Van Hooser et al. 2013. This example data is a very small subset of the original data for the purposes of illustration.

Dataset setup

In this experiment, a single unit recording electrode was inserted into either lateral geniculate nucleus or V1. The electrode was connected to an amplifier, and the output signal was recorded by a Cambridge Electronic Design (CED) micro1401 digital acquisition board. A CED Spike2 software script was used that stored 1 recording epoch per folder. In software, the voltage from the electrode was assigned to channel 11. A visual stimulus monitor was set up in front of the animal, and visual stimuli were generated by custom software running on a separate computer. A record of the stimulus parameters was stored in the epoch folder in a file called stims.mat, and stimulus timing information was delivered to the digital inputs of the micro1401 in the form of a stimulus onset trigger and an 8-bit stimulus code signal that were generated by the stimulus computer.

2.1.2 Introduction to the data

The data are in a folder called ts_exper1. It contains recordings of two neurons, an LGN neuron and a V1 neuron, to presentation of sinusoidal gratings that either vary in orientation or in spatial frequency. The data, as provided by the host lab, are organized into subfolders, named t00001, t00002, t00003, and t00004. The first two folders are recordings of the LGN neuron, and the second two folders are recordings of the V1 neuron. Each t0000N folder has 3 files:

  • probemap.txt - a file that we made for NDI that describe what probes are recorded in this folder
  • spike2data.smr - a CED Spike2 file that has the raw electrophysiology data and the stimulus timing information
  • stims.mat - A Matlab file that has detailed parameters of the stimuli that were run

First, let's list these files using the ls command (that's the letter l followed by the letter s, short for list) and take a quick look at what is in them. Set up the "prefix" path to your data as necessary. (Here, we assume you put the ts_exper1 folder into your Matlab userpath folder but you can put it anywhere.

Code block 2.1.2.1. Type this in to Matlab:

prefix = [userpath filesep 'Documents' filesep 'NDI']; % or '/Users/yourusername/Desktop/' if you put it on the desktop perhaps
ls([prefix filesep 'ts_exper1' filesep 't*']); % list all the files in the t0000N folders

We wrote a short function so that you can see the raw voltage recording and the stimulus timing information. Each stimulus appears as a number and its duration is indicated by the black bar. You can pan with the mouse to scroll through the recording.

Code block 2.1.2.2. Type this in to Matlab:

my_smr_file = fullfile(prefix,'ts_exper1','t00001','spike2data.smr')
ndi.example.tutorial.plottreeshrewdata(my_smr_file);

You should see a Matlab window that looks like this, and when you hover your mouse over the image, it should turn into a "hand" that allows you to drag and pan around.

Tree shrew voltage trace with stimulus IDs

2.1.3 Specifying the metadata that NDI needs to read the experiment

In this example, we have already prepared the metadata files that are necessary for NDI to read the data. Let's look at them in turn.

First, we need to tell NDI what probes we have in our experiment. A probe is anything that measures or stimulates; one end of a probe is connected to a subject, and the other end of a probe is connected to a data acquisition device. We tell NDI how the probe is connected by creating an ndi.epoch.epochprobemap. Usually, we do this with a little code that instructs NDI how to read this information directly from the laboratory's own file information, but in this example, we will use the generic ndi.epoch.epochprobemap_daqsystem object, which reads in a simple tab-delimited text file.

Let's print the probemap.txt file for directory t00001:

Code block 2.1.3.1. Type this in to Matlab:

type (fullfile(prefix,'ts_exper1','t00001','probemap.txt'))

You will see a tab-delimited text table that looks like the following. In the Matlab command window, the tabs may not line up, so don't be concerned if it doesn't look like a nice formatted table.

name reference type devicestring subjectstring
ctx 1 n-trode ced_daqsystem:ai11 treeshrew_12345@mylab.org
vis_stim 1 stimulator vis_daqsystem:mk30;text30;md1 treeshrew_12345@mylab.org

This text file has all of the information to specify the probe and its connections. It has a name for the probe that is meaningful to the user, and the reference specifies an identifier that groups the recordings of the probe. Typically, when the probe is in one physical position, the reference number is kept the same, but when the probe is moved (such as when an electrode is advanced), the reference number is incremented. This tells NDI to try to combine information from recordings that have the same name and reference. type tells NDI what type of probe it is. There are a variety of pre-determined types, and this type instructs NDI (via this file) which probe object type to create in software.

Second, we need to specify the stimulus parameters metadata. Typically, one would create a metadata reader for ones own lab so that the native data can be read directly, but here we have made another tab-delimited text file with the parameters. You can read this for yourself by typing the following:

Code block 2.1.3.2. Type this in to Matlab:

type (fullfile(prefix,'ts_exper1','t00001','stims.tsv'))

2.1.4 Gaining access to the data in NDI: ndi.session and ndi.daq.system objects

Now all that remains is to open the data directory as an ndi.session object, and make ndi.daq.system objects to read your data. We will use an ndi.session.dir object, which allows us to read information from a directory (folder) on disk.

We will create a new ndi.system object by calling the constructor with the reference name we wish to give to the session and the pathname to our data:

Code block 2.1.4.1. Type this in to Matlab:

S = ndi.session.dir('ts_exper1',[prefix filesep 'ts_exper1'])

Now you have made a new NDI session. The session is brand new and doesn't know about any of the devices we used in our experiment. So, if we try to see if there are any probes, we will see that it doesn't know of any:

Code block 2.1.4.2. Type this in to Matlab:

S.getprobes()

Unless you ran this demo before, you won't see any probes here (it will return an empty cell array).

We need to make new ndi.daq.system objects for our data acquisition system and our stimulator. Our devices are multifunction data acquisition systems, so we use the ndi.daq.system.mfdaq subtype.

An ndi.daq.system object consists of three components: an ndi.file.navigator object whose job it is to find the files or streams associated with each epoch of data, an ndi.daq.reader object whose job it is to read the raw data from the files, and an ndi.daq.metadatareader (optionally) whose job it is to read any metadata associated with the epoch (such as stimulus parameter information).

First, we will build an ndi.daq.system.mfdaq object that we will call 'ced_daqsystem' to read the electrode data from our CED SMR files.

Code block 2.1.4.3. Type this in to Matlab:

ced_filenav = ndi.file.navigator(S, {'.*\.smr\>', 'probemap.txt'}, ...
    'ndi.epoch.epochprobemap_daqsystem','probemap.txt');
ced_rdr = ndi.daq.reader.mfdaq.cedspike2();
ced_system = ndi.daq.system.mfdaq('ced_daqsystem', ced_filenav, ced_rdr);
 % if you haven't already added the daq system, you can add it here:
S.daqsystem_add(ced_system);

Note: If you ran the tutorial before, you may have added ced_system to your session S already. That's fine, you'll get an error if you try to do it again. If you want to remove all your daq systems, you can call ndi.session.daqsystem_clear() by typing S.daqsystem_clear(), and then you can add them again.

Let's look at the epochs that ced_system can find, in order to understand how it searches for epochs:

Code block 2.1.4.4 Type this into Matlab

 % let's look at the epochs the daq.system can find
et = ced_system.epochtable() % should see a 4 element answer
f = ced_system.filenavigator.getepochfiles(1) % you should see the files from epoch 1, t00001

Second, we will build an ndi.daq.system.mfdaq for our visual stimulus system.

Code block 2.1.4.5. Type this in to Matlab:

vis_filenav = ndi.file.navigator(S, {'.*\.smr\>', 'probemap.txt', 'stims.tsv'},...
     'ndi.epoch.epochprobemap_daqsystem','probemap.txt');
vis_rdr = ndi.daq.reader.mfdaq.cedspike2();
vis_mdrdr = ndi.daq.metadatareader('stims.tsv');
vis_system = ndi.daq.system.mfdaq('vis_daqsystem', vis_filenav, vis_rdr, {vis_mdrdr});
 % if you haven't already added the daq system, you can add it here:
S.daqsystem_add(vis_system);

Last, we will tell NDI how these devices are synchronized with each other. These two daq systems have a file in common (spike2data.smr), which means that they have a common time base, but NDI doesn't yet know that the two daq systems can be synchronized. Here we add a "syncrule" that tells NDI that any daq systems that share at least 2 files per epoch also share a common time clock for that epoch.

Code block 2.1.4.6. Type this in to Matlab:

nsf = ndi.time.syncrule.filematch(struct('number_fullpath_matches',2));
S.syncgraph_addrule(nsf);

2.1.5 Opening the data in NDI: accessing probes via from ndi.daq.system.mfdaq

Now we can use NDI to see the probes that these daq systems can find and to access the data from those probes. Let's look at the electrode probe data first.

Code block 2.1.5.1. Type this in to Matlab:

p = S.getprobes() % get all of the probes that are in the ndi.session S
for i=1:numel(p), p{i}, end; % display the probe information for each probe

% look at the number of epochs recorded for probe 1
p_ctx1_list = S.getprobes('name','ctx','reference',1) % returns a cell array of matches
p_ctx1 = p_ctx1_list{1}; % take the first one, should be the only one
et = p_ctx1.epochtable()
for i=1:numel(et), et(i), end; % display the epoch table entries
epoch_to_read = 1;

You can see that probe 1 has a name of ctx, a reference of 1, and it is of type n-trode, or an n-channel electrode. It has a software object type of ndi.probe.timeseries.mfdaq, which simply means it is associated with multifunction DAQ systems and returns timeseries observations. Now let's read data from our probe p_ctx1 and plot the data:

Code block 2.1.5.2. Type this into Matlab

[data,t,timeref_p_ctx1]=p_ctx1.readtimeseries(epoch_to_read,-Inf,Inf); % read all data from epoch 1
figure(100);
plot(t,data);
xlabel('Time(s)');
ylabel('Voltage (V)');
set(gca,'xlim',[t(1) t(end)]);
box off;

This code calls an important function for reading data from probes: ndi.timeseries.readtimeseries:

This code is documentation; do not type into Matlab

[D, T, TIMEREF] = ndi.timeseries.readtimeseries(EPOCH_OR_TIMEREF, T0, T1)
% Reads data and timestamps from an ndi.timeseries.readtimeseries object with respect
% to a specific epoch or an ndi.time.timereference object, from time T0 to T1.

When analyzing data or writing apps to analyze data, ndi.timeseries.readtimeseries is one of the most commonly called functions.

Now let's also look at our stimulator probe vis_stim. First, let's examine the epochs that are known to vis_stim:

Code block 2.1.5.3. Type this in to Matlab:

p_visstim_list = S.getprobes('name','vis_stim','reference',1) % returns a cell array of matches
p_visstim = p_visstim_list{1}; % take the first one, should be the only one
et = p_visstim.epochtable()
for i=1:numel(et), et(i), end; % display the epoch table entries

Now let's read the data from our stimulator. To do this, we are going to ask NDI to read the stimulus timing information in the time units of our electrode probe p_ctx. You'll notice that when we read data from p_ctx1, readtimeseries returned an ndi.time.timereference object timeref_p_ctx1. Let's examine this quickly:

Code block 2.1.5.4. Type this in to Matlab:

timeref_p_ctx1

You'll see a structure with the following fields:

timeref_p_ctx1 timereference with properties
referent [1x1 ndi.probe.timeseries.mfdaq]
clocktype [1x1 ndi.time.clocktype]
epoch 1
time 0
session_ID '412687ba08e28694_c0d9c07d0b8726cf'

In NDI, one can refer to time with respect to a variety of different clocks, which is helpful because daq systems typically do not have access to a global clock. We can now ask for the time of the stimulus presentations with respect to our electrode's clock, and add the onset times to the graph.

Code block 2.1.5.5. Type this in to Matlab:

[data,t,timeref_stim]=p_visstim.readtimeseries(timeref_p_ctx1,-Inf,Inf); % read all data from epoch 1 of p_ctx1 !
figure(100);
hold on;
vlt.neuro.stimulus.plot_stimulus_timeseries(7,t.stimon,t.stimon+2,'stimid',data.stimid);

Let's look at what readtimeseries returned in the case of a stimulator. It is a little different than when being used with regularly-sampled data.

Code block 2.1.5.6. Type this in to Matlab:

t, % show timestamps
t.stimon,
data, % show data
data.stimid,
data.parameters{1}

Here we examined several fields of the variables data and t returned from readtimeseries from our ndi.probe.timeseries.stimulator.

You can see that t is a structure with 2 fields, stimon and stimoff. Our system kept track of when each stimulus began, but in these recordings, we did not have our data acquisition system keep track of when our stimulus turned off. (For later analysis, we will need to read this from the stimulus parameters.)

You can also see that data has some metadata about the stimuli. It has the ID number of each stimulus that was shown (data.stimid(1) is the stimulus that turned on at t.stimon(1)), and it has the parameters of each stimulus. data.parameters{1} is the parameters of the stimulus with ID number 1.

2.1.6 Discussion/Feedback

This concludes our tutorial on the fully manual way of reading data through NDI.

In the next tutorial, we'll explore how to create a couple of code objects that read our data directly from a lab's internal structure.

Post comments, bugs, questions, or discuss.