Statistics for small experiments
The binomial bound that says when a classifier is above chance, permutation tests that need no assumptions, cluster correction for waveforms, effect sizes, and the multiple-comparisons trap. Enough to not fool yourself.
On this page14 min
Your classifierClassifier An algorithm that assigns each trial a label, such as left hand or right hand, after being trained on labelled examples. Related: Linear discriminant analysis (LDA)Linear discriminant analysis (LDA) A simple, robust classifier that draws a straight boundary between classes; the workhorse of EEG decoding for two decades. , Support vector machine (SVM)Support vector machine (SVM) A classifier that finds the boundary with the widest margin between classes; strong on small datasets. , Cross-validationCross-validation Testing a model on data it never saw during training, repeatedly, so the reported accuracy means something. . Glossary entry scored 62 percent. Your ERPEvent-related potential (ERP) The small, stereotyped voltage waveform the brain produces in response to an event, invisible in a single trial and revealed by averaging many. Related: P300P300 A positive bump about 300 milliseconds after a rare or meaningful stimulus, the basis of the classic BCI speller. , N170N170 A negative deflection about 170 milliseconds after seeing a face, over the back of the head on the right. , Error-related negativity (ERN)Error-related negativity (ERN) A sharp negative wave about 50 to 100 milliseconds after you make a mistake, before you consciously notice. , Trial averagingTrial averaging Adding many time-locked epochs together so the consistent response grows and the random noise shrinks by the square root of the number of trials. . Glossary entry has a bump. Your feedback nights looked better than sham. Each of these is a number, and the question is always the same: how often would noise alone produce a number this big? The tools on this page answer that for the small, messy datasets you actually have. None of them need more than arithmetic and a loop.
When is a classifier above chance?
Two classes, N trials, a classifierClassifier An algorithm that assigns each trial a label, such as left hand or right hand, after being trained on labelled examples. Related: Linear discriminant analysis (LDA)Linear discriminant analysis (LDA) A simple, robust classifier that draws a straight boundary between classes; the workhorse of EEG decoding for two decades. , Support vector machine (SVM)Support vector machine (SVM) A classifier that finds the boundary with the widest margin between classes; strong on small datasets. , Cross-validationCross-validation Testing a model on data it never saw during training, repeatedly, so the reported accuracy means something. . Glossary entry that is really guessing. The number it gets right follows a binomial distribution with probability one half. The 95th percentile of that distribution, divided by N, is the accuracy a guessing classifierClassifier An algorithm that assigns each trial a label, such as left hand or right hand, after being trained on labelled examples. Related: Linear discriminant analysis (LDA)Linear discriminant analysis (LDA) A simple, robust classifier that draws a straight boundary between classes; the workhorse of EEG decoding for two decades. , Support vector machine (SVM)Support vector machine (SVM) A classifier that finds the boundary with the widest margin between classes; strong on small datasets. , Cross-validationCross-validation Testing a model on data it never saw during training, repeatedly, so the reported accuracy means something. . Glossary entry beats one time in twenty. For N = 40 it is about 62 percent. For 80, about 59. For 200, about 56. For 1000, about 52. Only above that bound does your accuracy mean anything, and the bound should sit next to the accuracy in every report.
The bound is the inverse binomial tail, per trial:
Here is the inverse cumulative binomial distribution, , the trial count, and the guessing rate for two classes.
from scipy.stats import binom
def chance_bound(n_trials, n_classes=2, alpha=0.05):
return binom.ppf(1 - alpha, n_trials, 1 / n_classes) / n_trials
The bound assumes trials are independent, which shuffled trials from one session are not. So it is the least you need, not the most. For unbalanced classes, use the majority-class rate as the null.
Permutation tests
You want to know whether two conditions differ in some measure: mean amplitude, accuracy, alpha power. Compute the difference. Then shuffle the condition labels, recompute the difference, and repeat ten thousand times. The fraction of shuffles whose difference is at least as large as yours is the p-value. No assumption about distributions, no formula to misremember, and it works for any statistic you can compute. For within-subject designs, shuffle within each participant (swap the two conditions’ labels per person).
The p-value is the tail probability under shuffling:
Here is the difference measured with the true labels and the differences from shuffled labels.
import numpy as np
def perm_test(a, b, n=10000, rng=np.random.default_rng(0)):
# a, b: paired measurements per participant
d = a - b; obs = d.mean()
signs = rng.choice([-1, 1], size=(n, len(d)))
null = (signs * d).mean(axis=1)
return (np.abs(null) >= abs(obs)).mean()
You test whether an ERP differs between conditions at every one of 200 time points and 32 channels. At a threshold of p below 0.05, how many significant points do you expect if there is no effect at all?
About 320. Five percent of 6400 tests are false positives by construction (). Testing many points and reporting the ones that cross the line is the multiple-comparisons problem, and it produces a “significant” cluster somewhere in nearly every dataset. Cluster-based permutation, below, is the standard fix for waveforms.
Cluster-based permutation for waveforms
Compute a t-statistic at every time point (and channel). Threshold it. Group neighbouring above-threshold points into clusters and give each cluster a mass (the sum of its t-values). Shuffle labels, repeat, and record the largest cluster mass in each shuffle. Your cluster’s p-value is the fraction of shuffles whose biggest cluster was bigger. This controls the false-positive rate across the whole waveform while exploiting the fact that real effects are extended in time and space. MNEMNE-Python The standard open-source Python library for EEG and MEG analysis: filtering, epoching, ICA, topographies, source localization. Related: Independent component analysis (ICA)Independent component analysis (ICA) A method that unmixes multichannel data into statistically independent sources, so blinks and heartbeat can be identified and removed. . Glossary entry implements it in one function. Report the cluster’s extent honestly: the test says a difference exists somewhere in the cluster, not that it starts or ends at the cluster’s edges.
Each cluster gets one number, its mass, the sum of its thresholded t-values:
Only the largest mass per shuffle is kept, which is what controls the error rate across the whole waveform.
Effect sizes
A p-value says whether an effect is distinguishable from zero. It does not say how big it is. Report the difference in the measure’s own units (2.3 µV) and a standardized effect size (Cohen’s d, the difference divided by the standard deviation across participants). A d of 0.2 is small, 0.5 medium, 0.8 large. Classic ERPEvent-related potential (ERP) The small, stereotyped voltage waveform the brain produces in response to an event, invisible in a single trial and revealed by averaging many. Related: P300P300 A positive bump about 300 milliseconds after a rare or meaningful stimulus, the basis of the classic BCI speller. , N170N170 A negative deflection about 170 milliseconds after seeing a face, over the back of the head on the right. , Error-related negativity (ERN)Error-related negativity (ERN) A sharp negative wave about 50 to 100 milliseconds after you make a mistake, before you consciously notice. , Trial averagingTrial averaging Adding many time-locked epochs together so the consistent response grows and the random noise shrinks by the square root of the number of trials. . Glossary entry effects are large; most “cognitive enhancement” claims are small at best, and a small effect in twelve people is noise.
Cohen’s d standardizes the difference by the spread across participants:
Here is the mean difference in the measure’s own units and is the standard deviation across participants.
The traps, named
Peeking and stopping. Collecting data until the p-value crosses 0.05 guarantees it will. Set N in advance.
Garden of forking paths. Trying several windows, channels, filtersFilter An operation that passes some frequencies and attenuates others; every filter also delays or distorts the timing of what it passes. Related: High-pass filterHigh-pass filter Removes slow drifts and DC offsets, passing everything above a cutoff; set it too high and you erase slow brain activity. , Low-pass filterLow-pass filter Removes fast content above a cutoff; the anti-alias filter is one, and so is the smoothing you apply to a noisy trace. , Notch filterNotch filter A very narrow filter that removes one frequency, usually 60 Hz mains hum; it also rings and distorts nearby content, so fix the hum at the source first. , Phase distortionPhase distortion Different frequencies being delayed by different amounts, which smears and shifts waveforms such as an ERP peak. . Glossary entry, and rejection thresholds and reporting the one that worked. Pre-register the analysis.
Double dipping. Choosing the channel or window where the effect looks biggest, then testing it there. The selection already used the data.
Correlation across trials treated as independence. Trials from one person are not independent samples of the population. Test across participants.
Confusing significance with size. A p of 0.001 with a d of 0.1 is a real, tiny effect, probably from a large N. Say so.
Deep dive Bayesian alternatives 2 min
A Bayes factor compares how well the data are predicted by “there is an effect” versus “there is none,” and can express evidence for the null, which a p-value cannot. For BCIBrain-computer interface (BCI) A system that turns measured brain activity into control of something outside the body, from a cursor to a wheelchair to a speech synthesizer. Related: DecoderDecoder The algorithm that maps neural signals to intended actions, such as a classifier or a Kalman filter. , Closed loopClosed loop A system in which the output feeds back to influence the input, such as a user adjusting their brain activity while watching the cursor it controls. . Glossary entry accuracy, a Bayesian estimate of the accuracy with a credible interval is often more useful than a bound. The tools (PyMC, brms) are more work to learn; do it when a reviewer asks or when you need to argue that something does not work, which the neurofeedbackNeurofeedback Showing someone a measure of their own brain activity in real time so they can try to change it; its clinical claims range from plausible to unsupported. Related: Closed loopClosed loop A system in which the output feeds back to influence the input, such as a user adjusting their brain activity while watching the cursor it controls. . Glossary entry project may require.
Deep dive Information transfer rate has error bars too 2 min
ITR is computed from an accuracy, and the accuracy has uncertainty. Bootstrap: resample selections with replacement, recompute ITR, repeat, report the 2.5 and 97.5 percentiles. A speller reported at 42 bits per minute with an interval from 30 to 50 is a different claim from one at 42 exactly.
For targets at accuracy , the bits per selection are:
Resample the selections, recompute each time, and the percentiles of the results are the interval.
Explain what this page was about to your roommate in three sentences. No jargon they would not know.
We call 62 percent on 40 trials the line for two choice guessing at the 5 percent level, dropping to near 52 percent at 1000 trials, with unbalanced tasks judged against the majority rate. We test condition differences by shuffling labels about 10000 times and counting how often the shuffled gap beats the real gap, which needs no shape assumptions. We fix waveform floods by grouping neighbor hits into clusters and testing the largest mass against shuffles, then report size in microvolts plus a standardized gap near 2 tenths for small and near 8 tenths for large.
Yours does not have to match. Check that the three ideas are there.