def event_cut(self): # Get cutting parameters try: baseline = float(self.eventBaseline.text()) / self.dt duration = float(self.eventDuration.text()) / self.dt except NameError: aux.error_box('Invalid cutting value') # Cut out events, allAttrs = [], [] for onset in self.xOnsets: eStart = onset - baseline eEnd = onset + duration eData = self.trace[eStart:eEnd] events.append(eData) attrs = {} attrs['dt'] = self.dt attrs['onset'] = onset allAttrs.append(attrs) # Store event waveforms in h5 data tree results = [] #attrs = {} #attrs['dt'] = self.dt for e in np.arange(0, len(events)): #attrs['onset'] = self.xOnsets[e] results.append(['event' + str(e), events[e], allAttrs[e]]) aux.save_results(self.browser, 'Events', results)
def event_cut(self): # Get cutting parameters try: baseline = float(self.eventBaseline.text())/self.dt duration = float(self.eventDuration.text())/self.dt except NameError: aux.error_box('Invalid cutting value') # Cut out events, allAttrs = [], [] for onset in self.xOnsets: eStart = onset-baseline eEnd = onset+duration eData = self.trace[eStart:eEnd] events.append(eData) attrs = {} attrs['dt'] = self.dt attrs['onset'] = onset allAttrs.append(attrs) # Store event waveforms in h5 data tree results = [] #attrs = {} #attrs['dt'] = self.dt for e in np.arange(0, len(events)): #attrs['onset'] = self.xOnsets[e] results.append(['event'+str(e), events[e], allAttrs[e]]) aux.save_results(self.browser, 'Events', results)
def event_cut(self): # Get trace and event onsets using stored dataIndex itrace = self.eventItemsIndex[0] trace = self.browser.ui.workingDataTree.dataItems[itrace] ionsets = self.eventItemsIndex[1] onsets = self.browser.ui.workingDataTree.dataItems[ionsets] dt = float(trace.attrs['dt']) # Get cutting parameters baseline = float(self.eventBaseline.text())/dt duration = float(self.eventDuration.text())/dt # Cut out events = [] for onset in onsets.data: eStart = onset-baseline eEnd = onset+duration eData = trace.data[eStart:eEnd] events.append(eData) # Store event waveforms in h5 data tree results = [] attrs = {} attrs['dt'] = dt for e in np.arange(0, len(events)): results.append(['event'+str(e), events[e], attrs]) aux.save_results(self.browser, 'Events', results)
def func(self, browser): """ Measure selected properties or statistics in the region defined by the data cursors. Options: 1) create new entries in Working Data tree with the results """ ############################################ # ANALYSIS FUNCTION plotWidget = browser.ui.dataPlotsWidget toolsWidget = browser.ui.oneDimToolStackedWidget data = aux.get_data(browser) c1, c2 = aux.get_cursors(plotWidget) dataIndex = plotWidget.plotDataIndex saveData = False # Get dt list dtList = aux.get_attr(plotWidget.plotDataItems, 'dt') # Go through data and check selected values to measure # Can probably do this in a more efficient way results = [] for option in self.toolOptions: if option.isChecked(): if option.text()=='Store result': saveData = True if option.text()=='Minimum': dataMin = [] for t in range(len(data)): dt = dtList[t] y = np.min(data[t][c1/dt:c2/dt]) x = np.argmin(data[t][c1/dt:c2/dt]) dataMin.append(y) aux.plot_point(plotWidget, c1/dt, x, y, dt) results.append(['Minimum', dataMin]) if option.text()=='Maximum': dataMax = [] for t in range(len(data)): dt = dtList[t] y = np.max(data[t][c1/dt:c2/dt]) x = np.argmax(data[t][c1/dt:c2/dt]) dataMax.append(y) aux.plot_point(plotWidget, c1/dt, x, y, dt) results.append(['Maximum', dataMax]) if option.text()=='Mean': dataMean = [] for t in range(len(data)): dt = dtList[t] y = np.mean(data[t][c1/dt:c2/dt]) dataMean.append(y) plotWidget.plot([c1,c2], [y,y], pen=pg.mkPen('#CF1C04', width=1)) results.append(['Mean', dataMean]) # Store results if saveData: aux.save_results(browser, 'Measurements', results)
def func(self, browser): """ Filter traces Options: 1) Filter type (currently Bessel only) 2) Filter parameters Note: filter frequencies are in Hz, make sure dt is in seconds """ ############################################ # ANALYSIS FUNCTION # Get options btype = str(self.comboPass.currentText()) if 'Low' in btype: btype = 'low' elif 'High' in btype: btype = 'high' try: cutoff = float(self.filterCutoff.text()) order = float(self.filterOrder.text()) except ValueError: aux.error_box('Invalid Cut off or Order value') return bidir = self.filterDirection.isChecked() # Get widgets plotWidget = browser.ui.dataPlotsWidget toolsWidget = browser.ui.oneDimToolStackedWidget # Get parent text of plotted items try: parentText = plotWidget.plotDataItems[0].parent().text(0) # Assumes all plotted data have the same parent except AttributeError: # Parent = None parentText = 'Data' # Filter data results, itemsToPlot = [], [] for item in plotWidget.plotDataItems: # Copy attributes and add some new ones attrs = item.attrs attrs['filter_type'] = btype attrs['filter_cutoff'] = cutoff attrs['filter_order'] = order # Filter traceFilter = acq4filter.besselFilter(item.data, cutoff, order, float(item.attrs['dt'])/1000, btype, bidir) results.append([item.text(0), traceFilter, attrs]) # Store filtered traces filterItem = aux.make_h5item('filter', traceFilter, item.attrs) itemsToPlot.append(filterItem) # Plot results pgplot.plot_multipleData(browser, plotWidget, itemsToPlot, clear=False, color='#F2EF44') # Store results aux.save_results(browser, parentText+'_filter', results)
def func(self, browser): """ Smooth traces Options: 1) Window type 2) Window length """ ############################################ # ANALYSIS FUNCTION # Get options window = str(self.comboBox.currentText()) try: window_len = float(self.smoothLength.text()) except ValueError: aux.error_box('Invalid Window Length') return # Get widgets plotWidget = browser.ui.dataPlotsWidget toolsWidget = browser.ui.oneDimToolStackedWidget # Get parent text of plotted items try: parentText = plotWidget.plotDataItems[0].parent().text( 0) # Assumes all plotted data have the same parent except AttributeError: # Parent = None parentText = 'Data' # Smooth data results, itemsToPlot = [], [] for item in plotWidget.plotDataItems: # Copy attributes and add some new ones attrs = item.attrs attrs['smooth_window_type'] = window attrs['smooth_window_length'] = window_len # Smooth traceSmooth = smooth.smooth(item.data, window_len=window_len, window=window) results.append([item.text(0), traceSmooth, attrs]) # Store smoothed item smoothItem = aux.make_h5item('smooth', traceSmooth, item.attrs) itemsToPlot.append(smoothItem) # Plot results pgplot.plot_multipleData(browser, plotWidget, itemsToPlot, clear=False, color='#F2EF44') # Store results aux.save_results(browser, parentText + '_smooth', results)
def func(self, browser): """ Remove a specific frequency from the data Options: 1) Frequency 2) Number of harmonics 3) Number of FFT data samples to blank centered on the Frequency 4) Scale factor for how much the frequency is dampened (0<x<1) Note: frequencies are in Hz, make sure dt is in seconds """ ############################################ # ANALYSIS FUNCTION # Get options frequency = float(self.freqRemoveFrequency.text()) harmonics = int(self.freqRemoveHarmonics.text()) samples = int(self.freqRemoveSamples.text()) mag = float(self.freqRemoveMag.text()) # Get widgets plotWidget = browser.ui.dataPlotsWidget toolsWidget = browser.ui.oneDimToolStackedWidget # Get parent text of plotted items try: parentText = plotWidget.plotDataItems[0].parent().text( 0) # Assumes all plotted data have the same parent except AttributeError: # Parent = None parentText = 'Data' # Remove frequency results = [] for item in plotWidget.plotDataItems: # Copy attributes and add some new ones attrs = item.attrs attrs['frequency_removed'] = frequency # Remove traceFilter = acq4filter.removePeriodic( item.data, frequency, float(item.attrs['dt']) / 1000, harmonics, samples, mag) results.append([item.text(0), traceFilter, attrs]) # Store and plot filtered trace filterItem = aux.make_h5item('filter', traceFilter, item.attrs) pgplot.browse_singleData(browser, plotWidget, filterItem, clear=False, color='#F2EF44') # Store results aux.save_results(browser, parentText + '_filter', results)
def func(self, browser): """ Average trials for protocols with more than one sweep (e.g.: current or voltage steps) Options: 1) Number of trials contained in the selected traces """ ############################################ # ANALYSIS FUNCTION # Get options try: numberOfTrials = int(self.trialsNumber.text()) except ValueError: aux.error_box('Please input a valid number of trials') return # Get widgets plotWidget = browser.ui.dataPlotsWidget toolsWidget = browser.ui.oneDimToolStackedWidget # Get parent text of plotted items try: parentText = plotWidget.plotDataItems[0].parent().text(0) # Assumes all plotted data have the same parent except AttributeError: # Parent = None parentText = 'Data' # Get data and attributes data = aux.get_data(self.browser) item = plotWidget.plotDataItems[0] # Reshape data and average try: data.shape = (numberOfTrials, data.shape[0]/numberOfTrials, data.shape[1]) avg = data.mean(axis=0) # Make h5 items results, itemsToPlot = [], [] for sweep in avg: #sweepItem = aux.make_h5item('sweep', sweep, item.attrs) results.append(['sweep', sweep, item.attrs]) # Store average sweep sweepItem = aux.make_h5item('sweep', sweep, item.attrs) itemsToPlot.append(sweepItem) # Plot results pgplot.plot_multipleData(browser, plotWidget, itemsToPlot, clear=False, color='#F2EF44') # Store results aux.save_results(browser, parentText+'_trialsAvg', results) except ValueError: aux.error_box('The number of sweeps and trials do not match', sys.exc_info())
def func(self, browser): """ Downsample data by taking one point every N points Options: 1) Number of points """ ############################################ # ANALYSIS FUNCTION # Get options try: nPoints = int(self.points.text()) except ValueError: aux.error_box('Invalid number of points') return # Get widgets plotWidget = browser.ui.dataPlotsWidget toolsWidget = browser.ui.oneDimToolStackedWidget # Get parent text of plotted items try: parentText = plotWidget.plotDataItems[0].parent().text( 0) # Assumes all plotted data have the same parent except AttributeError: # Parent = None parentText = 'Data' # Smooth data results, itemsToPlot = [], [] for item in plotWidget.plotDataItems: # Copy attributes and add some new ones attrs = dict(item.attrs) attrs['dt'] = attrs['dt'] * nPoints # Downsample traceDsampled = item.data[0::nPoints] results.append([item.text(0), traceDsampled, attrs]) # Store smoothed item dSampledItem = aux.make_h5item('smooth', traceDsampled, attrs) itemsToPlot.append(dSampledItem) # Plot results pgplot.plot_multipleData(browser, plotWidget, itemsToPlot, clear=False, color='#F2EF44') # Store results aux.save_results(browser, parentText + '_dSample', results)
def func(self, browser): """ Smooth traces Options: 1) Window type 2) Window length """ ############################################ # ANALYSIS FUNCTION # Get options window = str(self.comboBox.currentText()) try: window_len = float(self.smoothLength.text()) except ValueError: aux.error_box('Invalid Window Length') return # Get widgets plotWidget = browser.ui.dataPlotsWidget toolsWidget = browser.ui.oneDimToolStackedWidget # Get parent text of plotted items try: parentText = plotWidget.plotDataItems[0].parent().text(0) # Assumes all plotted data have the same parent except AttributeError: # Parent = None parentText = 'Data' # Smooth data results, itemsToPlot = [], [] for item in plotWidget.plotDataItems: # Copy attributes and add some new ones attrs = item.attrs attrs['smooth_window_type'] = window attrs['smooth_window_length'] = window_len # Smooth traceSmooth = smooth.smooth(item.data, window_len=window_len, window=window) results.append([item.text(0), traceSmooth, attrs]) # Store smoothed item smoothItem = aux.make_h5item('smooth', traceSmooth, item.attrs) itemsToPlot.append(smoothItem) # Plot results pgplot.plot_multipleData(browser, plotWidget, itemsToPlot, clear=False, color='#F2EF44') # Store results aux.save_results(browser, parentText+'_smooth', results)
def func(self, browser): """ Remove a specific frequency from the data Options: 1) Frequency 2) Number of harmonics 3) Number of FFT data samples to blank centered on the Frequency 4) Scale factor for how much the frequency is dampened (0<x<1) Note: frequencies are in Hz, make sure dt is in seconds """ ############################################ # ANALYSIS FUNCTION # Get options frequency = float(self.freqRemoveFrequency.text()) harmonics = int(self.freqRemoveHarmonics.text()) samples = int(self.freqRemoveSamples.text()) mag = float(self.freqRemoveMag.text()) # Get widgets plotWidget = browser.ui.dataPlotsWidget toolsWidget = browser.ui.oneDimToolStackedWidget # Get parent text of plotted items try: parentText = plotWidget.plotDataItems[0].parent().text(0) # Assumes all plotted data have the same parent except AttributeError: # Parent = None parentText = 'Data' # Remove frequency results = [] for item in plotWidget.plotDataItems: # Copy attributes and add some new ones attrs = item.attrs attrs['frequency_removed'] = frequency # Remove traceFilter = acq4filter.removePeriodic(item.data, frequency, float(item.attrs['dt'])/1000, harmonics, samples, mag) results.append([item.text(0), traceFilter, attrs]) # Store and plot filtered trace filterItem = aux.make_h5item('filter', traceFilter, item.attrs) pgplot.browse_singleData(browser, plotWidget, filterItem, clear=False, color='#F2EF44') # Store results aux.save_results(browser, parentText+'_filter', results)
def func(self, browser): """ Downsample data by taking one point every N points Options: 1) Number of points """ ############################################ # ANALYSIS FUNCTION # Get options try: nPoints = int(self.points.text()) except ValueError: aux.error_box('Invalid number of points') return # Get widgets plotWidget = browser.ui.dataPlotsWidget toolsWidget = browser.ui.oneDimToolStackedWidget # Get parent text of plotted items try: parentText = plotWidget.plotDataItems[0].parent().text(0) # Assumes all plotted data have the same parent except AttributeError: # Parent = None parentText = 'Data' # Smooth data results, itemsToPlot = [], [] for item in plotWidget.plotDataItems: # Copy attributes and add some new ones attrs = dict(item.attrs) attrs['dt'] = attrs['dt']*nPoints # Downsample traceDsampled = item.data[0::nPoints] results.append([item.text(0), traceDsampled, attrs]) # Store smoothed item dSampledItem = aux.make_h5item('smooth', traceDsampled, attrs) itemsToPlot.append(dSampledItem) # Plot results pgplot.plot_multipleData(browser, plotWidget, itemsToPlot, clear=False, color='#F2EF44') # Store results aux.save_results(browser, parentText+'_dSample', results)
def func(self, browser): """ Smooth traces Options: 1) Window type 2) Window length """ ############################################ # ANALYSIS FUNCTION # Get options window = str(self.comboBox.currentText()) window_len = float(self.smoothLength.text()) # Get widgets plotWidget = browser.ui.dataPlotsWidget toolsWidget = browser.ui.oneDimToolStackedWidget # Get parent text of plotted items try: parentText = plotWidget.plotDataItems[0].parent().text(0) # Assumes all plotted data have the same parent except AttributeError: # Parent = None parentText = 'Data' # Smooth data results = [] for item in plotWidget.plotDataItems: # Copy attributes and add some new ones attrs = item.attrs attrs['smooth_window_type'] = window attrs['smooth_window_length'] = window_len # Smooth traceSmooth = smooth.smooth(item.data, window_len=window_len, window=window) results.append([item.text(0), traceSmooth, attrs]) # Plot smoothed trace #x = np.arange(0, len(traceSmooth)*item.attrs['dt'], item.attrs['dt']) #plotWidget.plot(x, traceSmooth, pen=pg.mkPen('#F2EF44', width=1)) smoothItem = aux.make_h5item('smooth', traceSmooth, item.attrs) pgplot.browse_singleData(browser, plotWidget, smoothItem, clear=False, color='#F2EF44') # Store results aux.save_results(browser, parentText+'_smooth', results)
def func(self, browser): """ Calculate average trace from currently plotted traces. Options: 1) create new entry in Working Data tree with the result 2) plot average with orginal traces """ ############################################ # ANALYSIS FUNCTION plotWidget = browser.ui.dataPlotsWidget toolsWidget = browser.ui.oneDimToolStackedWidget # Clear plot and get data plotWidget.clear() data = aux.get_data(browser) # Get dt and dt = aux.get_attr(plotWidget.plotDataItems, 'dt')[0] # Calculate average avgData = np.mean(data,0) # Check selected options for option in self.toolOptions: if option.isChecked(): if option.text()=='Store result': results = [] # Get attributes from plotted items item = plotWidget.plotDataItems[0] attrs = item.attrs # Store data results.append(['avg_trace', avgData, attrs]) aux.save_results(browser, item.parent().text(0)+'_average', results) if option.text()=='Show traces': pgplot.replot(browser, plotWidget) # Plot average item = aux.make_h5item('avg', avgData, plotWidget.plotDataItems[0].attrs) pgplot.browse_singleData(browser, plotWidget, item, clear=False, color='r') if browser.ui.actionShowCursors.isChecked(): pgplot.replot_cursors(browser, plotWidget)
def event_cut(self, profile=False): # Read options baseline = float(self.eventBaseline.text()) duration = float(self.eventDuration.text()) # Get plotted tracking data trackData = self.plotWidget.plotDataItems[0].data # Get selected trigger data item = self.browser.ui.workingDataTree.currentItem() triggers = item.data # Deal with dt dt = self.plotWidget.plotDataItems[0].attrs['dt'] item.attrs['dt'] = dt # Extract events events, stimProfiles = [], [] for t in self.tevents: event = trackData[t-int(baseline/dt):t+int(duration/dt)] events.append(event) if profile: eventProfile = self.triggerProfileData[t-int(baseline/dt):t+int(duration/dt)] stimProfiles.append(eventProfile) # Store data results = [] attrs = {} attrs['dt'] = dt attrs['t0'] = baseline # in sampling points for e in np.arange(0, len(events)): attrs['trigger_time'] = self.tevents[e] # in sampling points results.append(['event'+str(e), events[e], attrs]) aux.save_results(self.browser, 'Events', results) if profile: results = [] for e in np.arange(0, len(stimProfiles)): attrs['trigger_time'] = self.tevents[e] # in sampling points results.append(['stim'+str(e), stimProfiles[e], attrs]) aux.save_results(self.browser, 'Stimulation Profiles', results)
def func(self, browser): """ Analyse tracking data for user defined zones in the arena. Currently the two zones are defined by a divider on the X or Y coordinates. Properties measured: 1 - time spent in each zone normalized to total time 2 - number of zone transitions Options: 1) Position of the divider between the two zones """ ############################################ # ANALYSIS FUNCTION # Read options zoneDivider = float(self.plotWidget.zoneDividerCursorPos) # Get data and check cursors traces = self.plotWidget.plotDataItems dt = traces[0].attrs['dt'] data, c1 = aux.get_dataRange(self.plotWidget, traces[0]) # Get time in each zone zoneTime = [] totalTime = float(len(data)) zone1 = np.sum(data < zoneDivider) zone2 = totalTime - zone1 zoneTime.append(zone1 / totalTime) zoneTime.append(zone2 / totalTime) # Get number of zone transitions transitions = np.sum(np.diff(data < zoneDivider)) # Store data results = [] results.append(['Time', zoneTime]) results.append(['Transitions', [transitions]]) aux.save_results(self.browser, 'Zone_analysis', results)
def func(self, browser): """ Temporary event detection function using amplitude threshold only. Noise safety is for when coming down from peak, go down an extra amount from threshold before starting to search for the next event. """ ############################################ # ANALYSIS FUNCTION # Read detection options try: threshold = float(self.browser.ui.dataPlotsWidget.cursorThsPos) except NameError: aux.error_box('No threshold selected') return try: noiseSafety = float(self.eventNoiseSafety.text()) smoothFactor = float(self.eventSmooth.text()) direction = str(self.eventDirection.currentText()) minDuration = float(self.eventMinDuration.text()) detectionTrace = str(self.detectionTrace.currentText()) minInterval = float(self.eventMinInterval.text()) except NameError: aux.error_box('Invalid detection value') bslWindow = 1.0 slowestRise = 0.5 #minEventInterval = 5000.0 # Ensure that noise safety has the same sign as the threshold noiseSafety = np.sign(threshold) * abs(noiseSafety) # Get widgets plotWidget = browser.ui.dataPlotsWidget # Get dt list and attrs for use in concatenated data dtList = aux.get_attr(self.browser.ui.dataPlotsWidget.plotDataItems, 'dt') dt = dtList[0] self.dt = dt item = self.browser.ui.dataPlotsWidget.plotDataItems[0] attrs = item.attrs # Get data currently plotted and concatenate in a single sweep data = [] for item in plotWidget.plotDataItems: trace, c1 = aux.get_dataRange(plotWidget, item) data.append(trace) data = np.array(data).ravel() # Get derivative trace and filter dtrace = None if detectionTrace=='derivative': print threshold dtrace = np.diff(data) dtrace = acq4filter.besselFilter(dtrace, 2000, 1, dt/1000, 'low', True) # Smooth self.trace = data if smoothFactor > 1: data = smooth.smooth(data, window_len=smoothFactor, window='hanning') # Comparison functions if direction=='negative': comp = lambda a, b: a < b elif direction=='positive': comp = lambda a, b: a > b # Correct times for dt minEventInterval = minInterval/dt minDuration = minDuration/dt bslWindow = bslWindow/dt slowestRise = slowestRise/dt # Run detection eventCounter,i = 0,0 #+bslWindow/dt+slowestRise/dt iLastDetection = 0 self.xOnsets, self.yOnsets = [], [] bsl = 0 if dtrace is not None: detectionData = dtrace else: detectionData = data while i<len(detectionData): # Sliding baseline #bsl = np.mean(data[i-bslWindow-slowestRise:i]) if comp(detectionData[i]-bsl,threshold): print i, i-iLastDetection if i-iLastDetection>minEventInterval: # Min inter-event interval self.xOnsets.append(i) self.yOnsets.append(data[i]) eventCounter+=1 iLastDetection = i while i<len(detectionData) and comp(detectionData[i]-bsl,(threshold-noiseSafety)): i+=1 # skip values if index in bounds AND until the value is below/above threshold again if i-iLastDetection < minDuration: # Event is too brief self.xOnsets.pop() self.yOnsets.pop() eventCounter-=1 else: i+=1 # for min event interval else: i+=1 frequency = eventCounter/(len(data)*dt)*1000 # in Hz print eventCounter, 'events detected at', frequency, 'Hz' # Store event onsets and peaks in h5 data tree results = [] results.append(['trace', np.array(self.trace), attrs]) results.append(['xOnsets', np.array(self.xOnsets)]) results.append(['yOnsets', np.array(self.yOnsets)]) results.append(['number', np.array([eventCounter])]) results.append(['frequency', np.array([frequency])]) aux.save_results(browser, 'Event_Detection', results) # Plot results self.show_events(data, np.array(self.xOnsets), np.array(self.yOnsets), dt) # Turn cursors off (the plot has been cleared so there are no cursors displayed) self.browser.ui.actionShowCursors.setChecked(False) plotWidget.cursor = False
def func(self, browser): """ Temporary event detection function using amplitude threshold only. Noise safety is for when coming down from peak, go down an extra amount from threshold before starting to search for the next event. """ ############################################ # ANALYSIS FUNCTION # Read detection options threshold = float(self.browser.ui.dataPlotsWidget.cursorThsPos) noiseSafety = float(self.eventNoiseSafety.text()) smoothFactor = float(self.eventSmooth.text()) direction = str(self.eventDirection.currentText()) c1, c2 = aux.get_cursors(self.browser.ui.dataPlotsWidget) # Ensure that noise safety has the same sign as the threshold noiseSafety = np.sign(threshold) * abs(noiseSafety) # Get dt list and attrs for use in concatenated data dtList = aux.get_attr(self.browser.ui.dataPlotsWidget.plotDataItems, 'dt') dt = dtList[0] item = self.browser.ui.dataPlotsWidget.plotDataItems[0] attrs = item.attrs # Get data currently plotted within the cursors and concatenate in a single sweep data = aux.get_data(self.browser) if browser.ui.dataPlotsWidget.cursor1Pos: data = data[:,c1/dt:c2/dt] data = data.ravel() # Smooth original_data = data if smoothFactor > 1: data = smooth.smooth(data, window_len=smoothFactor, window='hanning') # Run detection if direction=='negative': comp = lambda a, b: a < b elif direction=='positive': comp = lambda a, b: a > b eventCounter,i = 0,0 xOnsets, yOnsets = [], [] while i<len(data): if comp(data[i],threshold): xOnsets.append(i) yOnsets.append(data[i]) eventCounter +=1 while i<len(data) and comp(data[i],(threshold-noiseSafety)): i+=1 # skip values if index in bounds AND until the value is below/above threshold again else: i+=1 frequency = eventCounter/(len(data)*dt)*1000 # in Hz print eventCounter, 'events detected at', frequency, 'Hz' # Store event onsets and peaks in h5 data tree results = [] results.append(['trace', np.array(original_data), attrs]) results.append(['onsets', np.array(xOnsets)]) results.append(['peaks', np.array(yOnsets)]) results.append(['number', np.array([eventCounter])]) results.append(['frequency', np.array([frequency])]) listIndexes = aux.save_results(browser, 'Event_Detection', results) # Store list indexes temporarily in stack widget list for further event analysis self.eventItemsIndex = listIndexes # Plot results self.show_events(data, np.array(xOnsets), np.array(yOnsets), dt)
def func(self, browser): """ Temporary event detection function using amplitude threshold only. Noise safety is for when coming down from peak, go down an extra amount from threshold before starting to search for the next event. """ ############################################ # ANALYSIS FUNCTION # Read detection options try: threshold = float(self.browser.ui.dataPlotsWidget.cursorThsPos) except NameError: aux.error_box('No threshold selected') return try: noiseSafety = float(self.eventNoiseSafety.text()) smoothFactor = float(self.eventSmooth.text()) direction = str(self.eventDirection.currentText()) minDuration = float(self.eventMinDuration.text()) detectionTrace = str(self.detectionTrace.currentText()) minInterval = float(self.eventMinInterval.text()) except NameError: aux.error_box('Invalid detection value') bslWindow = 1.0 slowestRise = 0.5 #minEventInterval = 5000.0 # Ensure that noise safety has the same sign as the threshold noiseSafety = np.sign(threshold) * abs(noiseSafety) # Get widgets plotWidget = browser.ui.dataPlotsWidget # Get dt list and attrs for use in concatenated data dtList = aux.get_attr(self.browser.ui.dataPlotsWidget.plotDataItems, 'dt') dt = dtList[0] self.dt = dt item = self.browser.ui.dataPlotsWidget.plotDataItems[0] attrs = item.attrs # Get data currently plotted and concatenate in a single sweep data = [] for item in plotWidget.plotDataItems: trace, c1 = aux.get_dataRange(plotWidget, item) data.append(trace) data = np.array(data).ravel() # Get derivative trace and filter dtrace = None if detectionTrace == 'derivative': print threshold dtrace = np.diff(data) dtrace = acq4filter.besselFilter(dtrace, 2000, 1, dt / 1000, 'low', True) # Smooth self.trace = data if smoothFactor > 1: data = smooth.smooth(data, window_len=smoothFactor, window='hanning') # Comparison functions if direction == 'negative': comp = lambda a, b: a < b elif direction == 'positive': comp = lambda a, b: a > b # Correct times for dt minEventInterval = minInterval / dt minDuration = minDuration / dt bslWindow = bslWindow / dt slowestRise = slowestRise / dt # Run detection eventCounter, i = 0, 0 #+bslWindow/dt+slowestRise/dt iLastDetection = 0 self.xOnsets, self.yOnsets = [], [] bsl = 0 if dtrace is not None: detectionData = dtrace else: detectionData = data while i < len(detectionData): # Sliding baseline #bsl = np.mean(data[i-bslWindow-slowestRise:i]) if comp(detectionData[i] - bsl, threshold): print i, i - iLastDetection if i - iLastDetection > minEventInterval: # Min inter-event interval self.xOnsets.append(i) self.yOnsets.append(data[i]) eventCounter += 1 iLastDetection = i while i < len(detectionData) and comp( detectionData[i] - bsl, (threshold - noiseSafety)): i += 1 # skip values if index in bounds AND until the value is below/above threshold again if i - iLastDetection < minDuration: # Event is too brief self.xOnsets.pop() self.yOnsets.pop() eventCounter -= 1 else: i += 1 # for min event interval else: i += 1 frequency = eventCounter / (len(data) * dt) * 1000 # in Hz print eventCounter, 'events detected at', frequency, 'Hz' # Store event onsets and peaks in h5 data tree results = [] results.append(['trace', np.array(self.trace), attrs]) results.append(['xOnsets', np.array(self.xOnsets)]) results.append(['yOnsets', np.array(self.yOnsets)]) results.append(['number', np.array([eventCounter])]) results.append(['frequency', np.array([frequency])]) aux.save_results(browser, 'Event_Detection', results) # Plot results self.show_events(data, np.array(self.xOnsets), np.array(self.yOnsets), dt) # Turn cursors off (the plot has been cleared so there are no cursors displayed) self.browser.ui.actionShowCursors.setChecked(False) plotWidget.cursor = False
def func(self, browser): """ Calculate the probability of having an event per time bin Very simple threshold crossing event detection. Options: 1) Threshold for detecting spikes 2) Time bin size (ms) 3) Event direction """ ############################################ # ANALYSIS FUNCTION # Read detection options try: threshold = float(self.browser.ui.dataPlotsWidget.cursorThsPos) except NameError: aux.error_box('No threshold selected') return try: timeBin = np.abs(float(self.timeBin.text())) except ValueError: aux.error_box('Invalid time bin') return direction = str(self.eventDirection.currentText()) # Get widgets plotWidget = browser.ui.dataPlotsWidget toolsWidget = browser.ui.oneDimToolStackedWidget # Get parent text of plotted items try: parentText = plotWidget.plotDataItems[0].parent().text( 0) # Assumes all plotted data have the same parent except AttributeError: # Parent = None parentText = 'Data' # Detect events in each trace if direction == 'negative': comp = lambda a, b: a < b elif direction == 'positive': comp = lambda a, b: a > b # Check time bin (using the first plotted item) item = plotWidget.plotDataItems[0] dt = item.attrs['dt'] data, c1 = aux.get_dataRange(plotWidget, item) if (timeBin / dt == 0) or (len(data) < timeBin / dt): aux.error_box( 'Invalid time bin', infoText= 'Make sure time bin is smaller than the selected data range') return # Detect events results = [] plotWidget.clear() for item in plotWidget.plotDataItems: dt = item.attrs['dt'] data, c1 = aux.get_dataRange(plotWidget, item) apCounter, i = 0, 0 xOnsets, yOnsets = [], [] while i < len(data): if comp(data[i], threshold): xOnsets.append(i) yOnsets.append(data[i]) apCounter += 1 while i < len(data) and comp(data[i], threshold): i += 1 # skip values if index in bounds AND until the value is below/above threshold again else: i += 1 # Get events per time bin binSize = int(timeBin / dt) nbins = np.ceil(len(data) / binSize) eventCounter = [] for b in np.arange(1, nbins + 1): count = np.sum((xOnsets > (b - 1) * binSize) & (xOnsets < (b * binSize))) eventCounter.append(count) bins = np.arange(0 + binSize / 2., nbins * binSize + binSize / 2., binSize) * dt # Store data results.append(['event_counts', np.array(eventCounter)]) # Plot detected events self.show_events(data, np.array(xOnsets), np.array(yOnsets), dt) # Turn cursors off (the plot has been cleared so there are no cursors displayed) self.browser.ui.actionShowCursors.setChecked(False) plotWidget.cursor = False # Store results results.append(['time_bins', bins]) aux.save_results(browser, parentText + '_eventProbability', results)
def func(self, browser): """ Measure selected properties or statistics in the region defined by the data cursors. Rise time and onset are calculated by finding the peak and walking back until the limits are crossed. Onset limit is baseline mean; if no baseline has been set, it is taken as the mean value of the first 1 ms of data. Options: 1) create new entries in Working Data tree with the results """ ############################################ # ANALYSIS FUNCTION # Get widgets plotWidget = browser.ui.dataPlotsWidget toolsWidget = browser.ui.oneDimToolStackedWidget # Replot data to clear previous measure plot points pgplot.replot(browser, plotWidget) # Iterate through traces dataMin, dataMax, dataMean, dataMedian, dataSEM = [], [], [], [], [] dataRiseTime, dataOnset, dataDecay = [], [], [] for item in plotWidget.plotDataItems: # Get dt and data range dt = item.attrs['dt'] data, c1, cx1, cx2 = aux.get_dataRange(plotWidget, item, cursors=True) # Check baseline if 'baselineStart' in item.analysis: bsl = 0 # the mean of the baseline will always be 0 else: bsl = np.mean(data[0:round(1./dt)]) # Measure selected parameters if self.minBox.isChecked(): y = np.min(data) x = np.argmin(data) dataMin.append(y) aux.plot_point(plotWidget, c1, x, y, dt) if self.maxBox.isChecked(): y = np.max(data) x = np.argmax(data) dataMax.append(y) aux.plot_point(plotWidget, c1, x, y, dt) if self.meanBox.isChecked(): y = np.mean(data) dataMean.append(y) plotWidget.plot([cx1,cx2], [y,y], pen=pg.mkPen('#CF1C04', width=1)) if self.medianBox.isChecked(): y = np.median(data) dataMedian.append(y) plotWidget.plot([cx1,cx2], [y,y], pen=pg.mkPen('#CF1C04', width=1)) if self.semBox.isChecked(): y = np.std(data)/np.sqrt(len(data)) dataSEM.append(y) plotWidget.plot([cx1,cx2], [y,y], pen=pg.mkPen('#CF1C04', width=1)) if self.riseTimeBox.isChecked(): try: lowerLimit = float(self.riseLowerLimit.text())/100 upperLimit = float(self.riseUpperLimit.text())/100 except ValueError: aux.error_box('Invalid limits') return # Get peak xPeak, yPeak = self.get_peak(data) # Get limits lowerLimit = lowerLimit*(yPeak-bsl)+bsl upperLimit = upperLimit*(yPeak-bsl)+bsl # Find limit crosses lowerCrossed, upperCrossed = False, False i = 0 while lowerCrossed==False or upperCrossed==False: if (upperCrossed==False) and self.comp(data[xPeak-i],upperLimit): xUpper = xPeak-i upperCrossed = True if self.comp(data[xPeak-i], lowerLimit): xLower = xPeak-i lowerCrossed = True i+=1 dataRiseTime.append((xUpper-xLower)*dt) # Plot points aux.plot_point(plotWidget, c1, xLower, lowerLimit, dt) aux.plot_point(plotWidget, c1, xUpper, upperLimit, dt) if self.onsetBox.isChecked(): xPeak, yPeak = self.get_peak(data) bslCrossed = False i = 0 while bslCrossed==False: if self.comp(data[xPeak-i], bsl): onset = xPeak-i bslCrossed = True i+=1 aux.plot_point(plotWidget, c1, onset, bsl, dt) dataOnset.append(onset*dt) if self.decayTimeBox.isChecked(): try: decayLimit = float(self.decayLimit.text())/100 except ValueError: aux.error_box('Invalid limits') return xPeak, yPeak = self.get_peak(data) yDecay = decayLimit*(yPeak-bsl)+bsl decayCrossed = False i = 0 while decayCrossed==False: if self.comp(data[xPeak+i], yDecay): decayTime = xPeak+i decayCrossed = True i+=1 aux.plot_point(plotWidget, c1, decayTime, yDecay, dt) dataDecay.append(decayTime*dt) # Store results results = [] if self.storeBox.isChecked(): if self.minBox.isChecked(): results.append(['Minimum', np.array(dataMin)]) if self.maxBox.isChecked(): results.append(['Maximum', np.array(dataMax)]) if self.meanBox.isChecked(): results.append(['Mean', np.array(dataMean)]) if self.medianBox.isChecked(): results.append(['Median', np.array(dataMedian)]) if self.semBox.isChecked(): results.append(['SEM', np.array(dataSEM)]) if self.riseTimeBox.isChecked(): results.append(['RiseTime', np.array(dataRiseTime)]) if self.onsetBox.isChecked(): results.append(['Onset', np.array(dataOnset)]) if self.decayTimeBox.isChecked(): results.append(['Decay', np.array(dataDecay)]) aux.save_results(browser, 'Measurements', results)
def func(self, browser): """ Generate a FI curve from current steps Options: 1) Threshold for detecting spikes """ ############################################ # ANALYSIS FUNCTION # Read detection options try: threshold = float(self.browser.ui.dataPlotsWidget.cursorThsPos) except NameError: aux.error_box('No threshold selected') return # Get widgets plotWidget = browser.ui.dataPlotsWidget toolsWidget = browser.ui.oneDimToolStackedWidget # Get parent text of plotted items try: parentText = plotWidget.plotDataItems[0].parent().text(0) # Assumes all plotted data have the same parent except AttributeError: # Parent = None parentText = 'Data' # Detect APs in each trace comp = lambda a, b: a > b apNumber, apFrequency, results = [], [], [] plotWidget.clear() for item in plotWidget.plotDataItems: dt = item.attrs['dt'] data, c1 = aux.get_dataRange(plotWidget, item) apCounter, i = 0, 0 xOnsets, yOnsets = [], [] while i<len(data): if comp(data[i],threshold): xOnsets.append(i) yOnsets.append(data[i]) apCounter+=1 while i<len(data) and comp(data[i],threshold): i+=1 # skip values if index in bounds AND until the value is below/above threshold again else: i+=1 # Calculate AP frequency as 1/mean(ISI) isi = np.mean(np.diff(xOnsets))*dt # in ms if isi>0: freq = 1/(isi/1000.) else: freq = 0.0 # Store number of APs and mean frequency apNumber.append(apCounter) apFrequency.append(freq) # Plot detected APs self.show_events(data, np.array(xOnsets), np.array(yOnsets), dt) # Turn cursors off (the plot has been cleared so there are no cursors displayed) self.browser.ui.actionShowCursors.setChecked(False) plotWidget.cursor = False # Store results results.append(['AP_number', np.array(apNumber)]) results.append(['AP_frequency', np.array(apFrequency)]) aux.save_results(browser, parentText+'_FI', results)
def func(self, browser): """ Fit data traces within cursor-defined range Options: 1) Equation 2) Initial guesses """ ############################################ # ANALYSIS FUNCTION # Get widgets plotWidget = browser.ui.dataPlotsWidget toolsWidget = browser.ui.oneDimToolStackedWidget # Replot data to clear previous measure plot points pgplot.replot(browser, plotWidget) # Get parent text of plotted items try: parentText = plotWidget.plotDataItems[0].parent().text(0) # Assumes all plotted data have the same parent except AttributeError: # Parent = None parentText = 'Data' # Get current function currentFuncmap = self.dataFit.fitfuncmap[str(self.comboBox.currentText())] # Read initial guesses pInit = [] try: nParam = len(currentFuncmap[2]) for n in range(nParam): pInit.append(float(self.p[n].text())) except ValueError: aux.error_box('Invalid value in initial guess') return # Fit data fitResults, fitTraces, fitTracesAttrs = [], [], [] for item in plotWidget.plotDataItems: # Get data range to fit dt = item.attrs['dt'] fitTracesAttrs.append(item.attrs) yData, c1, cx1, cx2 = aux.get_dataRange(plotWidget, item, cursors=True) xRange = pgplot.make_xvector(yData, dt)+cx1 self.dataFit.c1 = cx1 # Fit func = currentFuncmap[0] fitParams = self.dataFit.fit(func, xRange, yData, pInit) fitResults.append(fitParams) print fitParams # Plot fitted function over trace if self.extendBox.isChecked(): xRange = pgplot.make_xvector(item.data, dt) #xRange = np.arange(plotWidget.viewBox.viewRange()[0][0], plotWidget.viewBox.viewRange()[0][1], dt) fittedTrace = func(xRange, *fitParams) plotWidget.plot(xRange, fittedTrace, pen=pg.mkPen('r', width=1)) fitTraces.append(fittedTrace) # Store results if self.storeBox.isChecked(): fitResults = np.array(fitResults) results = [] for n in range(np.shape(fitResults)[1]): results.append([str(self.plabels[n].text()), fitResults[:,n]]) for n in range(len(fitTraces)): results.append(['fitTraces_'+str(n), fitTraces[n], fitTracesAttrs[n]]) aux.save_results(browser, parentText+'_fit', results)
def func(self, browser): """ Calculate the probability of having an event per time bin Very simple threshold crossing event detection. Options: 1) Threshold for detecting spikes 2) Time bin size (ms) 3) Event direction """ ############################################ # ANALYSIS FUNCTION # Read detection options try: threshold = float(self.browser.ui.dataPlotsWidget.cursorThsPos) except NameError: aux.error_box('No threshold selected') return try: timeBin = np.abs(float(self.timeBin.text())) except ValueError: aux.error_box('Invalid time bin') return direction = str(self.eventDirection.currentText()) # Get widgets plotWidget = browser.ui.dataPlotsWidget toolsWidget = browser.ui.oneDimToolStackedWidget # Get parent text of plotted items try: parentText = plotWidget.plotDataItems[0].parent().text(0) # Assumes all plotted data have the same parent except AttributeError: # Parent = None parentText = 'Data' # Detect events in each trace if direction=='negative': comp = lambda a, b: a < b elif direction=='positive': comp = lambda a, b: a > b # Check time bin (using the first plotted item) item = plotWidget.plotDataItems[0] dt = item.attrs['dt'] data, c1 = aux.get_dataRange(plotWidget, item) if (timeBin/dt==0) or (len(data)<timeBin/dt): aux.error_box('Invalid time bin', infoText='Make sure time bin is smaller than the selected data range') return # Detect events results = [] plotWidget.clear() for item in plotWidget.plotDataItems: dt = item.attrs['dt'] data, c1 = aux.get_dataRange(plotWidget, item) apCounter, i = 0, 0 xOnsets, yOnsets = [], [] while i<len(data): if comp(data[i],threshold): xOnsets.append(i) yOnsets.append(data[i]) apCounter+=1 while i<len(data) and comp(data[i],threshold): i+=1 # skip values if index in bounds AND until the value is below/above threshold again else: i+=1 # Get events per time bin binSize = int(timeBin/dt) nbins = np.ceil(len(data)/binSize) eventCounter = [] for b in np.arange(1, nbins+1): count = np.sum((xOnsets>(b-1)*binSize) & (xOnsets<(b*binSize))) eventCounter.append(count) bins = np.arange(0+binSize/2., nbins*binSize+binSize/2., binSize)*dt # Store data results.append(['event_counts', np.array(eventCounter)]) # Plot detected events self.show_events(data, np.array(xOnsets), np.array(yOnsets), dt) # Turn cursors off (the plot has been cleared so there are no cursors displayed) self.browser.ui.actionShowCursors.setChecked(False) plotWidget.cursor = False # Store results results.append(['time_bins', bins]) aux.save_results(browser, parentText+'_eventProbability', results)
def func(self, browser): """ Get some properties of currently plotted tracking trigger events extracted with behaviour_triggerEvents.py module Properties measured: 1 - failures to escape 2 - latency to escape 3 - time to reach the nest 4 - average speed of escape 5 - peak speed during escape Extracted events have useful attributes: t0 : data point of the trigger from the start of the event trigger_time : data point of the trigger from the start of the original tracking trace Options: 1) Threshold for the nest long-axis coordinate 2) Threshold for the position derivative (to measure response latency) """ ############################################ # ANALYSIS FUNCTION # Read options nestPos = float(self.plotWidget.nestCursorThsPos) derivativeThs = float(self.plotWidget.derivativeCursorThsPos) # Get plotted events events = self.plotWidget.plotDataItems # Go through events failures = [] latencies, timeToNest, avgSpeed, maxSpeed = [], [], [], [] comp = lambda a, b: a < b for event in events: # Get data range #data, c1 = aux.get_dataRange(plotWidget, event) # Mark failures depending on position reached nest if np.sum(event.data<nestPos)==0: failures.append(1) else: failures.append(0) # Get latency to reaction t0 = event.attrs['t0'] i = t0 # start from stimulus trigger dtrace = np.diff(event.data) while i<len(dtrace): if comp(dtrace[i], derivativeThs): latencies.append(i-t0) break i+=1 # Get latency to nest i = t0 while i<len(event.data): if comp(event.data[i], nestPos): timeToNest.append(i-t0) break i+=1 # Get other measures avgSpeed.append(np.mean(dtrace[t0:i])) maxSpeed.append(dtrace[t0:i].min()) # Store data results = [] results.append(['Failures', failures]) results.append(['Latencies', latencies]) results.append(['Time_to_nest', timeToNest]) results.append(['Average_speed', avgSpeed]) results.append(['Max_speed', maxSpeed]) aux.save_results(self.browser, 'Event_measurements', results)
def func(self, browser): """ Fit data traces within cursor-defined range Options: 1) Equation 2) Initial guesses """ ############################################ # ANALYSIS FUNCTION # Get widgets plotWidget = browser.ui.dataPlotsWidget toolsWidget = browser.ui.oneDimToolStackedWidget # Replot data to clear previous measure plot points pgplot.replot(browser, plotWidget) # Get parent text of plotted items try: parentText = plotWidget.plotDataItems[0].parent().text( 0) # Assumes all plotted data have the same parent except AttributeError: # Parent = None parentText = 'Data' # Get current function currentFuncmap = self.dataFit.fitfuncmap[str( self.comboBox.currentText())] # Read initial guesses pInit = [] try: nParam = len(currentFuncmap[2]) for n in range(nParam): pInit.append(float(self.p[n].text())) except ValueError: aux.error_box('Invalid value in initial guess') return # Fit data fitResults, fitTraces, fitTracesAttrs = [], [], [] for item in plotWidget.plotDataItems: # Get data range to fit dt = item.attrs['dt'] fitTracesAttrs.append(item.attrs) yData, c1, cx1, cx2 = aux.get_dataRange(plotWidget, item, cursors=True) xRange = pgplot.make_xvector(yData, dt) + cx1 self.dataFit.c1 = cx1 # Fit func = currentFuncmap[0] fitParams = self.dataFit.fit(func, xRange, yData, pInit) fitResults.append(fitParams) print fitParams # Plot fitted function over trace if self.extendBox.isChecked(): xRange = pgplot.make_xvector(item.data, dt) #xRange = np.arange(plotWidget.viewBox.viewRange()[0][0], plotWidget.viewBox.viewRange()[0][1], dt) fittedTrace = func(xRange, *fitParams) plotWidget.plot(xRange, fittedTrace, pen=pg.mkPen('r', width=1)) fitTraces.append(fittedTrace) # Store results if self.storeBox.isChecked(): fitResults = np.array(fitResults) results = [] for n in range(np.shape(fitResults)[1]): results.append([str(self.plabels[n].text()), fitResults[:, n]]) for n in range(len(fitTraces)): results.append( ['fitTraces_' + str(n), fitTraces[n], fitTracesAttrs[n]]) aux.save_results(browser, parentText + '_fit', results)
def func(self, browser): """ Generate a FI curve from current steps Options: 1) Threshold for detecting spikes """ ############################################ # ANALYSIS FUNCTION # Read detection options try: threshold = float(self.browser.ui.dataPlotsWidget.cursorThsPos) except NameError: aux.error_box('No threshold selected') return # Get widgets plotWidget = browser.ui.dataPlotsWidget toolsWidget = browser.ui.oneDimToolStackedWidget # Get parent text of plotted items try: parentText = plotWidget.plotDataItems[0].parent().text( 0) # Assumes all plotted data have the same parent except AttributeError: # Parent = None parentText = 'Data' # Detect APs in each trace comp = lambda a, b: a > b apNumber, apFrequency, results = [], [], [] plotWidget.clear() for item in plotWidget.plotDataItems: dt = item.attrs['dt'] data, c1 = aux.get_dataRange(plotWidget, item) apCounter, i = 0, 0 xOnsets, yOnsets = [], [] while i < len(data): if comp(data[i], threshold): xOnsets.append(i) yOnsets.append(data[i]) apCounter += 1 while i < len(data) and comp(data[i], threshold): i += 1 # skip values if index in bounds AND until the value is below/above threshold again else: i += 1 # Calculate AP frequency as 1/mean(ISI) isi = np.mean(np.diff(xOnsets)) * dt # in ms if isi > 0: freq = 1 / (isi / 1000.) else: freq = 0.0 # Store number of APs and mean frequency apNumber.append(apCounter) apFrequency.append(freq) # Plot detected APs self.show_events(data, np.array(xOnsets), np.array(yOnsets), dt) # Turn cursors off (the plot has been cleared so there are no cursors displayed) self.browser.ui.actionShowCursors.setChecked(False) plotWidget.cursor = False # Store results results.append(['AP_number', np.array(apNumber)]) results.append(['AP_frequency', np.array(apFrequency)]) aux.save_results(browser, parentText + '_FI', results)
def func(self, browser): """ Measure selected properties or statistics in the region defined by the data cursors. Rise time and onset are calculated by finding the peak and walking back until the limits are crossed. Onset limit is baseline mean; if no baseline has been set, it is taken as the mean value of the first 1 ms of data. Options: 1) create new entries in Working Data tree with the results """ ############################################ # ANALYSIS FUNCTION # Get widgets plotWidget = browser.ui.dataPlotsWidget toolsWidget = browser.ui.oneDimToolStackedWidget # Replot data to clear previous measure plot points pgplot.replot(browser, plotWidget) # Iterate through traces dataMin, dataMax, dataMean, dataMedian, dataSEM = [], [], [], [], [] dataRiseTime, dataOnset, dataDecay = [], [], [] for item in plotWidget.plotDataItems: # Get dt and data range dt = item.attrs['dt'] data, c1, cx1, cx2 = aux.get_dataRange(plotWidget, item, cursors=True) # Check baseline if 'baselineStart' in item.analysis: bsl = 0 # the mean of the baseline will always be 0 else: bsl = np.mean(data[0:round(1. / dt)]) # Measure selected parameters if self.minBox.isChecked(): y = np.min(data) x = np.argmin(data) dataMin.append(y) aux.plot_point(plotWidget, c1, x, y, dt) if self.maxBox.isChecked(): y = np.max(data) x = np.argmax(data) dataMax.append(y) aux.plot_point(plotWidget, c1, x, y, dt) if self.meanBox.isChecked(): y = np.mean(data) dataMean.append(y) plotWidget.plot([cx1, cx2], [y, y], pen=pg.mkPen('#CF1C04', width=1)) if self.medianBox.isChecked(): y = np.median(data) dataMedian.append(y) plotWidget.plot([cx1, cx2], [y, y], pen=pg.mkPen('#CF1C04', width=1)) if self.semBox.isChecked(): y = np.std(data) / np.sqrt(len(data)) dataSEM.append(y) plotWidget.plot([cx1, cx2], [y, y], pen=pg.mkPen('#CF1C04', width=1)) if self.riseTimeBox.isChecked(): try: lowerLimit = float(self.riseLowerLimit.text()) / 100 upperLimit = float(self.riseUpperLimit.text()) / 100 except ValueError: aux.error_box('Invalid limits') return # Get peak xPeak, yPeak = self.get_peak(data) # Get limits lowerLimit = lowerLimit * (yPeak - bsl) + bsl upperLimit = upperLimit * (yPeak - bsl) + bsl # Find limit crosses lowerCrossed, upperCrossed = False, False i = 0 while lowerCrossed == False or upperCrossed == False: if (upperCrossed == False) and self.comp( data[xPeak - i], upperLimit): xUpper = xPeak - i upperCrossed = True if self.comp(data[xPeak - i], lowerLimit): xLower = xPeak - i lowerCrossed = True i += 1 dataRiseTime.append((xUpper - xLower) * dt) # Plot points aux.plot_point(plotWidget, c1, xLower, lowerLimit, dt) aux.plot_point(plotWidget, c1, xUpper, upperLimit, dt) if self.onsetBox.isChecked(): xPeak, yPeak = self.get_peak(data) bslCrossed = False i = 0 while bslCrossed == False: if self.comp(data[xPeak - i], bsl): onset = xPeak - i bslCrossed = True i += 1 aux.plot_point(plotWidget, c1, onset, bsl, dt) dataOnset.append(onset * dt) if self.decayTimeBox.isChecked(): try: decayLimit = float(self.decayLimit.text()) / 100 except ValueError: aux.error_box('Invalid limits') return xPeak, yPeak = self.get_peak(data) yDecay = decayLimit * (yPeak - bsl) + bsl decayCrossed = False i = 0 while decayCrossed == False: if self.comp(data[xPeak + i], yDecay): decayTime = xPeak + i decayCrossed = True i += 1 aux.plot_point(plotWidget, c1, decayTime, yDecay, dt) dataDecay.append(decayTime * dt) # Store results results = [] if self.storeBox.isChecked(): if self.minBox.isChecked(): results.append(['Minimum', np.array(dataMin)]) if self.maxBox.isChecked(): results.append(['Maximum', np.array(dataMax)]) if self.meanBox.isChecked(): results.append(['Mean', np.array(dataMean)]) if self.medianBox.isChecked(): results.append(['Median', np.array(dataMedian)]) if self.semBox.isChecked(): results.append(['SEM', np.array(dataSEM)]) if self.riseTimeBox.isChecked(): results.append(['RiseTime', np.array(dataRiseTime)]) if self.onsetBox.isChecked(): results.append(['Onset', np.array(dataOnset)]) if self.decayTimeBox.isChecked(): results.append(['Decay', np.array(dataDecay)]) aux.save_results(browser, 'Measurements', results)
def func(self, browser): """ Filter traces Options: 1) Filter type (currently Bessel only) 2) Filter parameters Note: filter frequencies are in Hz, make sure dt is in seconds """ ############################################ # ANALYSIS FUNCTION # Get options btype = str(self.comboPass.currentText()) if 'Low' in btype: btype = 'low' elif 'High' in btype: btype = 'high' try: cutoff = float(self.filterCutoff.text()) order = float(self.filterOrder.text()) except ValueError: aux.error_box('Invalid Cut off or Order value') return bidir = self.filterDirection.isChecked() # Get widgets plotWidget = browser.ui.dataPlotsWidget toolsWidget = browser.ui.oneDimToolStackedWidget # Get parent text of plotted items try: parentText = plotWidget.plotDataItems[0].parent().text( 0) # Assumes all plotted data have the same parent except AttributeError: # Parent = None parentText = 'Data' # Filter data results, itemsToPlot = [], [] for item in plotWidget.plotDataItems: # Copy attributes and add some new ones attrs = item.attrs attrs['filter_type'] = btype attrs['filter_cutoff'] = cutoff attrs['filter_order'] = order # Filter traceFilter = acq4filter.besselFilter( item.data, cutoff, order, float(item.attrs['dt']) / 1000, btype, bidir) results.append([item.text(0), traceFilter, attrs]) # Store filtered traces filterItem = aux.make_h5item('filter', traceFilter, item.attrs) itemsToPlot.append(filterItem) # Plot results pgplot.plot_multipleData(browser, plotWidget, itemsToPlot, clear=False, color='#F2EF44') # Store results aux.save_results(browser, parentText + '_filter', results)
def func(self, browser): """ Calculate average trace from currently plotted traces. If cursors are selected the average is calculated for the range within the cursors. Options: 1) create new entry in Working Data tree with the result 2) plot average with orginal traces 3) caclulate SD and SEM """ ############################################ # ANALYSIS FUNCTION plotWidget = browser.ui.dataPlotsWidget toolsWidget = browser.ui.oneDimToolStackedWidget results = [] # Get data and cursors data = [] for item in plotWidget.plotDataItems: dt = item.attrs['dt'] dataRange, c1 = aux.get_dataRange(plotWidget, item) data.append(dataRange) data = np.array(data) # Calculate average and make h5item for plotting try: #avgData = np.nanmean(data[:, c1:c2], 0) avgData = np.mean(data, 0) avgItem = aux.make_h5item('avg', avgData, plotWidget.plotDataItems[0].attrs) results.append(['avg_trace', avgData, item.attrs]) except ValueError: aux.error_box('Cannot calculate average on data with different lengths', sys.exc_info(), 'Please ensure that all traces have the same length') return # Calculate SD and SEM if self.sdBox.isChecked(): #sdData = np.nanstd(data[:, c1:c2], 0) sdData = np.std(data, 0) avgPlusSDItem = aux.make_h5item('avg+sd', avgData+sdData, plotWidget.plotDataItems[0].attrs) avgMinusSDItem = aux.make_h5item('avg-sd', avgData-sdData, plotWidget.plotDataItems[0].attrs) results.append(['avg+sd', avgData+sdData, item.attrs]) results.append(['avg-sd', avgData-sdData, item.attrs]) if self.semBox.isChecked(): #semData = np.nanstd(data[:, c1:c2], 0)/np.sqrt(len(data)) semData = np.std(data, 0)/np.sqrt(len(data)) avgPlusSEMItem = aux.make_h5item('avg+sem', avgData+semData, plotWidget.plotDataItems[0].attrs) avgMinusSEMItem = aux.make_h5item('avg-sem', avgData-semData, plotWidget.plotDataItems[0].attrs) results.append(['avg+sem', avgData+semData, item.attrs]) results.append(['avg-sem', avgData-semData, item.attrs]) # Plot data if self.showTraces.isChecked(): clear = False else: clear = True pgplot.browse_singleData(browser, plotWidget, avgItem, clear=clear, color='r') # Store data if self.storeResult.isChecked(): aux.save_results(browser, item.parent().text(0)+'_average', results)