text = fig.suptitle(title) if mcc: text.set_weight('bold') plt.subplots_adjust(0, 0.05, 1, 0.9, wspace=0, hspace=0) mne.viz.utils.plt_show() plot_t_p(ts[-1], ps[-1], titles[-1], mccs[-1]) ############################################################################### # "Hat" variance adjustment # ~~~~~~~~~~~~~~~~~~~~~~~~~ # The "hat" technique regularizes the variance values used in the t-test # calculation :footcite:`RidgwayEtAl2012` to compensate for implausibly small # variances. ts.append(ttest_1samp_no_p(X, sigma=sigma)) ps.append(stats.distributions.t.sf(np.abs(ts[-1]), len(X) - 1) * 2) titles.append(r'$\mathrm{t_{hat}}$') mccs.append(False) plot_t_p(ts[-1], ps[-1], titles[-1], mccs[-1]) ############################################################################### # Non-parametric tests # ^^^^^^^^^^^^^^^^^^^^ # Instead of assuming an underlying Gaussian distribution, we could instead # use a **non-parametric resampling** method. In the case of a paired t-test # between two conditions A and B, which is mathematically equivalent to a # one-sample t-test between the difference in the conditions A-B, under the # null hypothesis we have the principle of **exchangeability**. This means # that, if the null is true, we can exchange conditions and not change # the distribution of the test statistic.
def one_sample_parametric(a, n, hat=0., method='relative'): ts = ttest_1samp_no_p(a, sigma=hat, method=method) ps = stats.distributions.t.sf(np.abs(ts), n - 1) * 2 return ts, ps
def _stat_fun(x, sigma=0, method="relative"): from mne.stats import ttest_1samp_no_p t_values = ttest_1samp_no_p(x, sigma=sigma, method=method) t_values[np.isnan(t_values)] = 0 return t_values
# magnetometer RMS data[si, hi] = np.sqrt(np.mean(evo_cp.data, axis=0)**2) evokeds.append(evoked) evoked_dict[cond] = evokeds X.append(data) # |deviant - standard| per hem contrast = np.abs(X[1] - X[0]) contrast = np.transpose(contrast, (1, 0, 2)) ############################################# # One-sample t-testing with "hat" adjustment# ############################################# fig, axs = plt.subplots(1, len(sensors), sharex=True, sharey=True) for ii, key in enumerate(sensors.keys()): print(' Testing...\n' ' |deviant - standard| in %s hem...' % key) ts = ttest_1samp_no_p(contrast[ii], sigma=sigma) ps_unc = stats.distributions.t.sf(np.abs(ts), n - 1) * 2 n_samples, n_tests = contrast[ii].shape threshold_uncorrected = stats.t.ppf(1.0 - alpha, n_samples - 1) reject_bonferroni, pval_bonferroni = bonferroni_correction(ps_unc, alpha=alpha) threshold_bonferroni = stats.t.ppf(1.0 - alpha / n_tests, n_samples - 1) reject_fdr, pval_fdr = fdr_correction(ps_unc, alpha=alpha, method='indep') threshold_fdr = np.min(np.abs(ts)[reject_fdr]) axs[ii].plot(times, ts, label='$\mathrm{|deviant - standard|_{hat}}$',
if show: text = fig.suptitle(title) if mcc: text.set_weight('bold') plt.subplots_adjust(0, 0.05, 1, 0.9, wspace=0, hspace=0) mne.viz.utils.plt_show() plot_t_p(ts[-1], ps[-1], titles[-1], mccs[-1]) ############################################################################### # "Hat" variance adjustment # ~~~~~~~~~~~~~~~~~~~~~~~~~ # The "hat" technique regularizes the variance values used in the t-test # calculation [1]_ to compensate for implausibly small variances. ts.append(ttest_1samp_no_p(X, sigma=sigma)) ps.append(stats.distributions.t.sf(np.abs(ts[-1]), len(X) - 1) * 2) titles.append('$\mathrm{t_{hat}}$') mccs.append(False) plot_t_p(ts[-1], ps[-1], titles[-1], mccs[-1]) ############################################################################### # Non-parametric tests # ^^^^^^^^^^^^^^^^^^^^ # Instead of assuming an underlying Gaussian distribution, we could instead # use a **non-parametric resampling** method. Under the null hypothesis, # we have the principle of **exchangeability**, which means that, if the null # is true, we should be able to exchange conditions and not change the # distribution of the test statistic. # # In the case of a two-tailed 1-sample t-test (which can also be used to test
def _stat_fun(x, sigma=0, method='relative'): """Aux. function of stats""" t_values = ttest_1samp_no_p(x, sigma=sigma, method=method) t_values[np.isnan(t_values)] = 0 return t_values
def _stat_fun(x, sigma=0, method='relative'): t_values = ttest_1samp_no_p(x, sigma=sigma, method=method) t_values[np.isnan(t_values)] = 0 return t_values
def _stat_fun(x, sigma=0, method='relative'): from mne.stats import ttest_1samp_no_p import numpy as np t_values = ttest_1samp_no_p(x, sigma=sigma, method=method) t_values[np.isnan(t_values)] = 0 return t_values
def permutation_cluster_ttest(data1, data2, paired=False, n_permutations=1000, threshold=None, p_threshold=0.05, adjacency=None, tmin=None, tmax=None, fmin=None, fmax=None, trial_level=False, min_adj_ch=0): '''Perform cluster-based permutation test with t test as statistic. Parameters ---------- data1 : list of mne objects List of objects (Evokeds, TFRs) belonging to condition one. data2 : list of mne objects List of objects (Evokeds, TFRs) belonging to condition two. paired : bool Whether to perform a paired t test. Defaults to ``True``. n_permutations : int How many permutations to perform. Defaults to ``1000``. threshold : value Cluster entry threshold defined by the value of the statistic. Defaults to ``None`` which calculates threshold from p value (see ``p_threshold``) p_threshold : value Cluster entry threshold defined by the p value. adjacency : boolean array | sparse array Information about channel adjacency. tmin : float Start of the time window of interest (in seconds). Defaults to ``None`` which takes the earliest possible time. tmax : float End of the time window of interest (in seconds). Defaults to ``None`` which takes the latest possible time. fmin : float Start of the frequency window of interest (in seconds). Defaults to ``None`` which takes the lowest possible frequency. fmax : float End of the frequency window of interest (in seconds). Defaults to ``None`` which takes the highest possible frequency. min_adj_ch: int Minimum number of adjacent in-cluster channels to retain a point in the cluster. Returns ------- clst : borsar.cluster.Clusters Obtained clusters. ''' if data2 is not None: one_sample = False stat_fun = ttest_rel_no_p if paired else ttest_ind_no_p else: one_sample = True stat_fun = lambda data: ttest_1samp_no_p(data[0]) try: kwarg = 'connectivity' from mne.source_estimate import spatial_tris_connectivity except: kwarg = 'adjacency' from mne.source_estimate import spatial_tris_adjacency inst = data1[0] len1 = len(data1) len2 = len(data2) if data2 is not None else 0 if paired: assert len1 == len2 threshold = _compute_threshold([data1, data2], threshold, p_threshold, trial_level, paired, one_sample) # data1 and data2 have to be Evokeds or TFRs supported_types = (mne.Evoked, borsar.freq.PSD, mne.time_frequency.AverageTFR, mne.time_frequency.EpochsTFR) check_list_inst(data1, inst=supported_types) if data2 is not None: check_list_inst(data2, inst=supported_types) # find time and frequency ranges # ------------------------------ if isinstance(inst, (mne.Evoked, mne.time_frequency.AverageTFR)): tmin = 0 if tmin is None else inst.time_as_index(tmin)[0] tmax = (len(inst.times) if tmax is None else inst.time_as_index(tmax)[0] + 1) time_slice = slice(tmin, tmax) if isinstance(inst, (borsar.freq.PSD, mne.time_frequency.AverageTFR)): fmin = 0 if fmin is None else find_index(data1[0].freqs, fmin) fmax = (len(inst.freqs) if fmax is None else find_index( data1[0].freqs, fmax)) freq_slice = slice(fmin, fmax + 1) # handle object-specific data # --------------------------- if isinstance(inst, mne.time_frequency.AverageTFR): # + fmin, fmax assert not trial_level # data are in observations x channels x frequencies x time data1 = np.stack( [tfr.data[:, freq_slice, time_slice] for tfr in data1], axis=0) data2 = (np.stack( [tfr.data[:, freq_slice, time_slice] for tfr in data2], axis=0) if data2 is not None else data2) elif isinstance(inst, mne.time_frequency.EpochsTFR): assert trial_level data1 = inst.data[..., freq_slice, time_slice] data2 = (data2[0].data[..., freq_slice, time_slice] if data2 is not None else data2) elif isinstance(inst, borsar.freq.PSD): if not inst._has_epochs: assert not trial_level data1 = np.stack([psd.data[:, freq_slice].T for psd in data1], axis=0) data2 = (np.stack([psd.data[:, freq_slice].T for psd in data2], axis=0) if data2 is not None else data2) else: assert trial_level data1 = data1[0].data[..., freq_slice].transpose((0, 2, 1)) data2 = (data2[0].data[..., freq_slice].transpose( (0, 2, 1)) if data2 is not None else data2) else: data1 = np.stack([erp.data[:, time_slice].T for erp in data1], axis=0) data2 = (np.stack([erp.data[:, time_slice].T for erp in data2], axis=0) if data2 is not None else data2) data_3d = data1.ndim > 3 if (isinstance(adjacency, np.ndarray) and not sparse.issparse(adjacency) and not data_3d): adjacency = sparse.coo_matrix(adjacency) # perform cluster-based test # -------------------------- # TODO: now our cluster-based works also for 1d and 2d etc. if not data_3d: assert min_adj_ch == 0 adj_param = {kwarg: adjacency} stat, clusters, cluster_p, _ = permutation_cluster_test( [data1, data2], stat_fun=stat_fun, threshold=threshold, n_permutations=n_permutations, out_type='mask', **adj_param) if isinstance(inst, mne.Evoked): dimcoords = [inst.ch_names, inst.times[time_slice]] dimnames = ['chan', 'time'] elif isinstance(inst, borsar.freq.PSD): dimcoords = [inst.ch_names, inst.freqs[freq_slice]] dimnames = ['chan', 'freq'] return Clusters(stat.T, [c.T for c in clusters], cluster_p, info=inst.info, dimnames=dimnames, dimcoords=dimcoords) else: stat, clusters, cluster_p = permutation_cluster_test_array( [data1, data2], adjacency, stat_fun, threshold=threshold, n_permutations=n_permutations, one_sample=one_sample, paired=paired, min_adj_ch=min_adj_ch) # pack into Clusters object dimcoords = [inst.ch_names, inst.freqs, inst.times[tmin:tmax]] return Clusters(stat, clusters, cluster_p, info=inst.info, dimnames=['chan', 'freq', 'time'], dimcoords=dimcoords)