Results Validation
The statistics tab on the Results page: compare any factor or combination of factors in a study, with the test the design calls for, its assumptions, an effect size, and a corrected pairwise family.
Why a second tab
The Results page can tell you that one pipeline scored highest. It cannot tell you the difference is real. “Highest mean” is not a finding — with twelve subjects and three pipelines, a two-point accuracy gap is what you would expect from noise alone about as often as not, and a table of best scores gives a reader no way to know which they are looking at.
Results Validation closes that gap without leaving the app. Pick a metric, pick the axis to compare across — session, experiment, pipeline, workflow, subject, run, or any combination — and it runs the test the design actually calls for, checks that test’s assumptions, reports an effect size with an interval, corrects the pairwise family, and writes the whole thing out in a form you can paste into a methods section.
Results vs Results Validation
| Tab | Answers | Typical use |
|---|---|---|
| Results | What did this study measure? | Best accuracy per experiment, confusion matrix, ROC, PR, per-experiment comparison table. |
| Results Validation | Does the difference survive a test? | Is pipeline A really better than B? Did session 2 differ from session 1? Does the winner depend on the workflow? |
Nothing runs until you press Run
What it reads
Every score your study has already stored. Each completed training run keeps a score per unit per pipeline per metric, and the tab turns all of them into one long table where every row carries every axis it belongs to:
value metric experiment run workflow pipeline subject session 0.7318 accuracy Motor Imagery Run 1 EEGNet EEGNet sub-01 Session 1 0.6741 accuracy Motor Imagery Run 1 CSP+LDA CSP+LDA sub-01 Session 1 0.7204 accuracy Motor Imagery Run 1 EEGNet EEGNet sub-01 Session 2 ⋮
Any column can become the grouping; any other column can become the observation unit. That is the whole idea — one table, and the design is whichever slice of it you ask for.
A unit id is not always a subject
sub-01 / Session 2; under “Combined subjects, per session” a job is a session across the whole cohort; under the pooled mode there is one job for everything. The tab reads the mode each run recorded and splits the id accordingly, so a session-wise comparison groups sessions and not strings that happen to look like them. Runs trained before the mode was recorded are inferred from the id shape, conservatively.Setting Up a Comparison
Six controls in the left panel. The preview line under them updates as you change any of them and tells you what you are about to run before you run it.
1. Metric
Lists only what this study actually measured, most-measured first. Metric names are canonicalised before they are grouped, so F1 Score from an ML Suite metric block and f1_macro from a typed backend are one metric rather than two half-empty ones. If a canonical metric turns out to pool more than one stored name, the result says so in its caveats — they are treated as one measurement, and if they are not one, that is yours to decide.
Direction is handled for you. Loss, log loss, MSE, RMSE and MAE are lower-is-better, so the written interpretation names the right winner without you inverting anything.
2. Compare by
Tick one factor, or several. Several ticked means their combination becomes the grouping: Session × Pipeline over two sessions and three pipelines gives six levels (Session 1 · EEGNet, Session 1 · CSP+LDA, and so on).
| Factor | One level per | Answers |
|---|---|---|
| Experiment | Experiment in the study | Did the redesign in v2 actually help? |
| Session | Recording session | Is performance stable across days, or is there a session effect? |
| Subject | Participant | How much does performance vary between people? |
| Pipeline | Pipeline that produced the score | Which model actually wins? |
| Workflow | Workflow graph the run executed | Does the preprocessing route change the outcome? |
| Run | Training run | Is run-to-run variation larger than the effect being claimed? |
A factor that takes only one value across the stored results is greyed out with its level count shown — there is nothing to compare it against, and that is visible before you click rather than as a refusal after. Factors that cut the data identically are labelled as such: in an ML Suite run a job is one workflow on one subject, so Pipeline and Workflow are usually the same partition under two names, and the picker says so instead of letting you build a two-factor model on a distinction that is not there.
3. Observation unit
This is the control that decides whether your n is real, and it is the one most worth understanding.
Averaging is a claim
| Unit | One observation per | When it is right |
|---|---|---|
| Subject | Participant | The usual choice. A cross-subject claim is about people, so a person is one observation. |
| Session | Session | Session-level designs, where the recording day is the sampling unit. |
| Subject × Session | Recording | Each recording is a separate measurement — more power, but only if sessions are genuinely independent. |
| Run | Training run | Comparing configurations where a run, not a person, is what was sampled. |
| Pipeline | Pipeline | Comparing subjects or sessions with pipeline as the repeated measure. |
| Each measurement | Stored score | Only when rows are genuinely independent — one score per recording, no repeats. Never the default. |
Units that are already the grouping are greyed out: a factor cannot be both, because then every unit would sit in exactly one group, nothing could be paired, and every group would hold a single value.
4. Levels
Every level of the current grouping, with a checkbox. Unticking one drops it from the comparison — useful for excluding a pilot session or a pipeline that never finished training. Exclusions are recorded and reported: a test over three of five pipelines is a claim about those three, and the caveat says which two were dropped, because excluding the levels that disagreed is exactly how a result stops being one.
5. Design
Auto detects it, and says why. The design is repeated measures when the same units were measured under every level — which is what makes pairing meaningful — and independent groups otherwise.
| Design | Chosen when | Why it matters |
|---|---|---|
| Repeated measures (paired) | The same units appear under every level, and at least 60% of units are complete. | Between-subject variance in BCI performance dwarfs most effects of interest. Pairing keeps it out of the error term, which is where nearly all the power comes from. |
| Independent groups | Different units per level, or too few complete units to pair without discarding most of the data. | Every value is a separate observation. Correct when the cohorts genuinely differ. |
Units missing a level are excluded from the paired test and named in the caveats — so an n of 9 out of 12 is never unexplained. Asking for pairing on a design that cannot support it is refused by name rather than quietly run as something else.
6. Test, post-hoc, correction, α
Test defaults to Recommended, which resolves to a named test with a stated reason printed next to the result — “three independent groups, every one normal by Shapiro-Wilk and with homogeneous variances by Levene’s test”. Every alternative valid for the design stays selectable, and whatever you select is what runs.
A recommendation, never a substitution
Post-hoc is Recommended (Tukey HSD after a one-way ANOVA on independent groups, pairwise contrasts otherwise), or an explicit choice, or none. Correction is Benjamini-Hochberg FDR by default, with Holm and Bonferroni for confirmatory work and “no correction” for a genuinely pre-registered single contrast. α defaults to 0.05.
The Test Catalogue
Which tests are offered depends on the design and the number of levels, because those are what make a test applicable at all.
Omnibus tests
| Test | Design | Levels | Assumes | Effect size |
|---|---|---|---|---|
| One-way ANOVA (Fisher) | Independent | 2+ | Normality, equal variances | η², plus ω² |
| Welch’s ANOVA | Independent | 2+ | Normality only | η² |
| Kruskal-Wallis H | Independent | 2+ | Neither | ε² |
| Repeated-measures ANOVA | Paired | 3+ | Normality, sphericity | partial η² |
| Friedman test | Paired | 3+ | Neither | Kendall’s W |
Welch’s ANOVA is not the classic one
f_oneway returns the equal-variance test that Welch’s exists to replace. BCILattice implements Welch (1951) properly — each group weighted by n/s² with corrected denominator degrees of freedom — and reports those corrected df, so F(2, 25.6) tells a reader which test ran. On unequal-variance, unequal-n groups the two disagree by enough to move a result across α.Repeated-measures ANOVA assumes sphericity and no Greenhouse-Geisser correction is applied, so a p between 0.01 and 0.05 is flagged as provisional and you are pointed at the Friedman test reported alongside it. That warning appears in the caveats every time, not only when it is convenient.
Two-level contrasts
| Test | Design | Assumes | Effect size |
|---|---|---|---|
| Welch’s t-test | Independent | Normality | Hedges’ g |
| Student’s t-test | Independent | Normality, equal variances | Hedges’ g |
| Mann-Whitney U | Independent | Neither | rank-biserial r |
| Paired t-test | Paired | Normality of differences | Cohen’s dz |
| Wilcoxon signed-rank | Paired | Neither | Cohen’s dz |
Welch’s is the default over Student’s wherever a t-test is used: it costs almost nothing when variances are equal and is correct when they are not. Below twelve observations in the smaller sample, Recommended takes the rank-based test instead, because the normality a t-test assumes cannot be checked from that little data anyway.
The effect size follows the test rather than the other way round. Quoting Hedges’ g — which standardises by a pooled SD — next to a Mann-Whitney p describes two different models at once, so the rank test reports rank-biserial r instead.
Post-hoc families
With three or more levels, a significant omnibus says the levels differ but not which ones.
| Family | What it does |
|---|---|
| Tukey HSD | Every pair with the family-wise error rate controlled exactly for the all-pairs family — the right post-hoc after a one-way ANOVA on independent groups. No further correction is applied on top. |
| Pairwise contrasts | Every pair as its own contrast — paired by unit identity when the design is repeated — then corrected across the family. Both the raw and the corrected p are shown, because a table showing only corrected values hides how much the correction cost. |
Correcting matters more than which correction you choose: eight levels give 28 pairs, and at α = 0.05 that family produces a false positive more than three quarters of the time uncorrected. Eleven levels (55 pairs) is the most this will print; past 60 pairs the family is refused rather than shown — see refusals.
If the omnibus did not reach α, the pairwise table still appears but is flagged as exploratory: reporting a significant pair under a non-significant omnibus is precisely the multiple-comparison problem the omnibus exists to control.
Two-factor models
Tick exactly two factors and a Type II two-way ANOVA is fitted alongside the main test, with both main effects and the interaction. That is the question a pair of one-way tests cannot answer: whether the effect of one factor depends on the other — whether the pipeline that wins on session 1 also wins on session 2.
A significant interaction is called out explicitly, because it means the main effects should not be reported on their own. Type II sums of squares are used because they are correct for the unbalanced designs that training results usually are. The model is fitted on the averaged table, not on the raw rows, so the same protection against pseudo-replication applies here as to the test above it.
The two-way model is descriptive when the design is paired
Assumption checks
| Check | Test | Reported as |
|---|---|---|
| Normality, per level | Shapiro-Wilk | W, p, and a verdict per level |
| Equal variances | Levene, median-centred (Brown-Forsythe) | One line under the table |
Levene is median-centred rather than mean-centred because BCI score distributions are routinely skewed, and the mean-centred form flags skew alone as unequal variance — which would push every such comparison onto Welch for a reason that is not heteroscedasticity.
“Cannot be checked” is not “normal”
Reading the Result
Verdict, effect, design, levels
Four tiles across the top:
Verdict
Effect size
Design
Levels
Not significant is not equivalence
The APA line and the reading
Under the tiles: a plain-language reading of what the test found, then the statistic formatted the way it would be written in a paper, then why this test and this design were chosen.
F(2, 22.0) = 168.74, p < .001, partial η² = 0.939 Repeated-measures ANOVA over 3 levels of pipeline, on 12 subjects: the levels differ by more than sampling noise (p < .001 < α = 0.05). The effect is large (partial η² = 0.939). "EEGNet" has the best mean accuracy (0.7311) and "CSP+LDA" the worst (0.6755), a spread of 0.0556.
The APA line is there because the number a reader needs is not the one a table is good at showing, and assembling F(2, 22.0) = 168.74, p < .001 by hand from six cells is where transcription errors come from.
The distribution plot
A box plot per level, ordered to match the descriptives table underneath it. When the design is paired, each unit is drawn as a faint line across every level, behind the boxes.
Why the paired lines are there
The figure renders off the UI thread and exports to PNG, SVG or PDF through the standard export button.
Cross-checks
Every result runs its sibling test too — the rank-based counterpart of a parametric test, or vice versa — and reports it as a labelled cross-check with whether it reaches the same conclusion.
Cross-checks are robustness, not candidates
What this result does not say
The last section of every result, and the one worth reading first. It is generated from what actually happened, not from a template. It can include:
- How many stored values were averaged into how many observations, and why testing them unaveraged would inflate n.
- Which units were excluded from a paired test for not having every level.
- Which levels you excluded by hand.
- That the canonical metric pooled more than one stored metric name.
- That the omnibus was not significant, so the pairwise table is exploratory.
- That the repeated-measures ANOVA assumes sphericity with no correction applied.
- That Levene rejected equal variances while Fisher’s ANOVA was the test run.
- That the design cannot reach α at all — see below.
When the sample cannot reach α at any effect size
Effect Sizes
Every test returns one, because “p = 0.03” answers whether a difference is distinguishable from zero and never how big it is. A 0.4-point accuracy gain across 200 subjects is both highly significant and useless.
| Measure | Reported with | Small | Medium | Large |
|---|---|---|---|---|
| η², partial η², ε², Kendall’s W | ANOVA family, Kruskal-Wallis, Friedman | 0.01 | 0.06 | 0.14 |
| Hedges’ g, Cohen’s dz | t-tests, paired contrasts | 0.2 | 0.5 | 0.8 |
| rank-biserial r | Mann-Whitney U | 0.1 | 0.3 | 0.5 |
Two-level contrasts also carry a percentile-bootstrap confidence interval on the effect size itself. Where too few resamples are usable to make that interval meaningful, it is reported as absent with the reason rather than as a number describing the resampling.
A perfectly consistent difference is undefined, not zero
What It Refuses to Do
A NaN in a results table reads as “not significant” to every reader who has seen one, so the tab refuses by name instead of producing one. Each refusal says what is missing and what to change.
| Situation | What happens |
|---|---|
| The grouping factor takes one value | Refused — a comparison needs at least two levels. The Run button is disabled and the preview explains why. |
| A factor is both the grouping and the observation unit | Unavailable in the picker: every unit would sit in one group, so nothing could be paired and every group would hold one value. |
| Fewer than two observations in a group | Refused — an ANOVA has no within-group variance to work with. |
| Every group has zero spread | Refused — F divides by zero. Report the group means directly. |
| Pairing requested on an incomplete design | Refused, naming how many units qualify out of how many. |
| A two-way table with an empty cell | The interaction cannot be estimated. Named cells are listed; an additive model is fitted instead and flagged. |
| More than 60 pairwise comparisons | Refused with the arithmetic: 40 levels give 780 tests and a Bonferroni threshold of 6.4 × 10⁻⁵. The omnibus still runs and still answers the question that many levels can support. |
| statsmodels is not installed | A named refusal with the install command, for the models that need it — never a traceback and never a silent fall back to a different method. |
Exporting for a Paper
Two buttons under the reading.
| Button | Produces |
|---|---|
| Copy report | The whole validation as plain text on your clipboard: setup, descriptives, assumptions, the primary test and its cross-checks, the post-hoc table, the two-way model, the reading, and every caveat. Deliberately includes what a screenshot of the table would leave out — which test was chosen and why, and what was averaged into what — because those are the parts a reviewer asks about. |
| Export CSV | One flat table with every number: setup rows, descriptives, normality, variance, the primary test, cross-checks, post-hoc rows, two-way terms, the per-unit observations that went in, and the caveats. One file rather than four, so the group ns and the corrected p can never drift apart. |
The distribution plot exports separately through the figure export button beneath it.
Or take the whole study out
Worked Examples
Which pipeline actually wins?
Metric Accuracy Compare by Pipeline Observation unit Subject Design Auto → repeated (the same 12 subjects ran all 3 pipelines) Test Recommended → Repeated-measures ANOVA Post-hoc Recommended → pairwise contrasts, FDR corrected
The paired design is the point here. Twelve subjects, three pipelines, and the between-subject spread is larger than the pipeline effect — an unpaired test would very likely miss a real difference that pairing finds comfortably.
Is performance stable across sessions?
Metric Accuracy Compare by Session Observation unit Subject Design Auto → repeated Test Recommended → Paired t-test or Wilcoxon by cohort size
A non-significant result here is the common and useful one — but read it as “this study cannot distinguish the sessions”, not as “the sessions are equivalent”.
Does the winning pipeline depend on the session?
Metric Accuracy Compare by Session + Pipeline (6 crossed levels) Observation unit Subject Design Auto → repeated → Repeated-measures ANOVA over the 6 levels, plus a two-way model with Session, Pipeline, and the Session × Pipeline interaction.
The interaction term is the answer. A non-significant interaction means the pipeline ranking holds across sessions; a significant one means it does not, and the main effects should not be quoted on their own.
Did the v2 experiment improve on v1?
Metric Accuracy Compare by Experiment Observation unit Subject × Session Design Auto → repeated if both experiments ran the same recordings
Does the preprocessing workflow matter?
Metric Accuracy Compare by Workflow Observation unit Subject → If the picker says Workflow is the same split as Pipeline, the two are one partition under two names in this study and comparing them is comparing pipelines. Group by Pipeline instead.
Troubleshooting
| Symptom | Cause and fix |
|---|---|
| “Nothing validated yet” and the metric list is empty | No completed run has stored per-result metrics yet. Train a model in ML Suite → Execute; the scores appear here once a run finishes. |
| Every factor is greyed out | The study has one experiment, one pipeline and one session, so nothing varies. Add a second arm — another pipeline, another session, another experiment — and re-run training. |
| Session shows only “Unspecified session” | The runs were trained under a cohort mode that does not separate sessions, so no session identity was recorded per score. Set the data source’s Runs mode to one that splits sessions and re-train. |
| Subject shows only “All subjects” | The runs were pooled — one job across the whole cohort — so there is one score, not one per participant. That is a valid design; it just has no subject axis to compare or pair on. |
| Design says independent when you expected paired | Too few units were measured under every level. The reason line says how many qualified; usually one arm did not run on the whole cohort. |
| Pairwise comparisons refused | More than 60 pairs. Untick levels, or group by a coarser factor. The omnibus result above is unaffected. |
| Effect size shows — | Either the spread is zero (a perfectly consistent difference, which is undefined rather than zero) or too few bootstrap resamples were usable. The note next to it says which. |
| A test you want is not in the list | The list follows the design and the level count. Two levels do not offer ANOVA; three do not offer a t-test. Change the design dropdown, or the grouping. |
| The two-factor model is missing | It appears only when exactly two factors are ticked, and only when every combination of them holds at least one result. |
Related