예제 #1
0
def plot_controlling_spectra(exp):
    """ Plots the stimulation power spectrum for the bipolar referenced
    electrode that provided the feedback signal for stimulation and a copy
    of the stimulation command stored in the .ns5 files.

    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 the controlling bipolar referenced electrode
        and a copy of the stimulation command for a particular condition.
        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['font_scale']})

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

    types = ['ns2', 'ns5']

    # hack the legend to be color agnostic
    legend = ['Neural Recording', 'Stimulation Command']
    axs[2].axvline(-3, color='k', linestyle='--')
    axs[2].axvline(-3, color='k')
    axs[2].legend(legend)

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

        ax = axs[i]

        for typ in types:

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

            if typ == 'ns2':
                ch_ix = [
                    ix for ix in np.arange(len(chs)) if 'elec1-83' in chs[ix]
                ]
                linestyle = '--'
            else:
                ch_ix = [
                    ix for ix in np.arange(len(chs)) if 'ainp2' in chs[ix]
                ]
                linestyle = '-'

            power = power[:, ch_ix, :, :].squeeze()
            power = power.mean(axis=0).mean(axis=-1)
            power = power / power.sum()

            ax.plot(freqs,
                    power,
                    color=config['colors'][i],
                    linestyle=linestyle)
            ax.set_title(condition)
            ax.set_xlabel('Frequency [Hz]')
            ax.set_xlim((freqs[0], freqs[-1]))
            ax.set_ylabel('Normalized Power')

    plt.tight_layout()
    sns.despine()

    return fig
예제 #2
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
예제 #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_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
예제 #5
0
def plot_array_band_comparison(exp):
    """ Plots the pre- and post-stimulation alpha and beta time series
    for all three conditions compared between recording arrays.

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

    Returns:
        A 2 x 3 matplotlib figure (frequency band x condition). Each subplot
        contains array1 and array2 time series for a particular condition and
        frequency band with bootstrap standard error shading. The stimulation
        period is ignored and centered at 0 with a +- 0.5 blacked out period
        representing stimulation edge artifact.
    """

    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(2, 3, figsize=(22, 10))
    plt.subplots_adjust(hspace=.3)

    window = 3
    xticks = np.arange(-window, window + 1)
    xticklabels = ['Stim' if x == 0 else x for x in xticks]
    ls = ['-', '--']

    # hack the legend to be color agnostic
    axs[0, 2].axvline(-3, color='k')
    axs[0, 2].axvline(-3, color='k', linestyle='--')
    axs[0, 2].legend(['Array 1', 'Array 2'])

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

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

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

        # select out pre and post stimulation
        # collapse stimulation into 0 and make pre and post stimulation times
        # relative to this 0 (so no longer 10 + for post stimulation)
        pre_mask = np.logical_and(times >= -5, times <= -.5)
        post_mask = np.logical_and(times >= 10.5, times <= 15)
        time_mask = np.where(np.logical_or(pre_mask, post_mask))[0]
        times = times[time_mask]
        power = power[:, :, time_mask]
        times[times >= 10] -= 10

        # 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)

            for k, arr in enumerate([arr1_ix, arr2_ix]):
                arr_power = band_power[arr, :].mean(axis=0)
                arr_stderr = band_power[arr, :].std(axis=0) / \
                    np.sqrt(len(arr))

                axs[j, i].plot(times,
                               arr_power,
                               color=config['colors'][i],
                               linestyle=ls[k])
                axs[j, i].fill_between(times,
                                       arr_power - arr_stderr,
                                       arr_power + arr_stderr,
                                       facecolor=config['colors'][i],
                                       alpha=0.2,
                                       edgecolor='none',
                                       label='_nolegend_')

            # pretty axis
            axs[j, i].set_title('%s %s Power' % (c, band.capitalize()))
            axs[j, i].set_xlim((-window, window))
            axs[j, i].set_xticks(xticks)
            axs[j, i].set_xticklabels(xticklabels)
            axs[j, i].set_ylim((-1, 1))
            if i == 0:
                axs[j, i].set_ylabel('dB Change From Baseline')
            if j == 1:
                axs[j, i].set_xlabel('Time (s)')

            # add blackout for stim period
            for x in np.arange(-.5, .5, .01):
                axs[j, i].axvline(x, color='k', alpha=0.8, label='_nolegend_')
                axs[j, i].axvline(x, color='k', alpha=0.8, label='_nolegend_')

    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_distributions(exp):
    """ Computes permutation distributions for alpha and beta band power
    for all three pairs stimulation conditions differences.

    For each condition pair, it loads those condition's raw tfr power data.
    It then runs through each sub-sample index and permutation index to
    equalize trial counts and then permute trial membership between the two
    conditions in order to calculate the difference between the mean
    post-stimulation band toi power creating a permutation distribution.
    Finally, it computes a permutation p-value testing for post-stimulation
    toi power differences between conditions.

    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 condition difference p-values into a
        compressed numpy file.
    """

    global all_conditions_power, trial_indices, times, freqs, chs, config
    global comp, exper, permutation_ix
    exper = exp

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

    # load pre-sampled indices
    f = '../data/stats/%s_experiment/condition_permutation_indices.npz' % exp
    permutation_indices = np.load(f)
    f = '../data/stats/%s_experiment/condition_subsample_indices.npz' % exp
    trial_indices = np.load(f)
    tmp = {}
    for condition in config['conditions']:
        tmp[condition] = trial_indices[condition]
    trial_indices = tmp

    permutation_info = {}

    # loop through condition comparisons
    comparisons = [['Open', 'Closed'], ['Open', 'Brain'], ['Brain', 'Closed']]
    for comp in comparisons:

        print('Computing Permutation Distribution for Condition ' +
              'Comparison: %s-%s' % (comp[0], comp[1]))

        # collect all power for the relevant conditions
        all_conditions_power = {}
        for condition in comp:
            power, chs, times, freqs = load_power_data(exp, condition)
            all_conditions_power[condition] = power

        # get the permutation index
        permutation_ix = permutation_indices['%s_%s' % (comp[0],
                                                        comp[1])]

        # compute the base difference
        base_diffs = compute_permutation_sample(-1, all_conditions_power,
                                                trial_indices, permutation_ix,
                                                times, freqs, chs, config,
                                                comp, exp)
        for i, band in enumerate(['alpha', 'beta']):
            permutation_info['%s_diff' % band] = base_diffs[i]

        num_permutations = permutation_indices['num_permutations']
        perm_diffs = []
        par = Parallel(n_jobs=config['n_jobs'])
        perm_diffs = par(delayed(compute_permutation_wrapper)(ix)
                         for ix in range(num_permutations))

        permutation_info['alpha_perm_dist'] = [diff[0] for diff in perm_diffs]
        permutation_info['beta_perm_dist'] = [diff[1] for diff in perm_diffs]

        # compute p-values
        tmp = compute_permutation_p_value(permutation_info['alpha_diff'],
                                          permutation_info['alpha_perm_dist'])
        permutation_info['alpha_p_value'] = tmp

        tmp = compute_permutation_p_value(permutation_info['beta_diff'],
                                          permutation_info['beta_perm_dist'])
        permutation_info['beta_p_value'] = tmp

        # save the permutation information
        f = '../data/stats/%s_experiment/%s-%s_%s_permutation_info.npz'
        np.savez_compressed(f % (exp, comp[0], comp[1], exp),
                            alpha_dist=permutation_info['alpha_perm_dist'],
                            beta_dist=permutation_info['beta_perm_dist'],
                            alpha_diff=permutation_info['alpha_diff'],
                            beta_diff=permutation_info['beta_diff'],
                            num_permutations=num_permutations,
                            alpha_p_value=permutation_info['alpha_p_value'],
                            beta_p_value=permutation_info['beta_p_value'])
예제 #8
0
def compute_bootstrap_distribution(exp):
    """ Computes bootstrap distributions for alpha and beta band power for all
    three stimulation conditions.

    For each condition, it loads that condition's raw tfr and pre-computed
    bootstrap sampled indices. It then runs through each re-sampled index
    and computes the re-sampled band power to create a bootstrap distribution.
    Finally, it computes a bootstrap p-value testing for post-stimulation
    toi power differences from 0.

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

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

    global power, times, freqs, chs, bootstrap_cond_ix, config, exper
    exper = exp

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

    # load in pre-computes bootstrap re-sample indices
    f = '../data/stats/%s_experiment/condition_bootstrap_indices.npz' % exp
    bootstrap_indices = np.load(f)
    num_bootstrap_samples = bootstrap_indices['num_samples']

    for condition in config['conditions']:

        print('Computing Bootstrap Distribution for Condition: %s' % condition)

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

        # compute the base band power
        base_ix = np.arange(power.shape[0])
        alpha_power, beta_power = compute_bootstrap_sample(base_ix, power,
                                                           times, freqs, chs,
                                                           config, exp)

        # loop through all bootstrap samples in parallel
        bootstrap_cond_ix = bootstrap_indices[condition]
        par = Parallel(n_jobs=config['n_jobs'])
        bootstrap_samples = par(delayed(compute_bootstrap_wrapper)(ix)
                                for ix in range(num_bootstrap_samples))

        # collect all the bootstrap samples into single matrix
        alpha_bootstrap_samples = np.vstack([s[0] for s in bootstrap_samples])
        beta_bootstrap_samples = np.vstack([s[1] for s in bootstrap_samples])

        # compute p-values
        alpha_p = compute_bootstrap_p_value(alpha_power,
                                            alpha_bootstrap_samples,
                                            times, config['toi'])
        beta_p = compute_bootstrap_p_value(beta_power,
                                           beta_bootstrap_samples,
                                           times, config['toi'])

        # save
        f = '../data/stats/%s_experiment/%s_bootstrap_info.npz' % (exp,
                                                                   condition)
        np.savez_compressed(f, alpha=alpha_power, beta=beta_power,
                            alpha_dist=alpha_bootstrap_samples,
                            beta_dist=beta_bootstrap_samples,
                            alpha_p=alpha_p, beta_p=beta_p, times=times)