def processData(self, data): s = self.stateGroup.state() events = functions.zeroCrossingEvents(data, minLength=s['minLength'], minPeak=s['minPeak'], minSum=s['minSum']) events = events[:s['eventLimit']] return events
def process(self, data, display=True): rp = self.ctrls['risePower'].value() stimTime = self.ctrls['stimulusTime'].value() times = data.xvals('Time') dt = times[1] - times[0] data1 = data.view(np.ndarray) if stimTime > times[-1]: raise Exception("stimulusTime is larger than length of input data.") stimInd = np.argwhere(times>=stimTime)[0][0] # 1. make sure offset is removed offset = np.median(data1[:stimInd]) data2 = data1 - offset # 2. check for zero-crossing events within 4ms after stimulus cross = functions.zeroCrossingEvents(data2[stimInd:]) maxInd = 4e-3 / dt minLength = self.ctrls['minDirectDuration'].value() / dt gotEvent = None for start, length, sum, peak in cross: if start > maxInd: break if length < minLength: continue if gotEvent is None or length > gotEvent[1]: gotEvent = (start+stimInd, length) #if len(cross) == 0: ## must be a single large event #gotEvent = (0, len(data2)-stimInd) fitParams = dict( directFitAmp1=0., directFitAmp2=0., directFitTime=0., directFitRise=0., directFitDecay1=0., directFitDecay2=0., directFitPeakTime=0., directFitPeak=0., directFitSubtracted=False, directFitValid=False, ) # 3. if there is no large event near stimulus, return original data if gotEvent is None: if display: self.plotItem.clear() return {'output': data, 'fitParams': fitParams, 'plot': self.plotItem} #print "============" #print stimInd #print gotEvent #print cross[:20] # 4. guess amplitude, tau from detected event evStart = gotEvent[0] evEnd = evStart + gotEvent[1] evRgn = data2[evStart:evEnd] #pg.plot(evRgn) mx = evRgn.max() mn = evRgn.min() ampGuess = mx if abs(mx) > abs(mn) else mn tauGuess = gotEvent[1] * dt if ampGuess < 0: ampBound = [None, 0] else: ampBound = [0, None] # 5. fit guess = [ampGuess*2, ampGuess*2, stimTime, 1e-3, tauGuess*0.5, tauGuess*2] bounds = [ ampBound, ampBound, [stimTime, stimTime+4e-3], [1e-4, 100e-3], [1e-3, 1], [5e-3, 10], ] #print guess #print bounds endInd = evStart + gotEvent[1] * 10 fitYData = data2[stimInd:endInd] fitXData = times[stimInd:endInd] fit = functions.fitDoublePsp(x=fitXData, y=fitYData, guess=guess, bounds=bounds, risePower=rp) # 6. subtract fit from original data (offset included), return y = functions.doublePspFunc(fit, times, rp) if display: self.plotItem.setData(x=fitXData, y=y[stimInd:endInd], pen=self.ctrls['plotColor'].color()) ## prepare list of fit params for output (xMax, yMax) = functions.doublePspMax(fit) xMax -= fit[2] ## really interested in time-to-peak, not the exact peak time. fitParams = dict( directFitAmp1=fit[0], directFitAmp2=fit[1], directFitTime=fit[2], directFitRise=fit[3], directFitDecay1=fit[4], directFitDecay2=fit[5], directFitPeakTime=xMax, directFitPeak=yMax, directFitSubtracted=None, directFitValid=True, ) if self.ctrls['subtractDirect'].isChecked(): out = metaarray.MetaArray(data-y, info=data.infoCopy()) fitParams['directFitSubtracted'] = True else: out = data fitParams['directFitSubtracted'] = False return {'fitParams': fitParams, 'output': out, 'plot': self.plotItem}
def processEventFits(events, startEvent, stopEvent, opts): ## This function does all the processing work for EventFitter. dt = opts['dt'] origTau = opts['tau'] multiFit = opts['multiFit'] waveform = opts['waveform'] tvals = opts['tvals'] nFields = len(events.dtype.fields) dtype = [(n, events[n].dtype) for n in events.dtype.names] output = np.empty(len(events), dtype=dtype + [ ('fitAmplitude', float), ('fitTime', float), ('fitRiseTau', float), ('fitDecayTau', float), ('fitTimeToPeak', float), ('fitError', float), ('fitFractionalError', float), ('fitLengthOverDecay', float), ]) offset = 0 ## not all input events will produce output events; offset keeps track of the difference. outputState = { 'guesses': [], 'eventData': [], 'indexes': [], 'xVals': [], 'yVals': [] } for i in range(startEvent, stopEvent): start = events[i]['time'] #sliceLen = 50e-3 sliceLen = dt*300. ## Ca2+ events are much longer than 50ms if i+1 < len(events): nextStart = events[i+1]['time'] sliceLen = min(sliceLen, nextStart-start) guessLen = events[i]['len']*dt tau = origTau if tau is not None: guessLen += tau*2. #print i, guessLen, tau, events[i]['len']*dt #sliceLen = 50e-3 sliceLen = guessLen if i+1 < len(events): ## cut slice back if there is another event coming up nextStart = events[i+1]['time'] sliceLen = min(sliceLen, nextStart-start) ## Figure out from where to pull waveform data that will be fitted startIndex = np.argwhere(tvals>=start)[0][0] stopIndex = startIndex + int(sliceLen/dt) eventData = waveform[startIndex:stopIndex] times = tvals[startIndex:stopIndex] #print i, startIndex, stopIndex, dt if len(times) < 4: ## PSP fit requires at least 4 points; skip this one offset += 1 continue ## reconvolve this chunk of the signal if it was previously deconvolved if tau is not None: eventData = functions.expReconvolve(eventData, tau=tau, dt=dt) #print i, len(eventData) ## Make guesses as to the shape of the event mx = eventData.max() mn = eventData.min() if mx > -mn: peakVal = mx else: peakVal = mn guessAmp = peakVal * 2 ## fit converges more reliably if we start too large guessRise = guessLen/4. guessDecay = guessLen/2. guessStart = times[0] zc = functions.zeroCrossingEvents(eventData - (peakVal/3.)) ## eliminate events going the wrong direction if len(zc) > 0: if guessAmp > 0: zc = zc[zc['peak']>0] else: zc = zc[zc['peak']<0] #print zc ## measure properties for the largest event within 10ms of start zc = zc[zc['index'] < 10e-3/dt] if len(zc) > 0: if guessAmp > 0: zcInd = np.argmax(zc['sum']) ## the largest event in this clip else: zcInd = np.argmin(zc['sum']) ## the largest event in this clip zcEv = zc[zcInd] #guessLen = dt*zc[zcInd]['len'] guessRise = .1e-3 #dt*zcEv['len'] * 0.2 guessDecay = dt*zcEv['len'] * 0.8 guessStart = times[0] + dt*zcEv['index'] - guessRise*3. ## cull down the data set if possible cullLen = zcEv['index'] + zcEv['len']*3 if len(eventData) > cullLen: eventData = eventData[:cullLen] times = times[:cullLen] ## fitting to exponential rise * decay ## parameters are [amplitude, x-offset, rise tau, fall tau] guess = [guessAmp, guessStart, guessRise, guessDecay] #guess = [amp, times[0], guessLen/4., guessLen/2.] ## careful! bounds = [ sorted((guessAmp * 0.1, guessAmp)), sorted((guessStart-min(guessRise, 0.01), guessStart+guessRise*2)), sorted((dt*0.5, guessDecay)), sorted((dt*0.5, guessDecay * 50.)) ] yVals = eventData.view(np.ndarray) fit = functions.fitPsp(times, yVals, guess=guess, bounds=bounds, multiFit=multiFit) computed = functions.pspFunc(fit, times) peakTime = functions.pspMaxTime(fit[2], fit[3]) diff = (yVals - computed) err = (diff**2).sum() fracError = diff.std() / computed.std() lengthOverDecay = (times[-1] - fit[1]) / fit[3] # ratio of (length of data that was fit : decay constant) output[i-offset] = tuple(events[i]) + tuple(fit) + (peakTime, err, fracError, lengthOverDecay) #output['fitTime'] += output['time'] #print fit #self.events.append(eventData) outputState['guesses'].append(guess) outputState['eventData'].append(eventData) outputState['indexes'].append(i) outputState['xVals'].append(times) outputState['yVals'].append(computed) if offset > 0: output = output[:-offset] outputState['output'] = output return outputState