def combination_viz(length, animal, n=10, plot=False, save=False): """plot the probability of each combination of a given length INPUTS: length - (int) the combination length to plot animal - (str) the animal identifier n - (int) the number of combinations to plot [default=10] plot - (bool) whether to plot the data save - (bool) whether to save the data to a txt file""" conditions = {'NA': 0, 'LH': 1, 'KET': 2} # get all response sequences and their probabilities seq = responseSequences([animal], length) # sort the response sequences based on their summed probability of # occurence across all conditions sorted_seq = sorted(seq.items(), key=lambda x: sum(x[1]), reverse=True) # response sequence names seq_names = [sequence[0] for sequence in sorted_seq[:n]] if plot: fig, ax = plt.subplots(1, 1, figsize=(15, 8)) for condition in conditions.keys(): # sequence probabilities for the given condition seq_vals = [ sequence[1][conditions[condition]] for sequence in sorted_seq[:n] ] plt.plot(seq_names, seq_vals, label=condition) simpleAxis(ax) plt.title(animal) plt.ylim(0, 0.8) plt.ylabel('Probability') plt.xlabel('Response sequence') y = [round(i, 2) for i in np.arange(0, 0.81, 0.2)] plt.yticks(y, [str(i) for i in y]) plt.xticks(rotation=45) plt.legend() plt.tight_layout() plt.savefig('_'.join((animal, str(length), 'sequenceCombinations'))) plt.close(fig) if save: fname = '_'.join( (animal, 'length' + str(length), 'sequenceCombinations')) with open(fname + '.txt', 'w') as f: f.write(json.dumps(sorted_seq[:n]))
def seq_viz(length, animal, condition, saveFig=False, showFig=False): """plot response sequences - arrow format INPUTS: length - (int) the sequence length to consider animal - (str) animal identifier condition - (str) condition of interest saveFig - (bool) whether to save the figure showFig - (bool) whether to display the figure""" responses = ['Premature', 'Failure', 'Escape'] positions = [str(i) for i in range(2, length + 1, 1)] conditions = {'NA': 0, 'LH': 1, 'KET': 2} # get data seq = responseSequences([animal], length) x = [i for i in range(1, length, 1)] y = {'Premature': 0.5, 'Failure': 2.5, 'Escape': 4.5} arr_color = 'k' ax = plt.subplot(111) plt.xlim(0, length - 0.8) plt.ylim(0, 5) plt.xlabel('Response position') plt.xticks(x, positions) plt.yticks(list(y.values()), responses) for item in seq.keys(): # get sequence of responses res_sequence = item.split('_') # get response pairs pairs = [res_sequence[i:i + 2] for i in range(len(res_sequence) - 1)] for position, pair in enumerate(pairs): alpha = seq[item][conditions[condition]] if position != (len(pairs) - 1): plt.plot([x[position] - 1, x[position]], [y[pair[0]], y[pair[1]]], alpha=alpha, color=arr_color) else: styles = ('simple, tail_width=' + str(5 * alpha) + ', head_width=10, head_length=12') kw = {'arrowstyle': styles, 'color': arr_color, 'alpha': alpha} a = patches.FancyArrowPatch((x[position] - 1, y[pair[0]]), (x[position], y[pair[1]]), **kw) ax.add_patch(a) simpleAxis(ax) plt.title('_'.join((animal, condition))) colors = plt.cm.ScalarMappable(cmap='Greys') colors.set_array([0, 1]) plt.colorbar(colors, ticks=[0, 0.5, 1], shrink=0.5) plt.tight_layout() if saveFig: plt.savefig('_'.join((animal, condition, 'responseSequences'))) if not showFig: plt.close() if showFig: plt.show()
def plotFeatures(plot=False, saveData=False): """compute time to peak and trough of each animal's mean trace as well as positive and negative AUC for figure S2 B INPUTS: plot - (bool) whether to plot the features saveData - (bool) whether to save the data""" conditions = ['NA', 'LH', 'KET', 'STR_LH', 'STR_KET'] responses = ['escape', 'failure'] animals = ['B1', 'B2', 'B3', 'B4', 'B7', 'B8', 'B9', 'B10', 'B11'] trialLength = 15 samplingRate = 250 # Hz nFeatures = 5 features = {feature: {animal: {res: [] for res in responses} for animal in animals} for feature in range(nFeatures)} for condition in conditions: for animal in animals: data = {res: [] for res in responses} # get data rawData = {res: photometry.windowEscData(animal, condition)[res] for res in responses} # only keep trials of correct length for res in responses: for trial in rawData[res]: if len(trial[1]) == samplingRate*trialLength: data[res].append(trial[1]) # stack all the data in a np array for res in responses: # take the mean of all traces from the given behavioral # response mean = np.mean(np.vstack(data[res]), axis=0) # compute peak features[0][animal][res].append(max(mean)) # compute time to peak indx = np.where(mean == max(mean)) features[1][animal][res].append( (indx[0][0] / samplingRate) - 5) # compute time to trough indx = np.where(mean == min(mean[:12*samplingRate])) features[2][animal][res].append( (indx[0][0] / samplingRate) - 5) # compute auc for trough meanB4 = np.mean(mean[:5*samplingRate]) temp = [point for point in mean[5*samplingRate:12*samplingRate] if point < meanB4] features[3][animal][res].append(np.trapz(temp)) # compute auc for peak temp = [point for point in mean[5*samplingRate:] if point > meanB4] features[4][animal][res].append(np.trapz(temp)) if saveData: with open('features.txt', 'w') as f: f.write(json.dumps(features)) if plot: feature = 0 x = [0, 1, 2] fig, axs = plt.subplots(2, 4) for col in range(4): for row, res in enumerate(responses): for animal in animals: axs[row, col].plot(x, features[feature][animal][res]) axs[row, col].set_xticks(x, ['NA', 'LH', 'KET']) simpleAxis(axs[row, col], displayX=1) feature += 1 plt.savefig('features_separate') plt.close(fig) fig, axs = plt.subplots(1, 4) for col in range(4): for animal in animals: axs[col].plot(x, abs(np.array(features[col][animal]['escape']) - np.array(features[col][animal]['failure']))) axs[col].set_xticks(x, ['NA', 'LH', 'KET']) simpleAxis(axs[col], displayX=1) plt.savefig('features') plt.close(fig)
def traceDistances(plot=False, saveDistances=False, saveMeanSEM=False, saveNtrials=False, returnDistances=False): """function to compute the distance between a given animal's mean escape and failure traces. Saves a plot as a tif and the raw data to a txt for Figure 2G INPUTS: plot - (bool) whether to plot the data saveDistances - (bool) whether to save the distances saveMeanSEM - (bool) whether to save the mean and sem saveNtrials - (bool) whether to save the number of trials returnDistances - (bool) whether to return the distances""" conditions = ['NA', 'LH', 'KET'] responses = ['escape', 'failure'] animals = ['B1', 'B2', 'B3', 'B4', 'B7', 'B8', 'B9', 'B10', 'B11', 'B12', 'B13', 'B14'] trialLength = 15 samplingRate = 250 # Hz distance = {animal: [] for animal in animals} if saveMeanSEM: meanSEM = {animal: {res: {} for res in responses} for animal in animals} if saveNtrials: nTrials = {animal: {res: [] for res in responses} for animal in animals} for condition in conditions: for animal in animals: # get data data = {res: photometry.windowEscData(animal, condition)[res] for res in responses} # only keep trials of correct length for res in responses: data[res] = [trial[1] for trial in data[res] if len(trial[1]) == samplingRate*trialLength] # stack all the data in a np array for res in responses: data[res] = np.vstack(data[res]) # take the mean of all traces from each behavioral response escapeMean = np.mean(data['escape'], axis=0) failureMean = np.mean(data['failure'], axis=0) # compute some similarity measure between these means distance[animal].append( round(np.linalg.norm(escapeMean - failureMean), 2)) if saveMeanSEM: for mean, res in zip([escapeMean, failureMean], responses): meanSEM[animal][res]['mean'] = mean.tolist() meanSEM[animal][res]['SEM'] = ( sstats.sem(data[res], axis=0).tolist()) if saveNtrials: for res in responses: nTrials[animal][res] = len(data[res]) if saveMeanSEM: with open('meanSEM_'+condition+'.txt', 'w') as f: f.write(json.dumps(meanSEM)) if saveNtrials: with open('nTrials_'+condition+'.txt', 'w') as f: f.write(json.dumps(nTrials)) if plot: # plot distance x = [0, 1, 2] plt.rcParams['figure.figsize'] = [3, 6] fig, ax = plt.subplots(1, 1) for animal in animals: ax.plot([0, 1, 2], distance[animal], label=animal) plt.xticks(x, ['NA', 'LH', 'KET']) plt.ylabel('Escape & failure distance') plt.legend() simpleAxis(ax, displayX=1) plt.savefig('traceDistances') plt.close(fig) if saveDistances: # save distances to text file with open('traceDistances.txt', 'w') as f: f.write(json.dumps(distance)) if returnDistances: return distance
def learningCurves(strongToo=False, together=False, plot=False, saveData=False, returnLearning=False, esc10=False): """function to plot learning curves for Figure 2F INPUTS: strongToo - (bool) whether to include strongLH and strongKET data together - (bool) whether to plot all the animals' learning curves together in one plot plot - (bool) whether to plot and save the learning curves saveData - (bool) whether to save the learning data in a text file returnLearning - (bool) whether to return the data esc10 - (bool) whether the data is from 10s long escapable shocks """ animals = ['B2', 'B3', 'B4', 'B7', 'B8', 'B9', 'B10', 'B11', 'B12', 'B13', 'B14'] if esc10: animals = ['A2', 'A3', 'A7', 'A9', 'A10', 'A11', 'A12'] conditions = ['NA', 'LH', 'KET'] if strongToo: conditions += ['STR_LH', 'STR_KET'] animals.remove('B8') if together: # initialize a list for learning curves from all animals allCurves = [] for animal in animals: # keep a list of condition lengths for the x ticks lengths = [] # list for learning curve y = [] for c in conditions: # get responses responses = behavior.responseExtract(animal, c, esc10=esc10) if c == 'KET' and animal in ['B1', 'B2', 'B3', 'B4']: responses = responses[:-100] elif c == 'NA' and animal in ['B7', 'B8']: responses = responses[100:] for r in responses: if len(y) == 0: if r == 'Escape': y.append(1) elif r == 'Failure': y.append(-1) else: y.append(0) else: if r == 'Escape': y.append(y[-1] + 1) elif r == 'Failure': y.append(y[-1] - 1) else: y.append(y[-1] + 0) lengths.append(len(y)) if not together and plot: label = [c+'\n'+str(cl) for c, cl in zip(conditions, lengths)] fig, ax = plt.subplots(1, 1) ax.plot(y) plt.xticks(lengths, label) plt.xlim((0, lengths[-1])) plt.yticks(range(-40, 81, 20)) simpleAxis(ax, displayX=1) plt.title(animal) plt.savefig(animal+'_Learning') if together: allCurves.append(y) if together: if plot: # plot curves together label = [c+'\n'+str(cl) for c, cl in zip(conditions, lengths)] fig, ax = plt.subplots(1, 1) for curve, animal in zip(allCurves, animals): ax.plot(curve, label=animal) plt.xticks(lengths, label) plt.xlim((0, lengths[-1])) plt.legend() simpleAxis(ax, displayX=1) plt.savefig('all_Learning_together') if saveData: # save learning curves to text file with open('learningCurves.txt', 'w') as f: f.write(json.dumps(list(zip(animals, allCurves)))) f.write(json.dumps(lengths)) if returnLearning: return {animal: curve for animal, curve in zip(animals, allCurves)}
def avgTraces(animals, condition, alignType, samplingRate, donut=False, sepDonut=False, showPlot=False, savePlot=False, saveSEM=False, esc10=False, revision_saline=False, revision_control=False, revision_saline_daily=False): """ function to plot the average traces for the 3 behavioral responses (Figure 2D) INPUTS: animals- (list) animal identifiers given as strings condition - (str) behavioral condition of interest alignType - (str) whether to align the traces to shock start or end. This is set by passing 'beginning' or 'end'. samplingRate - (int) data sampling rate in Hz donut - (bool) whether to display a donut plot with percent of each behavioral repsonse on avgTrace plot sepDonut - (bool) whether to save a separate donut plot with percent of each behavioral repsonse showPlot - (bool) whether to show the plot savePlot - (bool) whether to save the plot saveSEM - (bool) whether to save the mean +/- SEM to text files esc10 - (bool) whether the data is from 10s long escapable shocks revision_saline - (bool) denoting whether to use the revision saline data revision_saline_daily - (bool) denoting whether to use the revision saline daily data revision_control - (bool) denoting whether to use the revision control data""" # get windowed data windowedData = [photometry.windowEscData(animal, condition, alignType, esc10=esc10, revision_saline=revision_saline, revision_control=revision_control, revision_saline_daily=revision_saline_daily) for animal in animals] # initialize list for the escape traces escapeTraces, failureTraces = [], [] # initialize lists for escape shock lengths # To be used for color gradient in figure escapeShockLengths = [] if not esc10: noResponse = 'NoResponse' else: noResponse = 'No Response' # split escape, no escape (failure), and premature data into separate lists for animalData in windowedData: for trial in animalData['escape']: # check whether shock length is > 0 if trial[2] > 0 and trial[0] == 'Escape': escapeTraces.append(ssig.medfilt(trial[1], kernel_size=3)) escapeShockLengths.append(trial[2]) for trial in animalData['failure']: # check whether shock length is > 0 if trial[2] > 0 and trial[0] == noResponse: failureTraces.append(ssig.medfilt(trial[1], kernel_size=3)) prematureTraces = [trial[1] for trial in animalData['premature'] for animalData in windowedData] # initialize numpy arrays for average and SEMs of escape and failure # escape and failure trial lengths trialLengths = max(set([trace.shape[0] for trace in escapeTraces])) escapeAvg = np.zeros((trialLengths)) escapeSEM = np.zeros(escapeAvg.shape) failureAvg = np.zeros((trialLengths)) failureSEM = np.zeros(failureAvg.shape) # collect the dff values from a given timepoint for all traces for timeStep in range(trialLengths): temp = [] for trial in escapeTraces: if trial.shape[0] == trialLengths: temp.append(trial[timeStep]) if temp: # compute the mean and variance of the dff values escapeAvg[timeStep] = np.mean(temp) escapeSEM[timeStep] = sstats.sem(temp) temp = [] # redo above for the failure trials for trial in failureTraces: if trial.shape[0] == trialLengths: temp.append(trial[timeStep]) if temp: failureAvg[timeStep] = np.mean(temp) failureSEM[timeStep] = sstats.sem(temp) if saveSEM: fname = 'meanSEM_' + condition + '.txt' if len(animals) == 1: fname = '_'.join((animals[0], fname)) with open(fname, 'w') as f: f.write('escape mean') f.write('\n') f.write(json.dumps(escapeAvg.tolist())) f.write('\n') f.write('escape SEM') f.write('\n') f.write(json.dumps(escapeSEM.tolist())) f.write('\n') f.write('failure mean') f.write('\n') f.write(json.dumps(failureAvg.tolist())) f.write('\n') f.write('failure SEM') f.write('\n') f.write(json.dumps(failureSEM.tolist())) if sepDonut: escapeColor = '#7570b3' failureColor = '#d95f02' plt.figure() plt.pie([len(escapeTraces), len(failureTraces)], colors=[escapeColor, failureColor], autopct='%1.0f%%', textprops=dict(color='k', fontsize=8), startangle=90, pctdistance=1.4, wedgeprops=dict(width=0.4, edgecolor='w')) plt.axis('equal') if len(animals) == 1: plt.savefig('_'.join((animals[0], condition, 'donut'))) else: plt.savefig(condition+'_donut') plt.close() # PLOT if showPlot or savePlot: # bin escape shock lengths for gradient of shock depiction bins = collections.Counter(escapeShockLengths) # update each bin count to be itself + the sum of all bins with # greater keys for key in bins.keys(): for key2 in bins.keys(): if key2 > key: bins[key] += bins[key2] # define y axis limits based on whether plotting multiple animals if len(animals) > 1: yPos = (-0.2, 0.201) else: yPos = (-0.2, 0.4) # ystep size yStep = 0.2 # for each bin we need a tuple with the 1st value: the x value # for the left border of the rectangle 2nd value: rectangle width if alignType == 'beginning': rects = [(5, key) for key in bins.keys()] leftX = 5 elif alignType == 'end': rects = [(5-key, key) for key in bins.keys()] leftX = 2 # set the max alpha value alphaCeil = 0.3 # set the bin with the most counts as the max alpha val maxCounts = max(bins.values())/alphaCeil # set the transparency values for each bin alphas = [val/maxCounts for val in bins.values()] # make array for x axes in seconds sec = np.linspace(0, trialLengths/samplingRate, trialLengths) # change figure size plt.rcParams['figure.figsize'] = [4, 11] # plot escape and failure avg traces fig, (ax1, ax2, ax3) = plt.subplots(3, 1, sharex=True, sharey=True) # overall figure calls plt.xlim((0, trialLengths/samplingRate)) plt.ylim(yPos[0], yPos[1]) fig.suptitle(str(animals)+'_'+condition) # graph colors escapeColor = '#7570b3' failureColor = '#d95f02' shockColor = '#A9A9A9' # set SEM transparency semAlpha = 0.4 # plot escape traces ax1.plot(sec, escapeAvg, color=escapeColor) ax1.set(title='Average of escape traces', ylabel='DF/F') ax1.fill_between(sec, escapeAvg+escapeSEM, escapeAvg-escapeSEM, color=escapeColor, alpha=semAlpha, linewidth=0.01) for i, rect in enumerate(rects): r = Rectangle((rect[0], yPos[0]), rect[1], yPos[1]-yPos[0], alpha=alphas[i], color=shockColor) ax1.add_patch(r) ax1.annotate('n = ' + str(len(escapeTraces)), xy=(12, 0.18)) ax1.set_yticks(np.arange(yPos[0], yPos[1], yStep)) ax1.set_yticks(np.arange(yPos[0], yPos[1], yStep)) simpleAxis(ax1, 0) # plot failure traces ax2.plot(sec, failureAvg, color=failureColor) ax2.set(title='Average of failure traces', ylabel='DF/F') ax2.fill_between(sec, failureAvg+failureSEM, failureAvg-failureSEM, color=failureColor, alpha=semAlpha, linewidth=0.01) r = Rectangle((leftX, yPos[0]), 3, yPos[1]-yPos[0], alpha=alphaCeil, color=shockColor) ax2.add_patch(r) ax2.annotate('n = '+str(len(failureTraces)), xy=(12, 0.18)) ax1.set_yticks(np.arange(yPos[0], yPos[1], yStep)) ax2.set_yticks(np.arange(yPos[0], yPos[1], yStep)) simpleAxis(ax2, 0) # plot escape and failure together ax3.plot(sec, escapeAvg, color=escapeColor) ax3.fill_between(sec, escapeAvg+escapeSEM, escapeAvg-escapeSEM, color=escapeColor, alpha=semAlpha, linewidth=0.01) ax3.plot(sec, failureAvg, color=failureColor) ax3.fill_between(sec, failureAvg+failureSEM, failureAvg-failureSEM, color=failureColor, alpha=semAlpha, linewidth=0.01) simpleAxis(ax3, 1) ax3.set_xticks(np.arange(0, 16, 5)) ax3.set_xticklabels([str(val) for val in list(np.arange(0, 16, 5))]) ax3.set_yticks(np.arange(yPos[0], yPos[1], yStep)) if donut: # add donut plot to top left of graph to depict proportion of # trials in each condition inset = insLoc.inset_axes(ax3, width='20%', height='20%', loc='upper left') inset.pie([len(escapeTraces), len(failureTraces)], colors=[escapeColor, failureColor], autopct='%1.0f%%', textprops=dict(color='k', fontsize=8), startangle=90, pctdistance=1.4, wedgeprops=dict(width=0.4, edgecolor='w')) inset.axis('equal') # save and/or display plot based on function input if savePlot and len(animals) > 1: plt.savefig('all_'+condition+'_'+alignType + '_filtered_avgTraces.tif') plt.close(fig) elif savePlot: plt.savefig((animals[0]+'_'+condition+'_'+alignType + '_filtered_avgTraces.tif')) plt.close(fig) if showPlot: plt.show()
def dffTransitions(condition, animals, window=5, plot=False, saveData=False): """get the calcium transient data around each movement transition (start -> stop and stop -> start) INPUTS: condition - (str) the experimental condition animal - (str) the animal identifier window - (int) the length of calcium transients, in seconds, to plot around each transition plot - (bool) whether to plot the windowed calcium transient data saveData - (bool) whether to save the calcium transient data to a text file OUTPUTS: either figures or a text file with the average and SEM Ca2+ transients around each motion onset/offset and the number of motion onsets/offsets.""" # initialize lists for start and stop dff data startDff, stopDff = [], [] for animal in animals: # get the transitions from the locomotion data startTimes, stopTimes = transitionExtract(condition, animal) # get calcium transient data dffData = dffExtract(condition, animal) # sampling rate of dff data (Hz) dffRate = 250 # get dffData +/-window seconds around each transition # for transitions from not moving to moving for val in startTimes: # position of current transition in dff samples dffPos = val * dffRate # get +/- window seconds around the transition data = dffData[int(dffPos - (window * dffRate)):int(dffPos + (window * dffRate))] # only append data of correct length if len(data) == dffRate * 2 * window: startDff.append(data) for val in stopTimes: # position of current transition in dff samples dffPos = val * dffRate # get +/-window seconds around the transition data = dffData[int(dffPos - (window * dffRate)):int(dffPos + (window * dffRate))] # only append data of correct length if len(data) == dffRate * 2 * window: stopDff.append(data) # get the mean of all dff data at each timepoint startDffMean = [np.mean(timepoint) for timepoint in zip(*startDff)] stopDffMean = [np.mean(timepoint) for timepoint in zip(*stopDff)] # get the SEM of all dff data at each timepoint startDffSEM = [sstats.sem(timepoint) for timepoint in zip(*startDff)] stopDffSEM = [sstats.sem(timepoint) for timepoint in zip(*stopDff)] if saveData: with open('locomotionData_' + condition + '.txt', 'w') as f: f.write( json.dumps({ 'startDffMean': startDffMean, 'startDffSEM': startDffSEM, 'stopDffMean': stopDffMean, 'stopDffSEM': stopDffSEM, 'nStart': len(startDff), 'nStop': len(stopDff) })) if plot: # convert to numpy arrays to make plotting the SEM easier startDffMean = np.array(startDffMean) startDffSEM = np.array(startDffSEM) stopDffMean = np.array(stopDffMean) stopDffSEM = np.array(stopDffSEM) # plot calcium transients aligned to motion start times # make a list for the x axes sec = np.linspace(0, window * 2, startDffMean.shape[0]) fig, ax = plt.subplots() plt.plot(sec, startDffMean) plt.xlim((0, window * 2)) plt.fill_between(sec, startDffMean + startDffSEM, startDffMean - startDffSEM, alpha=0.4, linewidth=0.01) ax.set(title='Calcium transients aligned to motion start times', xlabel='Time (s)', ylabel='DF/F') ax.annotate('n = ' + str(len(startDff)), xy=(8.5, 0.09)) simpleAxis(ax, displayX=1) plt.savefig('start_locomotion') plt.close(fig) # plot calcium transients aligned to motion stop times # make a list for the x axes sec = np.linspace(0, window * 2, stopDffMean.shape[0]) fig, ax = plt.subplots() plt.plot(sec, stopDffMean) plt.xlim((0, window * 2)) plt.fill_between(sec, stopDffMean + stopDffSEM, stopDffMean - stopDffSEM, alpha=0.4, linewidth=0.01) ax.set(title='Calcium transients aligned to motion stop times', xlabel='Time (s)', ylabel='DF/F') ax.annotate('n = ' + str(len(stopDff)), xy=(8.8, 0.09)) simpleAxis(ax, displayX=1) plt.savefig('stop_locomotion') plt.close(fig)