import polars as plimport numpy as npimport tdt# HoloViews is a high-level plotting packageimport holoviews as hvimport holoviews.operation.datashaderhv.extension('bokeh')import datashaderimport bokeh.iobokeh.io.output_notebook()
Loading BokehJS ...
Tucker-Davis Technologies make hardware and accompanying software for a variety of neuroscience applications. Their data formats are specific to their instruments and are not in a standard format.
For this example, I will walk through how I figured out how to install the necessary software and load in TDT data sets.
I Googled TDT data file format Python and I got a hit for the company’s website on offline data analysis tools.
The documentation there recommends installing the software with pip install tdt, but I wanted to check dependencies first. I could not find this package on GitHub or other public repository, so I went straight to the Python Package Index for the tdt package.
I downloaded the files for the package. This does not install them, but simply saved on my machine.
I unzipped the download and navigated into the directory on the command line. I ran ls on the command line, and I found the following files and directories: PKG-INFO, setup.cfg, tdt/, README.md, setup.py, tdt.egg-info/.
There was no requirements.txt file, so I looked in the setup.cfg and setup.py files to see if there were dependencies. There were not.
I therefore navigated into the tdt/ directory and checked all import statements to find all packages and modules that the tdt package uses. I did this by executing grep import * on the command line.
I found that all imports were from the standard library except for Numpy. I therefore felt safe installing the tdt package in my current conda environment without the need to create a new one. Therefore, tdt is included in the datasai environment.
9.1 The sample data set
To demonstrate how TDT data are loaded and manipulated, we use a sample data set kindly donated from a friendly lab. The data set was acquired as part of a set of experiments looking at dopamine signaling in the nucleus accumbens area of a mouse brain in response to presentation and consumption of a palatable food stimulus, in this case a sucrose pellet. The nucleus accumbens was injected with an adeno-associated virus (AAV) delivering dLight, a fluorescent dopamine sensor. Then, a fiberoptic cannula was inserted into the nucleus accumbens to monitor fluorescence during the experiment.
During the imaging session, each mouse was placed in an experimental arena and allowed to acclimate for 5 minutes. A sucrose pellet was then placed in a designated area of the arena. Once the pellet was consumed, the experimenter waited a few minutes before placing the next pellet. This process was repeated until each mouse received a total of 12 sucrose pellets. Time stamps of pellet consumption were manually recorded based on the video recording of the experiment.
The TDT instrument measured two channels. The Ct1A channel was a measure of fluorescnece from a 405 nm excitation as a dopamine-independent control. The GC1A channel was excited at 465 nm and features dopamine-based fluorescence.
9.2 Loading in the TDT data
Now, let’s load in the data set! Let’s start with what we know how to do, and load in the time points where the pellets were presented based on a CSV file the experimenters manually constructed.
df_pellets = pl.read_csv(os.path.join(data_path, 'FB015_timepoints_pellets_V106N.csv'))# Take a lookdf_pellets
shape: (13, 6)
ID
Session
Timepoint
Hours
Minutes
Seconds
str
i64
str
i64
i64
f64
"V106N"
1
"start"
13
11
41.31
"V106N"
1
"p01"
13
17
4.58
"V106N"
1
"p02"
13
19
49.54
"V106N"
1
"p03"
13
22
34.12
"V106N"
1
"p04"
13
24
51.67
…
…
…
…
…
…
"V106N"
1
"p08"
13
33
16.24
"V106N"
1
"p09"
13
35
15.04
"V106N"
1
"p10"
13
37
24.36
"V106N"
1
"p11"
13
39
31.08
"V106N"
1
"p12"
13
41
34.59
We would like to convert Hours-Minutes-Seconds columns to total seconds.
Finally, we want to make the start time be zero, and then we can delete the row stating when the start was.
df_pellets = df_pellets.with_columns( pl.col('total_seconds') - pl.col('total_seconds').min()).filter( pl.col('Timepoint') !='start')# Take a lookdf_pellets
shape: (12, 7)
ID
Session
Timepoint
Hours
Minutes
Seconds
total_seconds
str
i64
str
i64
i64
f64
f64
"V106N"
1
"p01"
13
17
4.58
323.27
"V106N"
1
"p02"
13
19
49.54
488.23
"V106N"
1
"p03"
13
22
34.12
652.81
"V106N"
1
"p04"
13
24
51.67
790.36
"V106N"
1
"p05"
13
27
12.23
930.92
…
…
…
…
…
…
…
"V106N"
1
"p08"
13
33
16.24
1294.93
"V106N"
1
"p09"
13
35
15.04
1413.73
"V106N"
1
"p10"
13
37
24.36
1543.05
"V106N"
1
"p11"
13
39
31.08
1669.77
"V106N"
1
"p12"
13
41
34.59
1793.28
Now that we have that loaded, we can proceed to loading the data from the TDT instrument. The main function to do this, based on the documentation is tdt.read_block().
tdt_data = tdt.read_block(os.path.join(data_path, 'C57Bl6J_V106N_240910-131134'))# Take a looktdt_data
This view is useful, but we can get more information about the respective fields by investigating the object structure using the __dict__ attribute that every instance of a Python class has.
Apparently, the info and steams entries are the only ones with information. Specifically, the info entry has the duration of the experiment, which we can convert to total number of seconds since it is a datatime.timedelta object.
Now, we can turn our attention to the streams entry.
The two data sets are in the tdt_data.streams.Ct1A.data and tdt_data.streams.GC1A.data Numpy arrays. Since we can now extract the lengths of these arrays, we can compute the time points of the measurements, knowing also the duration of the experiment.
t = ( np.arange(len(tdt_data.streams.Ct1A.data)) /len(tdt_data.streams.Ct1A.data) * tdt_data.info.duration.total_seconds())
For convenience, we can make an array of the time when the pellets were presented.