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.

 

 

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 )

w

Connecting to %s