TF Baseline Explorer
Raw, un-normalized time-frequency power for C3 and C4, with the baseline window and the normalization applied live — so a badly placed baseline visibly rewrites the map.
4 claims on this page are unverified. TODO(confirm) marks a specific statement the author has not yet checked against a
primary source. Everything else on this page has been reviewed. Treat a marked claim as
provisional and go to the cited source rather than quoting the sentence.
Modes: default — the widget below runs in default. Use Share state to put the exact view in the URL.
What it does
The shipped asset is raw, un-normalized time-frequency power — nothing has had a baseline removed. Every normalization is applied in the browser, so dragging the baseline window changes the map at once. That is the whole design: the widget exists to show that the same recording produces a different picture depending on where the baseline is put, and to say in words what each wrong placement does to it.
Two channels are shown side by side, C3 and C4, for the same condition, so the lateralization is visible while the baseline moves.
Controls
| Control | What it sets |
|---|---|
| Baseline from / to | Number inputs and sliders over the epoch. Deliberately not clamped to a sensible range — moving it into the response, or into the edge margin, is the exercise |
| Use the data script’s own baseline | Jumps to the sidecar’s baseline_default_s (−1.5 to −0.5 s for this asset) |
| Normalization | dB · % change · z against the baseline’s own variation over time · raw power |
| Colour scale | Shared across channels (so the two maps compare by colour) or per channel |
| Clip the scale at percentile | 80–100; below 100 the colour limit is a percentile of |value| rather than the extreme |
| Shade the edge-effect margin | Drawn per frequency row from edge_s_by_freq, because the margin is 5 σ_t of that row’s own wavelet and is widest at the bottom of the map |
| Band from / to · Window from / to | The band and time window every read-out is computed over |
| Band summary | band-first or cell-first — which order of averaging the read-outs use |
Each map takes focus and responds to the arrow keys (shift for a bigger step, Escape to clear); the read-out is in a live region.
The three normalizations, exactly
With m(f) the mean and σ(f) the population standard deviation (divisor N) of power over the time points inside the baseline window:
dB 10 · log10( P / m )
percent 100 · ( P − m ) / m
z ( P − m ) / σ
These match mne.baseline.rescale: dB is 10 × its logratio, percent is 100 × its percent, and z is its zscore, which uses np.std’s default ddof = 0 — hence the population divisor. The baseline index range includes both endpoints, which is the same library’s rule.
Which z this is. The denominator here is the spread of the baseline over its own time points, on a map that is already averaged over trials. The other convention — the spread of single-trial power across trials at each time point — is a different quantity and cannot be computed from an averaged map at all. The control names the convention in full. TODO(confirm): shipping the across-trial z would need the single-trial maps (1.83 MB for this asset), which the data track ruled against; whether that is the convention the curriculum should teach is an open question for the author.
The two orders of averaging
A “mu ERD of −48 %” can be computed two ways and they do not agree:
- band-first (the default, and what the data script and the notebook do) — average the raw power over the frequency band, then normalize that one time course against its own baseline;
- cell-first — normalize every time × frequency cell, then average the normalized values over the band.
The mean of a ratio is not the ratio of means. On this asset the mu summary at C3 is −47.9 % band-first and −44.0 % cell-first; at C4 it is −10.5 % against −9.0 %. Neither is wrong, and a report that does not say which it used is ambiguous — so the widget shows both side by side and names the one the headline table is using. L4.4’s exercise names it in the prompt for the same reason.
What to look for
- Start in raw power and try to read the response off the map. You cannot: power falls off so steeply with frequency that the bottom row swamps everything above it, and a few µV² at 20 Hz is invisible beside hundreds at 4 Hz. That 1/f dominance is the whole reason a baseline exists (
pf-band-power-slope). - Switch to dB and the map becomes readable, because each frequency is now expressed relative to its own baseline rather than to the others. Percent shows the same ratio on a different ruler: −50 % is −3.01 dB and +100 % is +3.01 dB, always, so the two can never disagree about the sign or the location of an effect.
- Drag the baseline forward into the response. The desynchronisation shrinks toward zero and a mirror-image increase appears before the event — the divisor now contains the effect, so the effect divides itself out and the quiet pre-stimulus period becomes the anomaly. Nothing about the recording changed.
- Drag the baseline into the shaded margin at the start of the epoch. Power there is inflated by the estimator reading padding rather than data, so the divisor is too large and the whole map shifts down: an ERD appears everywhere, including at frequencies and times where nothing happened (
pf-tf-edge-effects). - Compare dB with z on a short baseline. dB and percent barely move — they only need the baseline mean. z moves a lot, because it divides by the baseline’s own variability, and a short or unusually quiet baseline makes that small. A large z is a statement about the baseline as much as about the response.
- Switch the band summary between band-first and cell-first and watch the headline number move by about four points at C3 with nothing else changed.
Used in
- L4.4 Baseline normalization and ERD/ERS (
default)
Data provenance
ds-eegbci — EEGMMIDB, PhysioNet v1.0.0 (Schalk et al., 2004). ODC-By 1.0, open access, DOI 10.13026/C28G6P. 64 channels at 160 Hz, no online filter or notch; 60 Hz mains.
The asset is subject S001, runs R04 + R08 + R12, channels C3 and C4, 22 right-fist motor-imagery trials (T2; 7 + 7 + 8), stored as (2 channels × 37 frequencies) × 281 times Float32 LE row-major. Frequencies 4–40 Hz in 1 Hz steps with n_cycles = max(3, f/2); times −2.5 to +4.5 s on a 40 Hz grid (160 Hz decimated by 4); cue at 0; the imagery interval is 4.1 s, from the EDF’s own annotation duration. Average reference over the 64 EEG channels applied offline; no band-pass, high-pass, notch or resampling of the source. Generated by data/scripts/make_tf_products.py.
The stored quantity is total power — the mean over trials of single-trial Morlet power — not the power of the trial average. The edge margin is 0.597 s (5 σ_t at 4 Hz with 3 cycles), so the widget shades [−2.5, −1.903] and [3.903, 4.5] s, with a per-row version that is widest at the bottom of the map. The sidecar’s defaults are a baseline of [−1.5, −0.5] s, which starts 0.403 s clear of the edge region and ends 0.5 s before the cue, and an active window of [0.5, 3.5] s.
Units. The stored values are the analysis library’s own |W ∗ x|² for an input in µV: proportional to power but carrying a frequency-dependent constant, so not µV². The sidecar ships unit_cosine_power_uv2 per frequency row, and the raw-power view divides each row by twice it and labels itself “µV² (mean square)”, with the cursor read-out giving amplitude in µV. Every normalization is a ratio inside one row, so the constant cancels exactly and dB, percent and z are identical with or without the conversion. As the sidecar’s own sanity check, the baseline mu at C3 works out to 7.56 µV of amplitude.
The subject was selected, and the selection is recorded. Ten candidate subjects were run through the identical pipeline and ranked by C3 mu ERD; S001 is the most negative of them at −47.9 %, where the median is −23.1 %, the range runs to +6.6 %, and only 6 of the 10 have C3 more negative than C4. Excluded from the pool: S088, S089, S092 and S100 for inconsistent event timestamps, plus S038 and S104. S001 is a teaching example chosen for legibility; nothing about the cohort follows from it, and the sidecar says so.
The sidecar also carries a measured block — values the data script computed from this very asset with named windows — which the widget recomputes and prints beside its own, with the difference, and with a warning when the current windows are not the ones the script used. That block is a cross-check, not the answer key: the key for L4.4 is filled from nb-4-4-erd, and if the two disagree the windows or the reference differ and that has to be resolved rather than averaged.
TODO(confirm): ds-eegbci’s own online reference is not documented in the catalogue, so the offline average reference is this site’s choice; the sidecar records that the C3 mu ERD roughly doubles under it on every candidate subject measured.
Open the code
site/src/components/widgets/w-tf-baseline-explorer/ — Widget.svelte, normalize.ts (the four normalizations, the baseline statistics, the calibration, both orders of averaging and every diagnostic), state.ts, data.ts, synthetic.ts, colormap.ts, plots.ts, and 93 tests including an integration test that reproduces every value in the sidecar’s measured block. Repository link: TODO(confirm) (GitHub org/repo, §13 item 3).
TODO(confirm): screenshot.png is a placeholder, not a capture of the running widget.