예제 #1
0
def plot_bootstrap_distributions(exp):
    """ Plots the bootstrap toi power distributions for each stimulation
    condition and frequency band.

    Args:
        exp: The experiment to collect data for. 'main' or 'saline'

    Returns:
        A 2 x 3 matplotlib figure. Each subplot contains the bootstrap
        distribution for a particular condition and frequency band.
        Additionally, the estimated toi power and bootstrap 95% CI are
        plotted as vertical lines.
    """

    with open('./experiment_config.json', 'r') as f:
        config = json.load(f)

    sns.set(style="white",
            font_scale=config['font_scale'],
            rc={"lines.linewidth": config['linewidth']})

    (fig, axs) = plt.subplots(2, 3, figsize=(20, 12))
    plt.subplots_adjust(hspace=0.4, wspace=0.2)

    for i, condition in enumerate(config['conditions']):

        f = '../data/stats/%s_experiment/%s_bootstrap_info.npz' % (exp,
                                                                   condition)
        bootstrap_info = np.load(f)

        for j, band in enumerate(['alpha', 'beta']):
            dist = bootstrap_info['%s_dist' % band]
            power = bootstrap_info['%s' % band]
            p = bootstrap_info['%s_p' % band]

            # reduce to toi power
            times = bootstrap_info['times']
            dist = np.sort(
                reduce_toi_power(dist, times, config['toi'], axis=-1))
            power = reduce_toi_power(power, times, config['toi'], axis=-1)

            # extract 95% confidence interval
            lower_ix = int(len(dist) * .025)
            upper_ix = int(len(dist) * .975)
            ci = [dist[lower_ix], dist[upper_ix]]

            # plot bootstrap distribution with actual value and ci marked
            ax = axs[j, i]
            sns.distplot(dist, ax=ax, color=config['colors'][i])
            ax.axvline(ci[0], color='k')
            ax.axvline(ci[1], color='k')
            ax.axvline(power, color=config['colors'][i], linewidth=2)
            title = '%s %s Bootstrap Distribution \n Uncorrected p = %.3f'
            ax.set_title(title % (condition, band, p))

    plt.tight_layout()
    sns.despine()
    return fig
예제 #2
0
def compute_bootstrap_p_value(power, bootstrap_dist, times, toi):
    """ Computes a bootstrap p-value to test the null hypothesis that
        post stimuation band power within a time period of interest is equal
        to zero.

        Checks to see how unlikely the given post-stimulation band power
       within a time period of interest is, given a bootstrapped null
        distribution that we shift to center around 0. It does this by
        calculating the % of bootstrapped values that are more extreme than
        the calculated value.

        Args:
            power: The time series consisting of the non-bootstrapped band
                power.
            bootstrap_dist: The bootstrap distribution of band power.
            times: List of time labels.
            toi: Tuple containing the limits for the time period of interest.

        Returns:
            Returns the p-value (float) corresponding to the percentage of
            bootstrapped toi values that were more extreme than the estimated
            toi band power value.
    """
    # reduce to toi
    bootstrap_dist = reduce_toi_power(bootstrap_dist, times, toi, axis=-1)
    power = reduce_toi_power(power, times, toi, axis=-1)

    # sort by toi value
    bootstrap_dist = np.sort(bootstrap_dist)

    # center the distribution to make it a "null distribution"
    # assumes symmetry of the distribution
    bootstrap_dist = bootstrap_dist - power

    # compute the p-value as the percentage of bootstrap values larger
    # in absolute value than the
    p_num = np.sum(np.abs(bootstrap_dist) >= np.abs(power)) + 1.
    p_denom = len(bootstrap_dist) + 1.

    return p_num / p_denom
예제 #3
0
def plot_early_vs_late_stim_spectra(exp):
    """ Plots the spectra (averaged TFR power) for the first 5 seconds of the
    stimulation period compared to last 5 seconds of the stimulation period.

    Inputs:
    - exp: main or saline indicating which experiment's data to load and plot

    Outputs:
    - fig: 1 x 3 plot where each plot contains the first and last 5 seconds
    of stimulation spectra for each condition.
    """

    with open('./experiment_config.json', 'r') as f:
        config = json.load(f)

    sns.set(style="white",
            font_scale=config['font_scale'],
            rc={"lines.linewidth": config['font_scale']})

    indices = {'Early': (0, 5), 'Late': (5, 10)}
    linestyles = ['-', '--']

    fig, axs = plt.subplots(1, 3, figsize=(24, 8))

    for i, condition in enumerate(config['conditions']):

        ax = axs[i]
        power, chs, times, freqs = load_power_data(exp, condition)

        # average over trials
        power = power.mean(axis=0)

        # average over array1
        power = reduce_array_power(power, chs, config['%s_bad_chs' % exp], '1',
                                   0)

        for j, tp in enumerate(['Early', 'Late']):

            # reduce to early or late stim toi
            toi_power = reduce_toi_power(power, times, indices[tp], axis=-1)

            # normalize the spectra
            toi_power /= toi_power.sum()

            # plot the spectra
            ax.plot(freqs,
                    toi_power,
                    color=config['colors'][i],
                    linestyle=linestyles[j])

        # pretty axes
        ax.set_title(condition)
        ax.set_xlabel('Frequency [Hz]')
        ax.set_ylabel('Normalized Power')

    # add legend
    axs[-1].legend(['Early', 'Late'])
    leg = axs[-1].get_legend()
    leg.legendHandles[0].set_color('black')
    leg.legendHandles[1].set_color('black')

    plt.tight_layout()
    sns.despine()

    return fig
예제 #4
0
def plot_before_during_after_spectra(exp):
    """ Plots the power spectrum for the 0.5 seconds immediately
    pre-stimulation, the stimulation period, and the 0.5 seconds immediately
    post-stimulation.

    Args:
        exp: The experiment to collect data for. 'main' or 'saline'

    Returns:
        A 1 x 3 matplotlib figure. Each subplot contains the normalized by sum
        of power spectrum for each condition for a period before, during,
        and after stimulation. Shading is bootstrap standard error.
    """

    with open('./experiment_config.json', 'r') as f:
        config = json.load(f)

    sns.set(style='white',
            font_scale=config['font_scale'],
            rc={"lines.linewidth": config['linewidth']})

    fig, axs = plt.subplots(1, 3, figsize=(24, 8))

    for i, time_period in enumerate(['Before', 'During', 'After']):

        ax = axs[i]

        for j, condition in enumerate(config['conditions']):

            power, chs, times, freqs = load_power_data(exp, condition)

            power = reduce_array_power(power,
                                       chs,
                                       config['%s_bad_chs' % exp],
                                       '1',
                                       axis=1)

            power = reduce_toi_power(power,
                                     times,
                                     config[time_period],
                                     axis=-1)

            bootstrap_dist = simple_bootstrap(power, axis=0)

            # reduce over trials
            power = power.mean(axis=0)
            bootstrap_dist = bootstrap_dist.mean(axis=1)

            # normalize spectra
            power /= power.sum()
            bootstrap_dist /= bootstrap_dist.sum(axis=-1)[:, np.newaxis]

            # extract bootstrap standard error
            bootstrap_std_err = bootstrap_dist.std(axis=0)

            # plot the spectra with standard error shading
            ax.plot(freqs, power, color=config['colors'][j])
            ax.fill_between(freqs,
                            power - bootstrap_std_err,
                            power + bootstrap_std_err,
                            color=config['colors'][j],
                            alpha=0.5,
                            label='_nolegend_')

        ax.set_title('%s Stimulation Power' % time_period)
        ax.set_xlabel('Frequency [Hz]')
        ax.set_ylabel('Normalized Power')
        ax.set_ylim((0, 0.5))

    axs[-1].legend(config['conditions'])
    plt.tight_layout()
    sns.despine()

    return fig
예제 #5
0
def plot_array_toi_comparison(exp):
    """ Plots the pre- and post-stimulation alpha and beta time of interest
    averages for all three conditions comparing the two arrays.

    Args:
        exp: The experiment to collect data for. 'main' or 'saline'

    Returns:
        A 1 x 2 matplotlib figure. Each subplot contains alpha and beta band
        toi average barplots for all three conditions split by recording array
        with bootstrap standard error bars and significance marking between
        array averages based on permutation testing.
    """

    with open('./experiment_config.json', 'r') as f:
        config = json.load(f)

    # plotting initialization
    sns.set(style='white',
            font_scale=config['font_scale'],
            rc={"lines.linewidth": config['linewidth']})
    fig, axs = plt.subplots(1, 2, figsize=(22, 10))
    plt.subplots_adjust(hspace=.3)

    stat_ys = [-.3, .15, -.4, -.2, .15, -.35]
    stat_hmults = [3, 1.5, 3, 3, 1.5, 3]
    stat_hs = [-.03, .02, -.03, -.03, .02, -.03]

    for i, c in enumerate(config['conditions']):

        f = '../data/stats/%s_experiment/%s_array_permutation_info.npz'
        perm_info = np.load(f % (exp, c))

        power, chs, times, freqs = load_power_data(exp, c)

        power = baseline_normalize(power, config['baseline'], times)

        # array indices
        arr1_ix = [
            ix for ix in np.arange(len(chs))
            if 'elec1' in chs[ix] and chs[ix] not in config['%s_bad_chs' % exp]
        ]
        arr2_ix = [ix for ix in np.arange(len(chs)) if 'elec2' in chs[ix]]

        for j, band in enumerate(['alpha', 'beta']):

            band_power = reduce_band_power(power, freqs, config[band], axis=1)
            toi_power = reduce_toi_power(band_power,
                                         times,
                                         config['toi'],
                                         axis=-1)

            for k, arr in enumerate([arr1_ix, arr2_ix]):
                arr_power = toi_power[arr].mean(axis=0)

                dist = np.sort(
                    simple_bootstrap(toi_power[arr][:, np.newaxis],
                                     axis=0).squeeze().mean(axis=0))
                lower_ix = int(len(dist) * .025)
                upper_ix = int(len(dist) * .975)
                ci = [dist[lower_ix], dist[upper_ix]]

                bar_tick = i * 2 + k * .8

                if k == 0:
                    axs[j].bar(bar_tick, arr_power, color=config['colors'][i])
                    axs[j].plot([bar_tick + .4, bar_tick + .4],
                                ci,
                                color='k',
                                label='_nolegend_')
                else:
                    axs[j].bar(bar_tick,
                               arr_power,
                               facecolor='none',
                               edgecolor=config['colors'][i],
                               linewidth=4,
                               hatch='/')
                    axs[j].plot([bar_tick + .4, bar_tick + .4],
                                ci,
                                color='k',
                                label='_nolegend_')

            # pretty axis
            axs[j].set_title('%s Power' % band.capitalize(), y=1.05)
            axs[j].set_xticks([x + .8 for x in [0, 2, 4]])
            axs[j].set_xticklabels(config['conditions'])
            axs[j].set_ylim((-.7, .7))
            axs[j].set_xlim((-.6, 6.4))
            axs[j].set_ylabel('dB Change From Baseline')
            axs[j].axhline(0, color='k', label='_nolegend_')

            # statistical annotation
            p = perm_info['%s_p_value' % band]
            if p < .0002:
                p = 'p < .0002'
            else:
                p = 'p = %.04f' % p
            x1, x2 = i * 2 + .4, i * 2 + 1.2
            y = stat_ys[j * 3 + i]
            hmult = stat_hmults[j * 3 + i]
            h = stat_hs[j * 3 + i]
            axs[j].plot([x1, x1, x2, x2], [y, y + h, y + h, y],
                        lw=2.5,
                        c='k',
                        label='_nolegend_')
            axs[j].text((x1 + x2) * .5,
                        y + hmult * h,
                        p,
                        ha='center',
                        va='bottom',
                        color='k',
                        size=22)

    # set legend
    axs[1].legend(["Array 1", "Array 2"])
    leg = axs[1].get_legend()
    leg.legendHandles[0].set_color('black')
    leg.legendHandles[1].set_edgecolor('black')

    plt.tight_layout()
    sns.despine()
    return fig
예제 #6
0
def compute_array_permutation_distribution(exp):
    """ Computes permutation distributions for alpha and beta band power
    difference between the two recording arrays for all three conditions.

    For each condition, it loads those condition's raw tfr power data. It
    then baseline normalizes the power and reduces to band power and averages
    over a post-stimulation time of interest. Then it permutes recording
    array membership to generate a permutation distribution of differences
    between recording array averages. Finally, it computes a permutation
    p-value testing for post-stimulation toi power differences between
    recording arrays for each condition.

    Args:
        exp: The experiment to collect data for. 'main' or 'saline'

    Returns:
        None. It saves all of the permutation information, including the
        band power toi difference estimates, the permutation distributions,
        and the post-stimulation array difference p-values into a
        compressed numpy file.
    """

    with open('./experiment_config.json', 'r') as f:
        config = json.load(f)

    for condition in config['conditions']:
        print(condition)

        np.random.seed(config['random_seed'])

        # load all data for condition
        power, chs, times, freqs = load_power_data(exp, condition)

        # baseline normalize and reduce to time of interest
        power = baseline_normalize(power, config['baseline'], times)
        power = reduce_toi_power(power, times, config['toi'], axis=-1)

        perm_info = {}
        for band in ['alpha', 'beta']:
            perm_info['%s_perm_dist' % band] = []

        # build the permutation distribution
        for i in range(config['num_permutations'] + 1):
            if i > 0:
                # shuffle channel array membership
                np.random.shuffle(chs)

            # select out array 1 and array 2 channels
            arr1_ix = [ix for ix in np.arange(len(chs))
                       if 'elec1' in chs[ix] and
                       chs[ix] not in config['%s_bad_chs' % exp]]
            arr2_ix = [ix for ix in np.arange(len(chs)) if 'elec2' in chs[ix]]

            for band in ['alpha', 'beta']:

                # reduce to desired band
                band_power = reduce_band_power(power, freqs, config[band],
                                               axis=-1)

                if i == 0:
                    tmp = '%s_diff' % band
                    perm_info[tmp] = ttest_ind(band_power[arr1_ix],
                                               band_power[arr2_ix])[0]
                else:
                    tmp = '%s_perm_dist' % band
                    perm_info[tmp].append(ttest_ind(band_power[arr1_ix],
                                                    band_power[arr2_ix])[0])

        for band in ['alpha', 'beta']:
            # compute the p-value
            tmp1 = '%s_p_value' % band
            tmp2 = '%s_diff' % band
            tmp3 = '%s_perm_dist' % band
            perm_info[tmp1] = compute_permutation_p_value(perm_info[tmp2],
                                                          perm_info[tmp3])

        # save permutation info to file
        f = '../data/stats/%s_experiment/' % exp + \
            '%s_array_permutation_info.npz' % condition
        np.savez_compressed(f, alpha_dist=perm_info['alpha_perm_dist'],
                            beta_dist=perm_info['beta_perm_dist'],
                            alpha_diff=perm_info['alpha_diff'],
                            beta_diff=perm_info['beta_diff'],
                            alpha_p_value=perm_info['alpha_p_value'],
                            beta_p_value=perm_info['beta_p_value'])

    print('Done!')
예제 #7
0
def compute_permutation_sample(perm_num, all_conditions_power, trial_indices,
                               permutation_indices, times, freqs, chs, config,
                               comp, exp):
    """ Helper function to compute the permuted toi band power difference for
    a particular permutation of trials between two conditions.

    This function takes in the tfr power data for two conditions, permutes
    the trial membership between the two conditions according to the given
    permutation index, and then computes the baseline-normalized band power
    averaged across the first recording array and a post-stimulation time
    period of interest for each condition and returns their difference.

    Args:
        perm_num: The permutation number used to index a particular
            permutation index and sub-sample index.
        all_conditions_power: Dictionary containing the tfr power data for
            each condition being tested.
        trial_indices: The pre-computed sub-sampling indices.
        permutation_indices: The pre-computed permutation indices.
        times: List of time labels.
        freqs: List of frequency labels.
        chs: List of channel names.
        config: Dictionary containing experiment wide configuration info. In
            this case it contains the baseline period to normalize to, bad chs
            to ignore, time period to average over, and the frequency ranges
            for alpha and beta band.
        comp: List of the two conditions to compare.

    Returns:
        List of two numbers representing the permuted difference between
        the two conditions for the alpha and beta bands.
    """

    # collect power across conditions into single array
    # we downsample to match trial sizes
    power = []
    for c in comp:
        if perm_num != -1 and c != 'Brain' and 'Brain' in comp:
            trial_ix = trial_indices[c][perm_num, :]
            power.append(all_conditions_power[c][trial_ix, :, :, :].squeeze())
        else:
            power.append(all_conditions_power[c])
    power = np.vstack(power)

    # permute the data
    if perm_num != -1:
        perm_ix = permutation_indices[perm_num, :]
        power = power[perm_ix, :, :, :].squeeze()

    # baseline normalize each condition separately
    if perm_num != -1:
        cond_len = power.shape[0] / 2
    else:
        cond_len = all_conditions_power[comp[0]].shape[0]
    tmp = []
    tmp.append(baseline_normalize(power[:cond_len, :], config['baseline'],
                                  times))
    tmp.append(baseline_normalize(power[cond_len:, :], config['baseline'],
                                  times))
    power = tmp

    # reduce over array
    power[0] = reduce_array_power(power[0], chs, config['%s_bad_chs' % exp],
                                  '1', axis=0)
    power[1] = reduce_array_power(power[1], chs, config['%s_bad_chs' % exp],
                                  '1', axis=0)

    # compute toi band power difference
    diffs = []
    for band in [config['alpha'], config['beta']]:

        # reduce to band
        c1_power = reduce_band_power(power[0], freqs, band, axis=0)
        c2_power = reduce_band_power(power[1], freqs, band, axis=0)

        # reduce over time
        c1_power = reduce_toi_power(c1_power, times, config['toi'], axis=0)
        c2_power = reduce_toi_power(c2_power, times, config['toi'], axis=0)

        diffs.append(c1_power - c2_power)

    return diffs