Importing Spike2 data into Python

Spike2 is a piece of software written by the folks at CED (Cambridge Electronic Design) to collect data with their data acquisition boards. As pointed out in a previous post, Spike2 can be used to analyse data. However, the drop-down menus and options make it difficult to ensure processing is consistent from session to session. Spike2 comes with its own programming language, but it is not intuitive and also somewhat limited. Thus, many scientists choose to collect their data with Spike2, but process their data in another programming language such as Matlab or Python.

How do we get Spike2 data into Python? In our previous post, we saw how we could export our Spike2 data into a Matlab format (filename.mat) and import this file into Python. While this method works well, it does require us to use Spike2 to export all our data files into a Matlab format. This also means that we will have double the amount of data: the original Spike2 .smr file and the exported .mat file.

To address the two limitations of the previous solution, we will now learn how to use the neo Python package to directly read Spike2 .smr files.

The neo package

Neo is a package for analysing electrophysiology data in Python, together with support for reading a wide range of neurophysiology file formats. The package can be installed using pip install neo. The package depends on two other packages: quantities and numpy.

Important! When neo is installed using pip, version 0.5.2 is installed. This is the version I used in the current blog and accompanying code. Unfortunately using the newest version (0.6.1) caused the current code to not work. As a work around, I have include the neo package for version 0.5.2 here. I will look into getting the code to work with version 0.6.1 and post a revision when I have figured out the issue.

Personally, I run Python via Anaconda, a free and open source distribution of Python for data science. Anaconda comes with many of the packages that data scientists use on a daily basis, and it offers an easy mechanism to install new packages. Having said that, neo could not be install via Anaconda, so I downloaded the .tar.gz file, extracted the files and copied the neo folder to ~/anaconda3/lib/python3.6/site-packages/. This worked on my Linux computer, but the location may be slightly different on a Windows or Mac computer.

Using the neo package to load Spike2 files

Now we will learn how to use the neo.io.Spike2IO function to read our data in Python. The basic approach is to create a reader object and then read in the block of data.

import neo
import numpy as np


# create a reader
reader = neo.io.Spike2IO(filename='data.smr')
# read the block
data = reader.read(cascade=True, lazy=False)[0]

If we type data on the Python command line after running this code, we see:

Block with 1 segments
name: 'One segment only'
# segments (N=1)
0: Segment with 4 analogsignals
   annotations: {'ced_version': '3'}
   # analogsignals (N=4)
   0: AnalogSignal with 1 channels of length 217741; units dimensionless; datatype float32 
      name: "b'Flex'"
      annotations: {'channel_index': 0,
        'comment': b'No comment',
        'physical_channel_index': 0,
        'title': b'Flex'}
      sampling rate: 5000.0 Hz
      time: 0.0 s to 43.5482 s
   1: AnalogSignal with 1 channels of length 217741; units dimensionless; datatype float32 
      name: "b'Ext'"
      annotations: {'channel_index': 1,
        'comment': b'No comment',
        'physical_channel_index': 1,
        'title': b'Ext'}
      sampling rate: 5000.0 Hz
      time: 4.9999999999999996e-05 s to 43.54825 s
   2: AnalogSignal with 1 channels of length 21774; units dimensionless; datatype float32 
      name: "b'Angle'"
      annotations: {'channel_index': 3,
        'comment': b'',
        'physical_channel_index': 3,
        'title': b'Angle'}
      sampling rate: 500.0 Hz
      time: 9.999999999999999e-05 s to 43.548100000000005 s
   3: AnalogSignal with 1 channels of length 21774; units dimensionless; datatype float32 
      name: "b'triangle'"
      annotations: {'channel_index': 6,
        'comment': b'',
        'physical_channel_index': 6,
        'title': b'triangle'}
      sampling rate: 500.0 Hz
      time: 0.00015 s to 43.54815 s

Wow! That is a lot of information. It is a good idea to read the neo documentation if you want to learn more about how data is imported. Our current block of data contains 4 analog signals. We can see that the name of the first analog signal is b'Flex, it was channel index number 0 when it was recorded in Spike2, it was sampled at 5000Hz, and contains data from 0 to 43.5482 s.

Extracting time index and data from neo data objects

The people who created neo have included many functions that can be used with this type of imported data. However, I prefer to take full control of my data processing and would prefer to simply have an array of numerical values that correspond to sample times and data values.

I decided to create separate Python dictionaries for each channel of data. Each dictionary will contain the sample times, data, and sampling rate associated with a given channel. I already know that I have 4 channels named Flex, Ext, Angle and triangle, so initialize 4 dictionaries and add the desired channel information.

flex = {}
ext = {}
angle = {}
triangle = {}
for i, asig in enumerate(data.segments[0].analogsignals):
        # Extract sample times
        times = asig.times.rescale('s').magnitude
    # Determine channel name, without leading b' 
        ch = str(asig.annotations['title']).split(sep="'")[1]
    # Extract sampling frequency
        fs = float(asig.sampling_rate)
    # Assign sampling times, sampling frequency and data to correct dictionary
    if ch == 'Flex':
            flex['times'] = times
            flex['signal'] = np.array(asig)
            flex['fs'] = fs
            flex = signal_proc.emg_proc(flex)
        elif ch == 'Ext':
            ext['times'] = times
            ext['signal'] = np.array(asig)
            ext['fs'] = fs
            ext = signal_proc.emg_proc(ext)
        elif ch == 'Angle':
            angle['times'] = times
            angle['signal'] = np.array(asig)
            angle['fs'] = fs
            angle = signal_proc.tremor_proc(angle)
        elif ch == 'triangle':
            triangle['times'] = times
            triangle['signal'] = np.array(asig)
            triangle['fs'] = fs

It is also a good idea to plot our data to make sure it is what we expect. After running some basic processing on the data, we can generate the following figure with the angular data of wrist flexion-extension and the triangle target participants had to follow, and the muscle activity in the wrist flexor and extensor muscles.


spike2_python

The data file (data.smr) and code used in the present example (including the basic processing and plotting functions) (spike2_python.py) are available here.

Summary

We learned how to use the neo package to directly import Spike2 .smr files into Python. While the neo data format is a little complex and field-specific, it was relatively easy to extract the data we needed and use dictionaries and numpy arrays.

 

 

3 comments

  • For anyone interested, I have made a module to help analyse data collected in Spike2 using Python. I can be found here on GitHub.

    Because of issues trying to open Spike2 files directly in Python, the module assumes the data has been exported to .mat files (Matlab files). A script is provided that will allow bulk-export.

    A dedicated webpage provides some tutorials on how to use the module, as well as the API for the various components of the module.

    I have not yet incorporated spike-related signals (primarily because I did not need them at the time I was coding the module), but I have plans to include these when I get a bit more time, or I actually need them for my own work.

    Like

  • Hi Eduarda, sorry to hear you are having trouble loading your data. I have not used Python to analyse spike/wavemark data yet, but I am happy to learn.

    First, do you care about the actual spike templates or do you simply want to firing times?

    Also, is the channel you want (i.e. 16 nw-10) a sub-channel of the initial wavemark channel? From sorting motor units in the past, I vaguely remember something about being able to split the various spikes/templates into individual channel. I also remember that when I exported the data to .mat, I would have to change one of the setting from the Export window so that wavemark channels were outputed as spike times. I think it was possible to actually export the wavemark channels as templates and open them in Matlab, but it has been many years since I looked into this.

    While it is not an elegant solution, I tend to resort to exporting Spike2 data to .mat and then opening the files with the io module. This is what Jo presented in a previous post (https://scientificallysound.org/2016/12/26/file-inout-how-to-import-data-files-from-spike2-and-matlab-into-python/). Coincidently, I am currently writing a Python package that provides various tools to import and process Spike2 data that has been exported to .mat. It is mostly for my own use, but would be happy to share it if it could be of use. It does not have functionality for every type of signal yet (e.g. wavemark), but I am sure I could figure it out and add it.

    My guess is that exporting your data to .mat would give you access to the data you want. There is a script offered by CED that allows all .smr files in a folder to be saved to .mat, but I am not sure what the default behaviour is for wavemark channels. Alternatively you could create your own script, which is make easier by turning on the script recording option, opening, exporting and closing a file, and then have Spike2 generate a script of those actions. You can then have the script run on all your files (although I am not very good at Spike2 scripting, so I would not help you with this step).

    Let me know how you go. Try exporting to .mat and see if your channel is there. If you have trouble, I am happy to have a play with it when I get a bit of free time; simply send a link to an example file (you can export a portion of a Spike2 file to .smr; that way you don’t have share a full trial; 10s would be fine). Also, I am happy to share my Python Package to work with Spike2 files exported to .mat; but it still a work in progress…

    Marty

    Like

  • Hey, so I tried to use this script to read some data from .smr files of neuronal activity and singing of my birds, it worked fine for the raw data, recognizing and channels and etc.
    However, we have one extra ‘channel’ called ’16 nw-10′ that contains spikes templates (chunks of spikes), with little parts that we would like to be put together, but it was not recognized by this code, even when we got an .smr of only this channel.
    Probably is something about how the read command works, would you have any ideas how to help us?

    thanks in advance.
    Eduarda

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s