StimulusData
This is a class which allows for the loading of Intan based stimulus data. Specifically .rhd files.
It relies on the Neo package from Python-Neo in order to lazily load analog and digital channels.
From this data it generates events, which can be used to analyze spike trains. These events
are based on TTL logic for digital signals and for deviations from 0Vs for analog stimuli. The length
of each event is assessed. These values are in samples rather than in time in order to allow
other operations to occur on ints rather than on floats.
Initializing StimulusData
StimulusData is intialized with a str or Path of the directory containing the .rhd
file. The class ensures that only this directory is used when processing this data. For Windows it may be necessary to
prepend “r” to the file path to generate the raw file path.
from spikeanalysis import StimulusData
stim = StimulusData(file_path = 'path/to/raw/data')
Or for Windows
from spikeanalysis import StimulusData
stim = StimulusData(file_path = r"path\to\raw\data") # r prevents escaping
First Processing Step for Both Analog and Digital data
Under the hood spikeanalysis uses NEO in order to read the .rhd file. The benefit of Neo
is that it uses a memorymap to prevent the whole file from being loaded into RAM during loading of the stimuli. Since
the large majority of the .rhd file is the amplifier_data which is used for spike sorting, but not needed
for this specific step, we can load the stimulus data from extremely large files even with relatively small amounts of RAM.
note this is bandwidth limited so loading from a local drive will be faster than over a network connection. The first
step is to create the NEO reader NEO.
stim.create_neo_reader() # creates the memmap for future functions
Time Slice
In the case of only wanting to analyze part of a recording a time_slice can be used with
the functions get_analog_data, get_raw_digital_data, and run_all. The
general set-up of the time_slice is to give the start and the stop within a
sequence (start, stop). This needs to be given in seconds and default is (None, None). when
None indicates the beginning or ending of the recording depending on the positioning. For example,
time_slice = (None, 10) would go from the start of the recording to only 10 seconds into the
recording.
Processing Analog Stimulus data
In order to assess analog stimulus data the data must first be read with Neo. This can be done with
get_analog_data. Then this data can be digitized (i.e. put into events) using the digitize_analog_data
stim.get_analog_data()
stim.digitize_analog_data(stim_length_seconds = 5, stim_name = ['ana_stim']) # ensure length of stimulus is longer than value entered
An example with a slice:
stim.get_analog_data(time_slice=(None, 300)) # from start to 6 minutes into recording
stim.get_analog_data(time_slice=(60, None)) # ignore first minute of recording
Processing Digital Stimulus data
Because Intan stores digital data with a bit-based code there are three functions to create events. First the raw digital
data is obtained with get_raw_digital_data. Then this data is converted to the distinct digital channels get_final_digital_data.
Finally the events are generated with generate_digital_events. For example:
stim.get_raw_digital_data()
stim.get_final_digital_data()
stim.generate_digital_events()
Because the trial groups can not automatically be determined for each stimulus, the code automatically sets each event to a grouping of 1.
If the stimulus is always the same (intensity, orientation et cetera), then this is not a problem, but if this is not desired, the
trial groups can be set with a utility function. set_trial_groups, which requires the trial_dictionary, a dictionary with
a key of the Intan channel and a value of the desired trial groups as an np.array. Since the channel names are not always easy to know
they can be returned using get_stimulus_channels. Finally stimulus’ should be named with set_stimulus_name.
stim_dict = stim.get_stimulus_channels()
stim.set_trial_groups(trial_dictionary=trial_dictionary) # dict as explained above
sitm.set_stimulus_name(stim_names = name_dictionary) # same keys with str values
Train-based data
One final utility function generate_stimulus_trains allows the conversion of digital stimulus data to trains. For example
in the case of optogenetic trains rather than looking at events, trains should be used. This code loads the
trains into the events. To do this a channel_name, a stim_freq (frequency of stimulus) and
stim_time_secs (length of the train) must be given.
Deleting Accidental events
In the case of an accidental event (for example turning on a TTL signal for just a moment accidental) one can use the delete_events()
function. Either one del_index is given or a list of indices to delete. This means that if event “25” was a mistaken signal one could
do the following:
stim.delete_events(del_index=24, digital=True, channel_name="DIGITAL-IN-01") #python is 0 based so 25th event is 24th index
Saving files for easy loading
After generating all raw analog, digital event data, and analog event data, a save function is provided which will store the
dictionary of event data as json and the raw analog data as a .npy binary file in the root folder. It is as
simple as
stim.save_events()
Loading previous data
Because generating the memmap file, loading the data, parsing the data, etc is a time consuming process if previous data has
been saved in the .rhd containing directory the get_all_files() function allows for loading in any previously
generated stimulus data. To load it simply requires:
stim.get_all_files()
Convenience Pipeline
With so many functions to run to process digital vs analog data a simple pipeline is included in the class to do most of the work
automatically. It also helps clean up the NEO reader memmap which can hold onto a small amount of RAM if not cleaned up. This
pipeline is triggered with run_all and only requires the insertion of the analog data parameters stim_length_seconds
and stim_name. Currently the trial groups and stimulus names for the digital data must occur outside of the pipeline.
And remember to save_events.
from spikeanalysis import StimulusData
stim = StimulusData(file_path='home/myawesomedata')
stim.run_all(stim_length_seconds=10, stim_name=['ana1'])
stim.set_trial_groups(trial_dictionary=my_dictionary)
stim.set_stimulus_name(stim_names=my_name_dictionary)
stim.save_events()