Menu

[r8989]: / trunk / py4science / examples / detect_action_potentials.py  Maximize  Restore  History

Download this file

184 lines (155 with data), 7.8 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
"""
In this example we will load binary membrane potential data from a
spiking neuron sampled at 2kHz, plot the voltage response vs time, and
write and algorithm to detect action potentials
"""
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.mlab as mlab
# Load the data from the file 'data/membrane.dat'. The data are
# stored as a single array of 4-byte floats. To scale the data into
# millivolts, multiple thebinary data by 100.
#@ HINT: You can use numpy.fromstring to load binary data, and the numpy
#@ datatype for 4-byte floats is np.float32
v = 100*np.fromfile('data/membrane.dat', np.float32) #@
# create an evenly sampled time array the same length as v samed at
# 2kHz. The array should be in seconds
#@ HINT: use np.arange to create an array of len(v) integers and then
#@ multiple by the stepsize 1/sampling frequency
t = np.arange(len(v))/2000. #@
# now plot the data, millivolts versus seconds, make an xlabel,
# ylabel, title and grid
fig = plt.figure() #@
ax = fig.add_subplot(111) #@
ax.plot(t, v) #@
ax.set_xlabel('time (s)') #@
ax.set_ylabel('mV') #@
ax.set_title('Spiking aplysia neuron') #@
ax.grid(True) #@
# now let's write an alogirthm to detect the times that an action
# potential occurs at. Plot a vertical line at each action potential
# time from the maximum voltage in the train (approx 5 mV but measure
# this from v) to the mx + 20mV. Use a simple algorithm which detects
# an action potential any time the voltage crosses -10mV from below.
# You can use a loop when practicing, but this should be done in numpy
# w/o a loop for full credit (and it will be much faster)
#@ HINT: create a boolean array equal to length(v) which is True if
#@ v>-10. Do the same where v<=-10. An action potential wjere the
#@ above condition is True and the below condition was True one index
#@ before. In the indexing below, we add 1 to correct for the [1:]
#@ slicing in the logical test. np.where turns the logical mask into
#@ an index array For example, the following finds the indicies where
#@ the uniform random numbers are above .95::
#@ x = np.random.rand(100)
#@ ind = np.where(x>0.95)[0]
above = v>-10 #@
below = v<=-10 #@
ind = np.where(above[1:] & below[:-1])[0] + 1 #@
vmax = v.max() #@
ax.vlines(t[ind], vmax, vmax+20) #@
# That was the easy way, but if we want a routine that works in batch,
# where we may not have the leisure to visually inspect the graph to
# find the threshold for action potentials (-10 in the example above)
# we have to bee a bit cleverer. A standard approach in the
# neurobiology community is to use the 2nd derivative of the voltage
# series, and when this is sufficiently positive, trigger an action
# potential. Write a function that detects action potentials and test
# it for several scalings of v which add an arbitrary constant and
# multiply it by an arbitrary scaling
def detect(t, vstar, refractory=0.05, percent_voltage=90., percent_d2=95.,
makefig=None):
"""
detect action potentials in vstar and return the indices. Remove
any event whose distance from the previous event is less than the
refractory period -- neurons are known to have a minimum time
between sikes and this will help remove duplicate events for each
spike. Return value is a numpy array of indices where the
conditions are met
Input parameters
----------------
*t*
the time array in seconds
*vstar*
the voltage array, equal to len(t)
*refractory*
the refractory period of the neuron, the minimum time in
seconds between action potentials
*percent_voltage*
an action potential must be in at least the percent voltage
percentile. Eg, if *percent_voltage*=95, the the voltage at the
spike must be at or above the 95th percentile of vstar
*percent_d2*
The second derivative of voltage must be in at least the
*percent_d2*-th percentile of second derivatives.
*makefig*
if *makefig* is not None, it is a matplotlib figure instance in
which to plot diagnostic information like the voltage trace,
the detected action potentials, the 2nd derivative of voltage,
and the *percent_voltage* and *percent_d2* tresholds
"""
# make sure *t* and *vstar* are numpy arrays
#@ HINT: use np.asarray
t = np.asarray(t)
vstar = np.asarray(vstar)
# Compute the 2nd derivative *d2* of *vstar* and measure the
# *percent_d2* cutoff percentile of all 2nd derivatives. Create a
# boolean mask array of len(v2) called *mask2* which is True
# everwhere that *d2* is >= cutoff.
#@ HINT: use np.diff(vstar, 2) to compute the 2nd derivative, and
#@ remember that the returned array will be two shorter than vstar,
#@ so you will have to index into your mask array starting in the
#@ 2nd position. You can use matplotlib.mlab.prctile to compute the
#@ percentiles
d2 = np.diff(vstar, 2) #@
threshold, = mlab.prctile(d2, (percent_d2,)) #@
mask2 = np.zeros(len(v2), np.bool) #@
mask2[2:] = d2>=threshold #@
# Now for the voltage check: the voltage at the action potentials
# must greater than the *percent_voltage* percentile, sometimes
# membrane noise has a large *d2*, so the 2nd derivative check
# alone is not sufficient to be sure we have an action potential.
# Create a boolean mask of len(v2) called *mask* that is True
# where *vstar* >= cutoff
cutoff, = mlab.prctile(vstar, (percent_voltage, )) #@
mask = vstar>=cutoff #@
# Now check the refractory condition -- the minimum time between
# spikes must be >= *refractory*. The default mask is True
# because the first spike will always be > refractory period First
# create the indices where both conditions in *mask* and *mask2*
# are True, find the times corresponding to these indicies, and
# only kee the times where the interspike-interval (the diff
# between times) >= refractory period.
ind = np.nonzero(mask & mask2) [0] #@
times = t[ind] #@
mask = np.ones(len(times), np.bool) #@
mask[1:] = np.diff(times)>=refractory #@
ind = ind[mask] #@
if makefig is None: return ind
fig = makefig
# plot *t* vs *vstar*, as before, but in an upper subplot plot the
# vlines as before, but you will have to figure out how big to
# make the vlines based on the min/max range of vstar
ax1 = fig.add_subplot(211) #@
ax1.plot(t, vstar) #@
ax1.axhline(cutoff) #@
vmax = vstar.max() #@
vrange = vmax-vstar.min() #@
ax1.vlines(t[ind], vmax, vmax+0.1*vrange) #@
# make a lower subplot to show d2 and the threshold; use the
# *sharex* keyword arg to link the x axis in the two subplots so as
# you pan and zoom in one, the other will be updated
ax2 = fig.add_subplot(212, sharex=ax1) #@
ax2.plot(t[2:], d2) #@
ax2.set_ylabel('2nd deriv') #@
ax2.axhline(threshold) #@
# now just return the indices
return ind
v1 = 20 + 20*v
v2 = -100 + 0.1*v
fig1 = plt.figure()
fig1.suptitle('Test auto-detect 1')
ind1 = detect(t, v1, makefig=fig1)
fig2 = plt.figure()
fig2.suptitle('Test auto-detect 2')
ind2 = detect(t, v2, makefig=fig2)
plt.show()
Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.