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()