コード例 #1
0
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]))
コード例 #2
0
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()
コード例 #3
0
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)
コード例 #4
0
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
コード例 #5
0
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)}
コード例 #6
0
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()
コード例 #7
0
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)