From window labels to events: asynchronous EEG decoding with DANCE#

braindecode defines brain decoding as \(f_\theta: X \to y\) (see the brain decode problem), and is explicit that the definition of \(y\) is broad. Most tutorials in this repository instantiate that mapping at a single point in the space it allows: \(X\) is a window already cut and aligned to a known onset (a motor-imagery cue, a sleep-epoch boundary, a P300 flash), and \(y\) is one categorical label for the whole window.

DANCE instantiates the same \(f_\theta: X \to y\) at a different point: \(X\) is one long, unaligned recording, and \(y\) is the set of (start, end, class) events \(f_\theta\) must locate and classify itself. The definition did not change. Only the shape of \(y\) did.

Same f theta of X arrow y, different shape of y. Onset-informed decoding gives one label per aligned window; DANCE gives a set of (start, end, class) events on a continuous recording.

This tutorial shows how to use the DANCE model for event detection on continuous, real EEG recordings, with plain PyTorch (no skorch, no Lightning). We reproduce, at reduced scale, the P300 setting from the original paper [1], using the Brain Invaders BI2014a dataset [2] loaded through MOABBDataset.

The pipeline is:

  1. load a few subjects of continuous, real P300 EEG (BI2014a) with MOABBDataset,

  2. preprocess with the exact minimal recipe from the paper: band-pass 0.1-100 Hz, resample to 128 Hz, per-channel robust scaling clamped to [-16, 16],

  3. cut it into fixed-length windows with create_fixed_length_windows() (W = 32 s, matching the paper’s BI2014a configuration),

  4. turn each window’s stimulus annotations (Target/NonTarget flashes) into a DANCE target dict (normalized start/end in [0, 1] plus a dense per-token class map),

  5. train DANCE with DanceLoss on a cross-subject split (train on some subjects, evaluate on held-out ones, following the paper’s protocol), and

  6. evaluate with the event-level f1_event() metric and a per-token macro F1 on the dense head (sklearn.metrics.f1_score()).

DANCE exposes two entry points: model.forward(x) returns the dense per-token logits (B, num_latents, n_outputs), while model.detect(x) returns the event set {class, start, end, dense}. Training and evaluation below use detect (the loss needs the query set); a downstream user who only wants a per-token prediction can call forward directly.

Note

DANCE follows a DETR-style class-0 = background / no-object convention: real event classes are 1 .. n_outputs - 1 and class 0 is reserved for “no event”. Here class 1 is a non-target flash and class 2 a target flash; the target builder and both F1 metrics honour this convention automatically.

Note

To keep the docs build fast, this tutorial uses only 3 subjects and 2 training epochs, far short of the paper’s full protocol (all subjects, 100 epochs with early stopping). Expect low F1 scores below; see the Conclusion for what changes at full scale.

# Authors: Bruno Aristimunha <b.aristimunha@gmail.com>
#
# License: MIT

random_state = 0

Loading and preprocessing the dataset#

Loading the raw recordings#

BI2014a is a Brain Invaders P300 (oddball) dataset: subjects watch a stream of flashes, most of which are “non-target” and a few “target”. We load 3 subjects: 2 for training, 1 held out for evaluation, following the paper’s cross-subject splitting protocol.

from braindecode.datasets import MOABBDataset

subject_ids = [1, 2, 3]
dataset = MOABBDataset(dataset_name="BI2014a", subject_ids=subject_ids)
  0%|                                              | 0.00/60.6M [00:00<?, ?B/s]
  0%|                                     | 13.3k/60.6M [00:00<11:25, 88.3kB/s]
  0%|                                      | 44.0k/60.6M [00:00<05:54, 171kB/s]
  0%|                                      | 95.2k/60.6M [00:00<03:43, 270kB/s]
  0%|                                       | 151k/60.6M [00:00<03:02, 331kB/s]
  0%|▏                                      | 236k/60.6M [00:00<02:15, 447kB/s]
  1%|▏                                      | 321k/60.6M [00:00<01:56, 517kB/s]
  1%|▎                                      | 397k/60.6M [00:00<01:50, 542kB/s]
  1%|▎                                      | 479k/60.6M [00:01<01:45, 569kB/s]
  1%|▎                                      | 561k/60.6M [00:01<01:42, 588kB/s]
  1%|▍                                      | 627k/60.6M [00:01<01:46, 562kB/s]
  1%|▍                                      | 692k/60.6M [00:01<01:49, 546kB/s]
  1%|▍                                      | 774k/60.6M [00:01<01:44, 573kB/s]
  1%|▌                                      | 831k/60.6M [00:01<01:52, 533kB/s]
  2%|▌                                      | 922k/60.6M [00:01<01:42, 581kB/s]
  2%|▋                                     | 1.00M/60.6M [00:01<01:39, 596kB/s]
  2%|▋                                     | 1.13M/60.6M [00:02<01:22, 720kB/s]
  2%|▊                                     | 1.22M/60.6M [00:02<01:25, 695kB/s]
  2%|▊                                     | 1.33M/60.6M [00:02<01:18, 751kB/s]
  2%|▉                                     | 1.43M/60.6M [00:02<01:18, 757kB/s]
  2%|▉                                     | 1.51M/60.6M [00:02<01:22, 719kB/s]
  3%|█                                     | 1.61M/60.6M [00:02<01:20, 734kB/s]
  3%|█                                     | 1.70M/60.6M [00:02<01:23, 703kB/s]
  3%|█                                     | 1.78M/60.6M [00:03<01:26, 681kB/s]
  3%|█▏                                    | 1.88M/60.6M [00:03<01:23, 703kB/s]
  3%|█▏                                    | 1.98M/60.6M [00:03<01:20, 724kB/s]
  4%|█▎                                    | 2.12M/60.6M [00:03<01:09, 846kB/s]
  4%|█▍                                    | 2.22M/60.6M [00:03<01:11, 819kB/s]
  4%|█▍                                    | 2.30M/60.6M [00:03<01:16, 764kB/s]
  4%|█▍                                    | 2.39M/60.6M [00:03<01:20, 727kB/s]
  4%|█▌                                    | 2.49M/60.6M [00:03<01:18, 738kB/s]
  4%|█▌                                    | 2.57M/60.6M [00:04<01:22, 706kB/s]
  4%|█▋                                    | 2.65M/60.6M [00:04<01:24, 683kB/s]
  5%|█▋                                    | 2.75M/60.6M [00:04<01:21, 708kB/s]
  5%|█▊                                    | 2.83M/60.6M [00:04<01:24, 687kB/s]
  5%|█▊                                    | 2.93M/60.6M [00:04<01:20, 713kB/s]
  5%|█▉                                    | 3.01M/60.6M [00:04<01:23, 687kB/s]
  5%|█▉                                    | 3.11M/60.6M [00:04<01:21, 708kB/s]
  5%|██                                    | 3.21M/60.6M [00:04<01:19, 724kB/s]
  5%|██                                    | 3.30M/60.6M [00:05<01:21, 702kB/s]
  6%|██                                    | 3.38M/60.6M [00:05<01:24, 680kB/s]
  6%|██▏                                   | 3.48M/60.6M [00:05<01:20, 705kB/s]
  6%|██▏                                   | 3.56M/60.6M [00:05<01:23, 685kB/s]
  6%|██▎                                   | 3.64M/60.6M [00:05<01:24, 670kB/s]
  6%|██▍                                   | 3.81M/60.6M [00:05<01:06, 849kB/s]
  7%|██▍                                   | 3.94M/60.6M [00:05<01:03, 897kB/s]
  7%|██▌                                   | 4.05M/60.6M [00:05<01:03, 889kB/s]
  7%|██▌                                   | 4.15M/60.6M [00:06<01:05, 856kB/s]
  7%|██▋                                   | 4.24M/60.6M [00:06<01:10, 799kB/s]
  7%|██▋                                   | 4.35M/60.6M [00:06<01:08, 820kB/s]
  7%|██▊                                   | 4.43M/60.6M [00:06<01:13, 764kB/s]
  7%|██▊                                   | 4.53M/60.6M [00:06<01:13, 764kB/s]
  8%|██▉                                   | 4.61M/60.6M [00:06<01:17, 724kB/s]
  8%|██▉                                   | 4.70M/60.6M [00:06<01:20, 696kB/s]
  8%|███                                   | 4.85M/60.6M [00:07<01:07, 831kB/s]
  8%|███                                   | 4.93M/60.6M [00:07<01:11, 776kB/s]
  8%|███▏                                  | 5.01M/60.6M [00:07<01:15, 731kB/s]
  9%|███▏                                  | 5.16M/60.6M [00:07<01:05, 852kB/s]
  9%|███▎                                  | 5.24M/60.6M [00:07<01:09, 794kB/s]
  9%|███▍                                  | 5.39M/60.6M [00:07<01:01, 891kB/s]
  9%|███▍                                  | 5.48M/60.6M [00:07<01:06, 833kB/s]
  9%|███▍                                  | 5.57M/60.6M [00:07<01:09, 792kB/s]
  9%|███▌                                  | 5.65M/60.6M [00:08<01:13, 745kB/s]
  9%|███▌                                  | 5.74M/60.6M [00:08<01:16, 713kB/s]
 10%|███▋                                  | 5.88M/60.6M [00:08<01:05, 839kB/s]
 10%|███▋                                  | 5.97M/60.6M [00:08<01:09, 781kB/s]
 10%|███▊                                  | 6.06M/60.6M [00:08<01:10, 769kB/s]
 10%|███▊                                  | 6.15M/60.6M [00:08<01:14, 728kB/s]
 10%|███▉                                  | 6.23M/60.6M [00:08<01:17, 699kB/s]
 10%|███▉                                  | 6.33M/60.6M [00:08<01:15, 716kB/s]
 11%|████                                  | 6.41M/60.6M [00:09<01:18, 694kB/s]
 11%|████                                  | 6.54M/60.6M [00:09<01:08, 791kB/s]
 11%|████▏                                 | 6.62M/60.6M [00:09<01:12, 743kB/s]
 11%|████▏                                 | 6.71M/60.6M [00:09<01:15, 709kB/s]
 11%|████▎                                 | 6.80M/60.6M [00:09<01:13, 727kB/s]
 11%|████▎                                 | 6.94M/60.6M [00:09<01:05, 814kB/s]
 12%|████▍                                 | 7.04M/60.6M [00:09<01:06, 801kB/s]
 12%|████▍                                 | 7.14M/60.6M [00:10<01:07, 788kB/s]
 12%|████▌                                 | 7.22M/60.6M [00:10<01:12, 741kB/s]
 12%|████▌                                 | 7.30M/60.6M [00:10<01:15, 708kB/s]
 12%|████▋                                 | 7.45M/60.6M [00:10<01:03, 840kB/s]
 12%|████▋                                 | 7.56M/60.6M [00:10<01:01, 855kB/s]
 13%|████▊                                 | 7.66M/60.6M [00:10<01:03, 828kB/s]
 13%|████▊                                 | 7.76M/60.6M [00:10<01:05, 806kB/s]
 13%|████▉                                 | 7.91M/60.6M [00:10<00:57, 909kB/s]
 13%|█████                                 | 8.00M/60.6M [00:11<01:02, 846kB/s]
 13%|█████                                 | 8.09M/60.6M [00:11<01:05, 803kB/s]
 13%|█████▏                                | 8.18M/60.6M [00:11<01:09, 751kB/s]
 14%|█████▏                                | 8.27M/60.6M [00:11<01:09, 751kB/s]
 14%|█████▎                                | 8.41M/60.6M [00:11<01:02, 832kB/s]
 14%|█████▎                                | 8.51M/60.6M [00:11<01:04, 809kB/s]
 14%|█████▍                                | 8.64M/60.6M [00:11<00:59, 868kB/s]
 14%|█████▍                                | 8.72M/60.6M [00:11<01:04, 808kB/s]
 15%|█████▌                                | 8.82M/60.6M [00:12<01:06, 781kB/s]
 15%|█████▌                                | 8.95M/60.6M [00:12<01:00, 849kB/s]
 15%|█████▋                                | 9.06M/60.6M [00:12<00:59, 864kB/s]
 15%|█████▋                               | 9.25M/60.6M [00:12<00:50, 1.02MB/s]
 15%|█████▊                                | 9.36M/60.6M [00:12<00:52, 982kB/s]
 16%|█████▊                               | 9.51M/60.6M [00:12<00:49, 1.03MB/s]
 16%|██████                                | 9.61M/60.6M [00:12<00:53, 958kB/s]
 16%|██████                                | 9.71M/60.6M [00:13<00:56, 895kB/s]
 16%|██████▏                               | 9.80M/60.6M [00:13<00:59, 846kB/s]
 16%|██████▏                               | 9.89M/60.6M [00:13<01:04, 788kB/s]
 17%|██████▎                               | 10.0M/60.6M [00:13<01:02, 810kB/s]
 17%|██████▎                               | 10.1M/60.6M [00:13<00:55, 912kB/s]
 17%|██████▍                               | 10.2M/60.6M [00:13<00:59, 852kB/s]
 17%|██████▍                               | 10.3M/60.6M [00:13<01:02, 802kB/s]
 17%|██████▌                               | 10.4M/60.6M [00:13<01:00, 826kB/s]
 17%|██████▌                               | 10.5M/60.6M [00:14<01:02, 804kB/s]
 18%|██████▋                               | 10.7M/60.6M [00:14<00:54, 910kB/s]
 18%|██████▋                              | 10.9M/60.6M [00:14<00:45, 1.09MB/s]
 18%|██████▊                              | 11.2M/60.6M [00:14<00:33, 1.49MB/s]
 19%|██████▉                              | 11.4M/60.6M [00:14<00:31, 1.58MB/s]
 20%|███████▎                             | 12.0M/60.6M [00:14<00:20, 2.32MB/s]
 20%|███████▌                             | 12.3M/60.6M [00:14<00:19, 2.44MB/s]
 22%|████████                             | 13.3M/60.6M [00:14<00:12, 3.89MB/s]
 23%|████████▍                            | 13.9M/60.6M [00:15<00:11, 4.21MB/s]
 25%|█████████▍                           | 15.4M/60.6M [00:15<00:06, 6.45MB/s]
 27%|█████████▉                           | 16.3M/60.6M [00:15<00:06, 6.69MB/s]
 30%|███████████                          | 18.2M/60.6M [00:15<00:04, 8.88MB/s]
 32%|████████████                         | 19.7M/60.6M [00:15<00:04, 9.73MB/s]
 34%|████████████▌                        | 20.6M/60.6M [00:15<00:04, 8.94MB/s]
 36%|█████████████▏                       | 21.5M/60.6M [00:15<00:04, 8.21MB/s]
 37%|█████████████▋                       | 22.4M/60.6M [00:16<00:06, 6.03MB/s]
 38%|██████████████                       | 23.0M/60.6M [00:16<00:06, 5.79MB/s]
 39%|██████████████▍                      | 23.7M/60.6M [00:16<00:06, 5.56MB/s]
 40%|██████████████▊                      | 24.3M/60.6M [00:16<00:06, 5.47MB/s]
 41%|███████████████▎                     | 25.1M/60.6M [00:16<00:06, 5.56MB/s]
 42%|███████████████▋                     | 25.7M/60.6M [00:17<00:11, 2.93MB/s]
 43%|████████████████                     | 26.3M/60.6M [00:17<00:10, 3.24MB/s]
 44%|████████████████▍                    | 26.8M/60.6M [00:17<00:09, 3.44MB/s]
 45%|████████████████▊                    | 27.5M/60.6M [00:17<00:08, 3.93MB/s]
 47%|█████████████████▎                   | 28.3M/60.6M [00:17<00:07, 4.42MB/s]
 48%|█████████████████▊                   | 29.2M/60.6M [00:17<00:06, 4.98MB/s]
 49%|██████████████████▎                  | 29.9M/60.6M [00:17<00:06, 4.95MB/s]
 51%|██████████████████▋                  | 30.7M/60.6M [00:18<00:06, 4.98MB/s]
 51%|███████████████████                  | 31.2M/60.6M [00:18<00:06, 4.73MB/s]
 55%|████████████████████▍                | 33.5M/60.6M [00:18<00:03, 8.31MB/s]
 59%|█████████████████████▊               | 35.8M/60.6M [00:18<00:02, 11.1MB/s]
 65%|████████████████████████             | 39.4M/60.6M [00:18<00:01, 15.9MB/s]
 70%|█████████████████████████▊           | 42.3M/60.6M [00:18<00:01, 17.9MB/s]
 76%|████████████████████████████▎        | 46.3M/60.6M [00:18<00:00, 23.2MB/s]
 80%|█████████████████████████████▊       | 48.7M/60.6M [00:18<00:00, 22.3MB/s]
 85%|███████████████████████████████▎     | 51.2M/60.6M [00:19<00:00, 23.0MB/s]
 90%|█████████████████████████████████▏   | 54.4M/60.6M [00:19<00:00, 24.5MB/s]
 97%|███████████████████████████████████▊ | 58.6M/60.6M [00:19<00:00, 26.7MB/s]
  0%|                                              | 0.00/60.6M [00:00<?, ?B/s]
100%|██████████████████████████████████████| 60.6M/60.6M [00:00<00:00, 215GB/s]

  0%|                                              | 0.00/29.6M [00:00<?, ?B/s]
  0%|                                   | 1.02k/29.6M [00:00<1:08:43, 7.17kB/s]
  0%|                                     | 14.3k/29.6M [00:00<07:46, 63.3kB/s]
  0%|                                      | 47.1k/29.6M [00:00<03:11, 154kB/s]
  0%|                                      | 93.2k/29.6M [00:00<02:02, 240kB/s]
  0%|▏                                      | 135k/29.6M [00:00<01:46, 278kB/s]
  1%|▏                                      | 182k/29.6M [00:00<01:33, 315kB/s]
  1%|▎                                      | 248k/29.6M [00:00<01:15, 388kB/s]
  1%|▍                                      | 325k/29.6M [00:00<01:02, 465kB/s]
  1%|▌                                      | 407k/29.6M [00:01<00:55, 528kB/s]
  2%|▋                                      | 488k/29.6M [00:01<00:50, 572kB/s]
  2%|▋                                      | 554k/29.6M [00:01<00:51, 561kB/s]
  2%|▊                                      | 636k/29.6M [00:01<00:48, 597kB/s]
  2%|▉                                      | 701k/29.6M [00:01<00:49, 579kB/s]
  3%|█                                      | 783k/29.6M [00:01<00:47, 606kB/s]
  3%|█▏                                     | 933k/29.6M [00:01<00:36, 791kB/s]
  4%|█▎                                    | 1.05M/29.6M [00:01<00:33, 840kB/s]
  4%|█▍                                    | 1.16M/29.6M [00:02<00:32, 868kB/s]
  4%|█▋                                    | 1.31M/29.6M [00:02<00:29, 969kB/s]
  5%|█▊                                    | 1.41M/29.6M [00:02<00:30, 919kB/s]
  5%|█▉                                    | 1.50M/29.6M [00:02<00:32, 868kB/s]
  5%|██                                    | 1.62M/29.6M [00:02<00:30, 904kB/s]
  6%|██▏                                   | 1.74M/29.6M [00:02<00:30, 917kB/s]
  6%|██▎                                   | 1.84M/29.6M [00:02<00:31, 882kB/s]
  7%|██▍                                   | 1.93M/29.6M [00:02<00:33, 835kB/s]
  7%|██▌                                   | 2.01M/29.6M [00:03<00:34, 790kB/s]
  7%|██▊                                   | 2.17M/29.6M [00:03<00:29, 937kB/s]
  8%|██▉                                   | 2.27M/29.6M [00:03<00:30, 898kB/s]
  8%|███                                   | 2.42M/29.6M [00:03<00:27, 992kB/s]
  9%|███▏                                 | 2.55M/29.6M [00:03<00:26, 1.02MB/s]
  9%|███▍                                  | 2.65M/29.6M [00:03<00:27, 965kB/s]
  9%|███▌                                  | 2.75M/29.6M [00:03<00:29, 914kB/s]
 10%|███▋                                  | 2.84M/29.6M [00:03<00:30, 873kB/s]
 10%|███▊                                  | 2.93M/29.6M [00:04<00:32, 826kB/s]
 10%|███▉                                  | 3.04M/29.6M [00:04<00:31, 844kB/s]
 11%|████                                  | 3.17M/29.6M [00:04<00:28, 912kB/s]
 11%|████▏                                | 3.32M/29.6M [00:04<00:26, 1.00MB/s]
 12%|████▍                                | 3.50M/29.6M [00:04<00:22, 1.15MB/s]
 12%|████▌                                | 3.67M/29.6M [00:04<00:21, 1.21MB/s]
 13%|████▋                                | 3.79M/29.6M [00:04<00:22, 1.14MB/s]
 13%|████▉                                | 3.91M/29.6M [00:04<00:23, 1.10MB/s]
 14%|█████                                | 4.02M/29.6M [00:05<00:24, 1.04MB/s]
 14%|█████▎                                | 4.13M/29.6M [00:05<00:25, 984kB/s]
 15%|█████▍                               | 4.31M/29.6M [00:05<00:22, 1.14MB/s]
 15%|█████▌                               | 4.44M/29.6M [00:05<00:22, 1.12MB/s]
 15%|█████▋                               | 4.56M/29.6M [00:05<00:23, 1.06MB/s]
 16%|█████▉                               | 4.75M/29.6M [00:05<00:20, 1.23MB/s]
 17%|██████▏                              | 4.94M/29.6M [00:05<00:18, 1.31MB/s]
 17%|██████▎                              | 5.07M/29.6M [00:05<00:19, 1.23MB/s]
 18%|██████▍                              | 5.19M/29.6M [00:06<00:20, 1.17MB/s]
 18%|██████▋                              | 5.32M/29.6M [00:06<00:21, 1.12MB/s]
 19%|██████▊                              | 5.50M/29.6M [00:06<00:19, 1.23MB/s]
 19%|███████▏                             | 5.74M/29.6M [00:06<00:16, 1.46MB/s]
 20%|███████▎                             | 5.89M/29.6M [00:06<00:17, 1.38MB/s]
 20%|███████▌                             | 6.03M/29.6M [00:06<00:18, 1.30MB/s]
 21%|███████▋                             | 6.16M/29.6M [00:06<00:19, 1.23MB/s]
 21%|████████                              | 6.28M/29.6M [00:06<00:25, 901kB/s]
 22%|████████▏                             | 6.38M/29.6M [00:07<00:26, 887kB/s]
 22%|████████▎                             | 6.48M/29.6M [00:07<00:26, 863kB/s]
 22%|████████▍                             | 6.60M/29.6M [00:07<00:26, 883kB/s]
 23%|████████▌                             | 6.70M/29.6M [00:07<00:26, 865kB/s]
 23%|████████▊                             | 6.81M/29.6M [00:07<00:25, 879kB/s]
 23%|████████▉                             | 6.91M/29.6M [00:07<00:26, 856kB/s]
 24%|█████████                             | 7.01M/29.6M [00:07<00:26, 840kB/s]
 24%|█████████▏                            | 7.13M/29.6M [00:07<00:25, 873kB/s]
 24%|█████████▎                            | 7.24M/29.6M [00:08<00:25, 890kB/s]
 25%|█████████▍                            | 7.33M/29.6M [00:08<00:26, 843kB/s]
 25%|█████████▌                            | 7.45M/29.6M [00:08<00:24, 890kB/s]
 26%|█████████▌                           | 7.67M/29.6M [00:08<00:19, 1.15MB/s]
 26%|█████████▋                           | 7.78M/29.6M [00:08<00:20, 1.09MB/s]
 27%|█████████▉                           | 7.95M/29.6M [00:08<00:18, 1.16MB/s]
 27%|██████████▏                          | 8.10M/29.6M [00:08<00:18, 1.18MB/s]
 28%|██████████▎                          | 8.21M/29.6M [00:08<00:19, 1.11MB/s]
 28%|██████████▍                          | 8.33M/29.6M [00:09<00:20, 1.05MB/s]
 29%|██████████▌                          | 8.44M/29.6M [00:09<00:20, 1.02MB/s]
 29%|██████████▊                          | 8.61M/29.6M [00:09<00:18, 1.12MB/s]
 29%|██████████▉                          | 8.72M/29.6M [00:09<00:19, 1.06MB/s]
 30%|███████████                          | 8.83M/29.6M [00:09<00:20, 1.00MB/s]
 30%|███████████▏                         | 8.95M/29.6M [00:09<00:20, 1.00MB/s]
 31%|███████████▋                          | 9.07M/29.6M [00:09<00:20, 984kB/s]
 31%|███████████▊                          | 9.18M/29.6M [00:09<00:20, 974kB/s]
 31%|███████████▉                          | 9.28M/29.6M [00:10<00:22, 922kB/s]
 32%|███████████▉                         | 9.49M/29.6M [00:10<00:17, 1.17MB/s]
 33%|████████████▏                        | 9.71M/29.6M [00:10<00:14, 1.34MB/s]
 34%|████████████▍                        | 9.92M/29.6M [00:10<00:13, 1.46MB/s]
 34%|████████████▌                        | 10.1M/29.6M [00:10<00:14, 1.38MB/s]
 35%|████████████▊                        | 10.2M/29.6M [00:10<00:14, 1.30MB/s]
 35%|████████████▉                        | 10.3M/29.6M [00:10<00:15, 1.23MB/s]
 36%|█████████████▏                       | 10.5M/29.6M [00:10<00:14, 1.30MB/s]
 36%|█████████████▍                       | 10.7M/29.6M [00:11<00:13, 1.36MB/s]
 37%|█████████████▌                       | 10.8M/29.6M [00:11<00:14, 1.28MB/s]
 37%|█████████████▋                       | 11.0M/29.6M [00:11<00:15, 1.21MB/s]
 38%|█████████████▉                       | 11.2M/29.6M [00:11<00:13, 1.33MB/s]
 38%|██████████████                       | 11.3M/29.6M [00:11<00:14, 1.25MB/s]
 39%|██████████████▎                      | 11.4M/29.6M [00:11<00:15, 1.19MB/s]
 39%|██████████████▍                      | 11.5M/29.6M [00:11<00:16, 1.12MB/s]
 39%|██████████████▌                      | 11.6M/29.6M [00:11<00:16, 1.06MB/s]
 40%|██████████████▋                      | 11.8M/29.6M [00:12<00:17, 1.00MB/s]
 40%|███████████████▏                      | 11.9M/29.6M [00:12<00:18, 950kB/s]
 40%|███████████████▎                      | 12.0M/29.6M [00:12<00:19, 903kB/s]
 41%|███████████████▍                      | 12.0M/29.6M [00:12<00:20, 865kB/s]
 41%|███████████████▋                      | 12.2M/29.6M [00:12<00:18, 927kB/s]
 42%|███████████████▊                      | 12.3M/29.6M [00:12<00:19, 894kB/s]
 42%|███████████████▉                      | 12.4M/29.6M [00:12<00:18, 947kB/s]
 42%|████████████████                      | 12.5M/29.6M [00:12<00:18, 901kB/s]
 43%|████████████████▏                     | 12.6M/29.6M [00:13<00:19, 853kB/s]
 43%|████████████████▎                     | 12.7M/29.6M [00:13<00:19, 861kB/s]
 43%|████████████████▍                     | 12.8M/29.6M [00:13<00:19, 843kB/s]
 44%|████████████████▌                     | 12.9M/29.6M [00:13<00:20, 831kB/s]
 44%|████████████████▋                     | 13.0M/29.6M [00:13<00:21, 786kB/s]
 44%|████████████████▉                     | 13.1M/29.6M [00:13<00:17, 951kB/s]
 45%|█████████████████                     | 13.3M/29.6M [00:13<00:16, 986kB/s]
 45%|█████████████████▏                    | 13.4M/29.6M [00:13<00:16, 971kB/s]
 46%|█████████████████▎                    | 13.5M/29.6M [00:13<00:17, 919kB/s]
 46%|█████████████████▍                    | 13.6M/29.6M [00:14<00:17, 927kB/s]
 46%|█████████████████▌                    | 13.7M/29.6M [00:14<00:18, 879kB/s]
 47%|█████████████████▊                    | 13.8M/29.6M [00:14<00:17, 914kB/s]
 47%|█████████████████▉                    | 13.9M/29.6M [00:14<00:17, 880kB/s]
 47%|██████████████████                    | 14.0M/29.6M [00:14<00:17, 897kB/s]
 48%|██████████████████▏                   | 14.2M/29.6M [00:14<00:15, 994kB/s]
 48%|█████████████████▉                   | 14.3M/29.6M [00:14<00:15, 1.02MB/s]
 49%|██████████████████▌                   | 14.4M/29.6M [00:14<00:15, 993kB/s]
 49%|██████████████████▋                   | 14.5M/29.6M [00:15<00:15, 980kB/s]
 50%|██████████████████▊                   | 14.6M/29.6M [00:15<00:16, 929kB/s]
 50%|██████████████████▌                  | 14.8M/29.6M [00:15<00:14, 1.01MB/s]
 51%|██████████████████▊                  | 15.0M/29.6M [00:15<00:12, 1.19MB/s]
 51%|██████████████████▉                  | 15.1M/29.6M [00:15<00:12, 1.13MB/s]
 52%|███████████████████                  | 15.2M/29.6M [00:15<00:13, 1.10MB/s]
 52%|███████████████████▏                 | 15.4M/29.6M [00:15<00:13, 1.09MB/s]
 52%|███████████████████▍                 | 15.5M/29.6M [00:15<00:12, 1.09MB/s]
 53%|███████████████████▌                 | 15.6M/29.6M [00:16<00:13, 1.04MB/s]
 53%|███████████████████▋                 | 15.8M/29.6M [00:16<00:12, 1.10MB/s]
 54%|███████████████████▊                 | 15.9M/29.6M [00:16<00:13, 1.04MB/s]
 54%|████████████████████▌                 | 16.0M/29.6M [00:16<00:13, 980kB/s]
 54%|████████████████████▏                | 16.1M/29.6M [00:16<00:13, 1.01MB/s]
 55%|████████████████████▎                | 16.2M/29.6M [00:16<00:12, 1.03MB/s]
 55%|████████████████████▍                | 16.4M/29.6M [00:16<00:13, 1.00MB/s]
 56%|█████████████████████▏                | 16.5M/29.6M [00:16<00:13, 945kB/s]
 56%|█████████████████████▎                | 16.6M/29.6M [00:17<00:13, 939kB/s]
 56%|█████████████████████▍                | 16.7M/29.6M [00:17<00:14, 902kB/s]
 57%|█████████████████████▌                | 16.8M/29.6M [00:17<00:15, 853kB/s]
 57%|█████████████████████▎               | 17.0M/29.6M [00:17<00:10, 1.18MB/s]
 58%|█████████████████████▍               | 17.1M/29.6M [00:17<00:11, 1.11MB/s]
 58%|█████████████████████▌               | 17.2M/29.6M [00:17<00:11, 1.04MB/s]
 59%|█████████████████████▋               | 17.3M/29.6M [00:17<00:12, 1.02MB/s]
 59%|██████████████████████▍               | 17.4M/29.6M [00:17<00:12, 961kB/s]
 59%|██████████████████████▌               | 17.6M/29.6M [00:18<00:12, 950kB/s]
 60%|██████████████████████▋               | 17.7M/29.6M [00:18<00:13, 898kB/s]
 60%|██████████████████████▊               | 17.7M/29.6M [00:18<00:13, 849kB/s]
 60%|██████████████████████▉               | 17.8M/29.6M [00:18<00:14, 829kB/s]
 61%|███████████████████████               | 17.9M/29.6M [00:18<00:14, 783kB/s]
 61%|███████████████████████▏              | 18.0M/29.6M [00:18<00:13, 826kB/s]
 61%|███████████████████████▎              | 18.2M/29.6M [00:18<00:12, 899kB/s]
 62%|███████████████████████▍              | 18.3M/29.6M [00:18<00:12, 910kB/s]
 62%|███████████████████████▌              | 18.4M/29.6M [00:19<00:13, 860kB/s]
 63%|███████████████████████▊              | 18.5M/29.6M [00:19<00:11, 986kB/s]
 63%|███████████████████████▉              | 18.6M/29.6M [00:19<00:11, 933kB/s]
 63%|████████████████████████              | 18.7M/29.6M [00:19<00:12, 892kB/s]
 64%|████████████████████████▏             | 18.8M/29.6M [00:19<00:11, 910kB/s]
 64%|████████████████████████▎             | 18.9M/29.6M [00:19<00:12, 878kB/s]
 64%|████████████████████████▍             | 19.1M/29.6M [00:19<00:11, 900kB/s]
 65%|████████████████████████▋             | 19.2M/29.6M [00:19<00:11, 911kB/s]
 65%|████████████████████████▊             | 19.3M/29.6M [00:19<00:11, 879kB/s]
 66%|████████████████████████▉             | 19.4M/29.6M [00:20<00:10, 936kB/s]
 66%|█████████████████████████             | 19.5M/29.6M [00:20<00:11, 886kB/s]
 66%|█████████████████████████▏            | 19.6M/29.6M [00:20<00:10, 916kB/s]
 67%|█████████████████████████▎            | 19.7M/29.6M [00:20<00:10, 962kB/s]
 67%|████████████████████████▉            | 19.9M/29.6M [00:20<00:09, 1.04MB/s]
 68%|█████████████████████████▋            | 20.0M/29.6M [00:20<00:09, 983kB/s]
 68%|█████████████████████████▊            | 20.1M/29.6M [00:20<00:10, 931kB/s]
 68%|█████████████████████████▉            | 20.2M/29.6M [00:20<00:10, 921kB/s]
 69%|██████████████████████████            | 20.3M/29.6M [00:21<00:10, 925kB/s]
 69%|██████████████████████████▏           | 20.4M/29.6M [00:21<00:10, 888kB/s]
 69%|██████████████████████████▎           | 20.5M/29.6M [00:21<00:10, 841kB/s]
 70%|██████████████████████████▌           | 20.6M/29.6M [00:21<00:09, 897kB/s]
 70%|██████████████████████████▋           | 20.8M/29.6M [00:21<00:09, 909kB/s]
 71%|██████████████████████████▊           | 20.9M/29.6M [00:21<00:09, 877kB/s]
 71%|██████████████████████████▉           | 21.0M/29.6M [00:21<00:09, 940kB/s]
 72%|██████████████████████████▍          | 21.2M/29.6M [00:21<00:07, 1.06MB/s]
 72%|██████████████████████████▋          | 21.3M/29.6M [00:22<00:07, 1.10MB/s]
 72%|██████████████████████████▊          | 21.4M/29.6M [00:22<00:07, 1.04MB/s]
 73%|███████████████████████████▋          | 21.5M/29.6M [00:22<00:08, 984kB/s]
 73%|███████████████████████████▊          | 21.6M/29.6M [00:22<00:08, 932kB/s]
 73%|███████████████████████████▉          | 21.7M/29.6M [00:22<00:08, 891kB/s]
 74%|████████████████████████████          | 21.8M/29.6M [00:22<00:08, 904kB/s]
 74%|████████████████████████████▏         | 22.0M/29.6M [00:22<00:07, 953kB/s]
 75%|███████████████████████████▋         | 22.1M/29.6M [00:22<00:07, 1.03MB/s]
 75%|████████████████████████████▌         | 22.2M/29.6M [00:23<00:07, 976kB/s]
 76%|████████████████████████████▋         | 22.3M/29.6M [00:23<00:07, 997kB/s]
 76%|████████████████████████████▊         | 22.4M/29.6M [00:23<00:07, 942kB/s]
 76%|████████████████████████████▉         | 22.5M/29.6M [00:23<00:07, 894kB/s]
 77%|█████████████████████████████         | 22.7M/29.6M [00:23<00:07, 946kB/s]
 77%|█████████████████████████████▎        | 22.8M/29.6M [00:23<00:07, 943kB/s]
 77%|█████████████████████████████▍        | 22.9M/29.6M [00:23<00:07, 901kB/s]
 78%|█████████████████████████████▌        | 23.0M/29.6M [00:23<00:07, 853kB/s]
 78%|█████████████████████████████▋        | 23.1M/29.6M [00:24<00:07, 861kB/s]
 78%|█████████████████████████████▊        | 23.2M/29.6M [00:24<00:07, 882kB/s]
 79%|█████████████████████████████▏       | 23.4M/29.6M [00:24<00:06, 1.02MB/s]
 80%|█████████████████████████████▌       | 23.6M/29.6M [00:24<00:04, 1.28MB/s]
 81%|█████████████████████████████▊       | 23.8M/29.6M [00:24<00:03, 1.46MB/s]
 81%|██████████████████████████████       | 24.0M/29.6M [00:24<00:03, 1.47MB/s]
 82%|██████████████████████████████▏      | 24.2M/29.6M [00:24<00:03, 1.47MB/s]
 82%|██████████████████████████████▍      | 24.3M/29.6M [00:24<00:03, 1.38MB/s]
 83%|██████████████████████████████▌      | 24.5M/29.6M [00:25<00:03, 1.31MB/s]
 83%|███████████████████████████████▌      | 24.6M/29.6M [00:25<00:05, 956kB/s]
 84%|███████████████████████████████▋      | 24.7M/29.6M [00:25<00:05, 937kB/s]
 84%|███████████████████████████████▉      | 24.8M/29.6M [00:25<00:04, 978kB/s]
 84%|███████████████████████████████▏     | 25.0M/29.6M [00:25<00:04, 1.01MB/s]
 85%|████████████████████████████████▏     | 25.1M/29.6M [00:25<00:04, 988kB/s]
 85%|████████████████████████████████▎     | 25.2M/29.6M [00:25<00:04, 943kB/s]
 86%|████████████████████████████████▌     | 25.3M/29.6M [00:25<00:04, 970kB/s]
 86%|████████████████████████████████▋     | 25.4M/29.6M [00:26<00:04, 960kB/s]
 87%|████████████████████████████████▏    | 25.8M/29.6M [00:26<00:02, 1.47MB/s]
 88%|████████████████████████████████▋    | 26.1M/29.6M [00:26<00:01, 1.88MB/s]
 90%|█████████████████████████████████▏   | 26.5M/29.6M [00:26<00:01, 2.41MB/s]
 91%|█████████████████████████████████▊   | 27.1M/29.6M [00:26<00:00, 2.94MB/s]
 94%|██████████████████████████████████▋  | 27.7M/29.6M [00:26<00:00, 3.67MB/s]
 96%|███████████████████████████████████▎ | 28.3M/29.6M [00:26<00:00, 3.96MB/s]
 99%|████████████████████████████████████▍| 29.2M/29.6M [00:26<00:00, 4.96MB/s]
29.8MB [00:27, 4.98MB/s]
  0%|                                              | 0.00/29.6M [00:00<?, ?B/s]
100%|██████████████████████████████████████| 29.6M/29.6M [00:00<00:00, 171GB/s]

  0%|                                              | 0.00/87.9M [00:00<?, ?B/s]
  0%|                                     | 13.3k/87.9M [00:00<16:00, 91.5kB/s]
  0%|                                      | 41.0k/87.9M [00:00<08:50, 166kB/s]
  0%|                                       | 100k/87.9M [00:00<04:45, 307kB/s]
  0%|                                       | 155k/87.9M [00:00<04:03, 361kB/s]
  0%|                                       | 204k/87.9M [00:00<03:52, 377kB/s]
  0%|                                       | 253k/87.9M [00:00<03:46, 386kB/s]
  0%|▏                                      | 304k/87.9M [00:00<03:40, 398kB/s]
  0%|▏                                      | 387k/87.9M [00:00<02:59, 488kB/s]
  1%|▏                                      | 453k/87.9M [00:01<02:53, 504kB/s]
  1%|▏                                      | 537k/87.9M [00:01<02:53, 505kB/s]
  1%|▎                                      | 635k/87.9M [00:01<02:27, 591kB/s]
  1%|▎                                      | 735k/87.9M [00:01<02:12, 656kB/s]
  1%|▎                                      | 834k/87.9M [00:01<02:04, 699kB/s]
  1%|▍                                      | 932k/87.9M [00:01<01:58, 732kB/s]
  1%|▍                                     | 1.10M/87.9M [00:01<01:35, 905kB/s]
  1%|▌                                     | 1.20M/87.9M [00:02<01:38, 878kB/s]
  1%|▌                                     | 1.28M/87.9M [00:02<01:44, 831kB/s]
  2%|▌                                     | 1.38M/87.9M [00:02<01:46, 813kB/s]
  2%|▋                                     | 1.46M/87.9M [00:02<01:52, 770kB/s]
  2%|▋                                     | 1.61M/87.9M [00:02<01:36, 890kB/s]
  2%|▊                                     | 1.74M/87.9M [00:02<01:30, 949kB/s]
  2%|▊                                    | 1.89M/87.9M [00:02<01:23, 1.02MB/s]
  2%|▊                                     | 1.99M/87.9M [00:02<01:28, 970kB/s]
  2%|▉                                     | 2.09M/87.9M [00:03<01:33, 919kB/s]
  3%|▉                                     | 2.20M/87.9M [00:03<01:33, 915kB/s]
  3%|▉                                     | 2.30M/87.9M [00:03<01:36, 885kB/s]
  3%|█                                     | 2.40M/87.9M [00:03<01:39, 861kB/s]
  3%|█                                     | 2.50M/87.9M [00:03<01:41, 843kB/s]
  3%|█                                     | 2.58M/87.9M [00:03<01:46, 800kB/s]
  3%|█▏                                    | 2.66M/87.9M [00:03<01:52, 758kB/s]
  3%|█▏                                    | 2.78M/87.9M [00:03<01:45, 807kB/s]
  3%|█▎                                    | 2.92M/87.9M [00:03<01:31, 931kB/s]
  3%|█▎                                    | 3.02M/87.9M [00:04<01:36, 880kB/s]
  4%|█▎                                    | 3.11M/87.9M [00:04<01:42, 830kB/s]
  4%|█▍                                    | 3.19M/87.9M [00:04<01:47, 788kB/s]
  4%|█▍                                   | 3.39M/87.9M [00:04<01:21, 1.04MB/s]
  4%|█▌                                   | 3.65M/87.9M [00:04<01:01, 1.37MB/s]
  4%|█▋                                   | 3.93M/87.9M [00:04<00:50, 1.65MB/s]
  5%|█▊                                   | 4.37M/87.9M [00:04<00:37, 2.22MB/s]
  5%|██                                   | 4.80M/87.9M [00:04<00:31, 2.62MB/s]
  6%|██▎                                  | 5.55M/87.9M [00:05<00:22, 3.68MB/s]
  7%|██▌                                  | 6.10M/87.9M [00:05<00:20, 3.92MB/s]
  8%|███                                  | 7.28M/87.9M [00:05<00:14, 5.66MB/s]
 10%|███▋                                 | 8.65M/87.9M [00:05<00:10, 7.30MB/s]
 12%|████▍                                | 10.5M/87.9M [00:05<00:08, 9.64MB/s]
 14%|█████▏                               | 12.3M/87.9M [00:05<00:06, 11.0MB/s]
 16%|██████                               | 14.3M/87.9M [00:05<00:05, 12.4MB/s]
 18%|██████▋                              | 15.8M/87.9M [00:05<00:05, 12.3MB/s]
 20%|███████▍                             | 17.8M/87.9M [00:06<00:05, 13.4MB/s]
 22%|████████                             | 19.1M/87.9M [00:06<00:05, 12.3MB/s]
 23%|████████▌                            | 20.3M/87.9M [00:06<00:07, 9.02MB/s]
 24%|████████▉                            | 21.4M/87.9M [00:06<00:09, 7.16MB/s]
 25%|█████████▎                           | 22.2M/87.9M [00:06<00:09, 7.03MB/s]
 26%|█████████▋                           | 23.0M/87.9M [00:07<00:11, 5.66MB/s]
 27%|█████████▉                           | 23.6M/87.9M [00:07<00:11, 5.58MB/s]
 28%|██████████▏                          | 24.2M/87.9M [00:07<00:11, 5.43MB/s]
 28%|██████████▍                          | 24.8M/87.9M [00:07<00:12, 5.25MB/s]
 29%|██████████▋                          | 25.4M/87.9M [00:07<00:11, 5.28MB/s]
 29%|██████████▉                          | 25.9M/87.9M [00:07<00:12, 4.82MB/s]
 30%|███████████                          | 26.4M/87.9M [00:07<00:12, 4.83MB/s]
 31%|███████████▎                         | 26.9M/87.9M [00:07<00:13, 4.62MB/s]
 31%|███████████▌                         | 27.4M/87.9M [00:08<00:13, 4.48MB/s]
 32%|███████████▊                         | 28.0M/87.9M [00:08<00:12, 4.63MB/s]
 33%|████████████                         | 28.6M/87.9M [00:08<00:12, 4.73MB/s]
 33%|████████████▎                        | 29.1M/87.9M [00:08<00:12, 4.66MB/s]
 34%|████████████▍                        | 29.6M/87.9M [00:08<00:13, 4.47MB/s]
 34%|████████████▋                        | 30.1M/87.9M [00:08<00:13, 4.35MB/s]
 35%|████████████▉                        | 30.7M/87.9M [00:08<00:12, 4.54MB/s]
 36%|█████████████▏                       | 31.3M/87.9M [00:08<00:12, 4.63MB/s]
 36%|█████████████▎                       | 31.8M/87.9M [00:08<00:12, 4.37MB/s]
 37%|█████████████▌                       | 32.4M/87.9M [00:09<00:12, 4.51MB/s]
 37%|█████████████▊                       | 33.0M/87.9M [00:09<00:11, 4.62MB/s]
 38%|██████████████                       | 33.4M/87.9M [00:09<00:12, 4.41MB/s]
 39%|██████████████▎                      | 34.1M/87.9M [00:09<00:11, 4.58MB/s]
 39%|██████████████▌                      | 34.7M/87.9M [00:09<00:12, 4.33MB/s]
 40%|██████████████▊                      | 35.3M/87.9M [00:09<00:11, 4.46MB/s]
 41%|███████████████                      | 35.9M/87.9M [00:09<00:11, 4.64MB/s]
 41%|███████████████▎                     | 36.4M/87.9M [00:10<00:11, 4.37MB/s]
 42%|███████████████▌                     | 36.8M/87.9M [00:10<00:12, 4.15MB/s]
 43%|███████████████▋                     | 37.4M/87.9M [00:10<00:12, 4.20MB/s]
 43%|███████████████▉                     | 37.9M/87.9M [00:10<00:11, 4.35MB/s]
 44%|████████████████▏                    | 38.5M/87.9M [00:10<00:10, 4.53MB/s]
 45%|████████████████▍                    | 39.2M/87.9M [00:10<00:10, 4.66MB/s]
 45%|████████████████▋                    | 39.7M/87.9M [00:10<00:10, 4.67MB/s]
 46%|████████████████▉                    | 40.2M/87.9M [00:10<00:10, 4.54MB/s]
 46%|█████████████████                    | 40.7M/87.9M [00:10<00:10, 4.31MB/s]
 47%|█████████████████▎                   | 41.1M/87.9M [00:11<00:11, 4.15MB/s]
 47%|█████████████████▌                   | 41.7M/87.9M [00:11<00:10, 4.44MB/s]
 48%|█████████████████▊                   | 42.4M/87.9M [00:11<00:09, 4.56MB/s]
 49%|██████████████████                   | 43.0M/87.9M [00:11<00:09, 4.56MB/s]
 50%|██████████████████▎                  | 43.6M/87.9M [00:11<00:09, 4.75MB/s]
 50%|██████████████████▌                  | 44.2M/87.9M [00:11<00:09, 4.70MB/s]
 51%|██████████████████▊                  | 44.8M/87.9M [00:11<00:08, 4.81MB/s]
 52%|███████████████████                  | 45.3M/87.9M [00:11<00:09, 4.59MB/s]
 52%|███████████████████▎                 | 45.9M/87.9M [00:12<00:08, 4.81MB/s]
 53%|███████████████████▌                 | 46.6M/87.9M [00:12<00:08, 4.89MB/s]
 54%|████████████████████▏                | 47.9M/87.9M [00:12<00:05, 7.00MB/s]
 58%|█████████████████████▎               | 50.7M/87.9M [00:12<00:03, 12.1MB/s]
 60%|██████████████████████▎              | 53.1M/87.9M [00:12<00:02, 14.5MB/s]
 64%|███████████████████████▋             | 56.3M/87.9M [00:12<00:01, 18.1MB/s]
 69%|█████████████████████████▍           | 60.4M/87.9M [00:12<00:01, 22.8MB/s]
 73%|███████████████████████████▏         | 64.4M/87.9M [00:12<00:00, 27.3MB/s]
 77%|████████████████████████████▋        | 68.0M/87.9M [00:13<00:00, 26.2MB/s]
 82%|██████████████████████████████▎      | 72.1M/87.9M [00:13<00:00, 28.0MB/s]
 87%|████████████████████████████████     | 76.2M/87.9M [00:13<00:00, 29.6MB/s]
 91%|█████████████████████████████████▋   | 80.2M/87.9M [00:13<00:00, 30.2MB/s]
 96%|███████████████████████████████████▍ | 84.3M/87.9M [00:13<00:00, 31.1MB/s]
100%|████████████████████████████████████▉| 87.9M/87.9M [00:13<00:00, 30.3MB/s]
  0%|                                              | 0.00/87.9M [00:00<?, ?B/s]
100%|██████████████████████████████████████| 87.9M/87.9M [00:00<00:00, 369GB/s]

Preprocessing#

We reproduce the exact minimal preprocessing recipe of the DANCE paper [1]: band-pass filter between 0.1 and 100 Hz, resample to a common 128 Hz, then a per-channel, per-session robust scaling (subtract the median, divide by the interquartile range) clamped to [-16, 16]. We reach for sklearn.preprocessing.robust_scale() rather than hand-rolling the median/IQR arithmetic. axis=1 scales each channel (row) using its own median and interquartile range computed across time (columns), which is exactly the per-channel recipe above.

import numpy as np
from sklearn.preprocessing import robust_scale

from braindecode.preprocessing import Preprocessor, preprocess


def robust_scale_clamp(data):
    """Per-channel median/IQR scaling, clamped to [-16, 16] (DANCE recipe)."""
    return np.clip(robust_scale(data, axis=1), -16, 16)


SFREQ = 128.0
preprocessors = [
    Preprocessor("pick_types", eeg=True, stim=False),
    Preprocessor("filter", l_freq=0.1, h_freq=100.0),
    Preprocessor("resample", sfreq=SFREQ),
    Preprocessor(robust_scale_clamp, apply_on_array=True),
]
preprocess(dataset, preprocessors)
BaseConcatDataset
TypeBaseConcatDataset of RawDataset
Recordings3
Total samples299070
Sfreq*128.0 Hz
Channels*16 (16 EEG)
Ch. names*Fp1, Fp2, F3, AFz, F4, T7, Cz, T8, P7, P3, ... (+6 more)
Montage*head
Duration*816.3 s
* from first recording
Description3 recordings × 3 columns [subject, session, run]


Cut fixed-length windows and read the flash events#

DANCE consumes long, fixed-length windows rather than per-trial epochs, so we use create_fixed_length_windows() instead of create_windows_from_events(). We use W = 32 s and up to 150 events per window, matching the paper’s BI2014a configuration (mean 45.8 events/window in the full dataset).

from braindecode.preprocessing import create_fixed_length_windows

WINDOW_S, N_CLASSES, NUM_LATENTS, MAX_EVENTS = 32.0, 3, 256, 150
WINDOW_SAMPLES = int(WINDOW_S * SFREQ)

windows_ds = create_fixed_length_windows(
    dataset,
    window_size_samples=WINDOW_SAMPLES,
    window_stride_samples=WINDOW_SAMPLES,
    drop_last_window=True,
    preload=True,
    use_mne_epochs=False,  # guarantees EEGWindowsDataset -> (X, y, crop_inds)
)


def bi_annotations_to_events(raw):
    """Read ``(start_s, end_s, class_int)`` flash events from a BI* raw.

    Class ``1`` = non-target flash, class ``2`` = target flash (class ``0``
    is reserved for background/no-event, per the CLASS-0 CONTRACT).
    """
    label_to_class = {"NonTarget": 1, "Target": 2}
    events = []
    for ann in raw.annotations:
        cls = label_to_class.get(str(ann["description"]))
        if cls is None:
            continue
        events.append((float(ann["onset"]), float(ann["onset"] + ann["duration"]), cls))
    return events

Map each window’s annotations to DANCE targets#

This is the code that builds the \(y\) from the right-hand panel of the figure above. For one window \(X\), the paper [1] defines the target as a set of events

\[y = \{e_i\}_{i=1}^N, \qquad e_i = (b_i, c_i),\]

where \(b_i = (t_{\mathrm{start}}, t_{\mathrm{end}}) \in [0, 1]^2\) are the event’s boundaries normalized to the window duration (exactly the colored spans in the figure), and \(c_i \in \{0, \dots, K\}\) is its class. \(c_i = 0\) is the shared background/no-object class (the unlabeled gray span in the figure); real classes are \(1, \dots, K\) (here \(K = 2\): non-target and target flashes).

Two consequences follow, and both matter for dance_target_builder below. Padding is indistinguishable from background: DanceLoss’s matcher expects a fixed-size set of max_events slots per window, so any slot with no real event defaults to \(c_i = 0\), the same value that also denotes “no object”. There is only one id for both, by design, and it never collides because a real annotation is never given class 0. The dense head also needs a per-token version of the same set: DANCE trains a per-timestep classifier over num_latents latent tokens, so we rasterize each event’s \([t_{\mathrm{start}}, t_{\mathrm{end}})\) span onto that token grid, and every idle token defaults to the same background class 0.

This matches braindecode’s own upstream reference: the dance/example/data.py MOABB bridge in facebookresearch/dance uses class 0 for padding/background, 1 for non-target, and 2 for target on this exact dataset, with events zero-padded to max_events = 150.

import torch


def dance_target_builder(
    annotations, window_onset, window_duration, max_events, num_latents
):
    """Build one window's DANCE target: the event set y and its dense grid."""
    start = torch.zeros(max_events)
    end = torch.zeros(max_events)
    cls = torch.zeros(max_events, dtype=torch.long)  # cls[i] = 0 -> background/padding
    w0, wd = window_onset, window_duration
    kept = 0
    for s, e, c in annotations:
        # clip the annotation to the window and normalize to [0, 1]
        s_c, e_c = max(s, w0), min(e, w0 + wd)
        if e_c <= s_c or int(c) == 0 or kept >= max_events:
            continue  # outside the window, background, or out of query slots
        start[kept] = (s_c - w0) / wd
        end[kept] = (e_c - w0) / wd
        cls[kept] = int(c)
        kept += 1

    # rasterize the kept events onto the num_latents-token grid: dense[t] is
    # the class of whichever event (if any) covers token t
    dense = torch.zeros(num_latents, dtype=torch.long)
    s_tok = (start * num_latents).clamp(0, num_latents).long()
    e_tok = (end * num_latents).clamp(0, num_latents).long()
    for i in range(kept):
        a, b = int(s_tok[i]), int(e_tok[i])
        if a < b:
            dense[a:b] = int(cls[i])
    return {"start": start, "end": end, "class": cls, "dense": dense}


def dance_collate(batch):
    """Stack ``[(eeg, target_dict), ...]`` into the batched dict schema
    :class:`~braindecode.training.DanceLoss` expects.
    """
    eeg = torch.stack([b[0] for b in batch])
    out = {"eeg": eeg}
    for key in ("start", "end", "class", "dense"):
        out[key] = torch.stack([b[1][key] for b in batch])
    return out


raw_events = {
    ds.description["subject"]: bi_annotations_to_events(ds.raw)
    for ds in windows_ds.datasets
}
metadata = windows_ds.get_metadata()

samples, sample_subjects = [], []
for i in range(len(windows_ds)):
    x, _, crop_inds = windows_ds[i]
    eeg = torch.as_tensor(np.asarray(x), dtype=torch.float32)
    subject = int(metadata.iloc[i]["subject"])
    window_onset = float(crop_inds[1]) / SFREQ  # i_start_in_trial / sfreq
    target = dance_target_builder(
        raw_events[subject],
        window_onset=window_onset,
        window_duration=WINDOW_S,
        max_events=MAX_EVENTS,
        num_latents=NUM_LATENTS,
    )
    samples.append((eeg, target))
    sample_subjects.append(subject)

Splitting into train and test sets (cross-subject)#

We hold out the last subject entirely for evaluation, mirroring the paper’s cross-subject splitting protocol (train and test subjects never overlap).

from torch.utils.data import DataLoader

sample_subjects = np.asarray(sample_subjects)
test_subject = subject_ids[-1]
train_idx = np.flatnonzero(sample_subjects != test_subject)
test_idx = np.flatnonzero(sample_subjects == test_subject)
train_samples = [samples[i] for i in train_idx]
test_samples = [samples[i] for i in test_idx]
print(f"{len(train_samples)} train windows, {len(test_samples)} test windows")

train_loader = DataLoader(
    train_samples,
    batch_size=len(train_samples),
    shuffle=True,
    collate_fn=dance_collate,
)
test_loader = DataLoader(
    test_samples,
    batch_size=len(test_samples),
    collate_fn=dance_collate,
)
37 train windows, 35 test windows

Create the model and the criterion#

from braindecode.models import DANCE
from braindecode.training import DanceLoss, f1_event
from braindecode.util import set_random_seeds

cuda = torch.cuda.is_available()
device = "cuda" if cuda else "cpu"
set_random_seeds(seed=random_state, cuda=cuda)

chs_info = windows_ds.datasets[0].raw.info["chs"]
model = DANCE(
    n_outputs=N_CLASSES,
    n_chans=len(chs_info),
    chs_info=chs_info,
    n_times=WINDOW_SAMPLES,
    sfreq=SFREQ,
    input_window_seconds=WINDOW_S,
).to(device)
criterion = DanceLoss(num_latents=NUM_LATENTS)
optimizer = torch.optim.Adam(model.parameters(), lr=5e-4)

Train the model#

detect() returns the query set + dense map that DanceLoss needs; forward() alone would only give the dense per-token logits.

Warning

Kept at 2 epochs and 3 subjects so the docs build stays fast. This is not a converged model: the paper trains on the full dataset (71 subjects) for up to 100 epochs with early stopping. See the Conclusion.

n_epochs = 2
model.train()
for epoch in range(n_epochs):
    epoch_loss = 0.0
    for batch in train_loader:
        batch = {k: v.to(device) for k, v in batch.items()}
        optimizer.zero_grad()
        out = model.detect(batch["eeg"])
        loss, details = criterion(out, batch, duration=WINDOW_S)
        loss.backward()
        optimizer.step()
        epoch_loss += float(loss)
    print(f"Epoch {epoch + 1}/{n_epochs}: loss={epoch_loss / len(train_loader):.3f}")
Epoch 1/2: loss=11.305
Epoch 2/2: loss=9.444

Evaluate on the held-out subject#

f1_event() scores the decoded event set (IoU > 0.5 + class match); the per-token macro F1 scores the dense head against the dense target with sklearn.metrics.f1_score().

def detections_to_events(detections, duration):
    """Decode a DANCE ``detect()`` output into per-window event tuples.

    Each query becomes ``(start_s, end_s, class, confidence)`` in seconds
    within the window; queries whose argmax class is ``0``
    (background/no-object) are dropped, following the CLASS-0 CONTRACT.
    """
    probs = torch.softmax(detections["class"], dim=-1)  # (B, Q, K+1)
    confidence, label = probs.max(dim=-1)  # (B, Q), one softmax call for the batch
    start = detections["start"] * duration
    end = detections["end"] * duration
    events = []
    for bi in range(label.shape[0]):
        keep = label[bi] != 0
        events.append(
            list(
                zip(
                    start[bi, keep].tolist(),
                    end[bi, keep].tolist(),
                    label[bi, keep].tolist(),
                    confidence[bi, keep].tolist(),
                )
            )
        )
    return events


from sklearn.metrics import f1_score

model.eval()
ev_f1s = []
dense_preds, dense_targets = [], []
with torch.no_grad():
    for batch in test_loader:
        batch = {k: v.to(device) for k, v in batch.items()}
        out = model.detect(batch["eeg"])
        pred_events = detections_to_events(out, duration=WINDOW_S)
        for bi in range(batch["eeg"].shape[0]):
            # ground-truth events of this window, in seconds within the window
            gt = [
                (float(s) * WINDOW_S, float(e) * WINDOW_S, int(c))
                for s, e, c in zip(
                    batch["start"][bi], batch["end"][bi], batch["class"][bi]
                )
                if int(c) != 0
            ]
            preds = [(s, e, c) for (s, e, c, _conf) in pred_events[bi]]
            ev_f1s.append(f1_event(preds, gt, iou_threshold=0.5))
        # dense head (B, T, n_outputs) vs dense target (B, T), one token per prediction
        dense_preds.append(out["dense"].argmax(-1).reshape(-1).cpu())
        dense_targets.append(batch["dense"].reshape(-1).cpu())

dense_preds = torch.cat(dense_preds).numpy()
dense_targets = torch.cat(dense_targets).numpy()
sample_f1 = f1_score(
    dense_targets, dense_preds, labels=list(range(N_CLASSES)), average="macro"
)
print(
    f"Held-out subject {test_subject}: "
    f"F1-event={np.mean(ev_f1s):.3f}  F1-sample={sample_f1:.3f}"
)
Held-out subject 3: F1-event=0.014  F1-sample=0.246

Predicting with the official pretrained checkpoint#

Note

braindecode has not yet published an official pretrained checkpoint for DANCE on the Hugging Face Hub, unlike the sleep-staging tutorials. The cell below uses the same loading pattern as the rest of the gallery, so it starts working automatically once a checkpoint is released. Until then it falls back to the model trained above and predicts on one held-out window, so the cell still runs end-to-end. Training longer closes most of that gap: the 2-epoch, 3-subject model above is far from converged, and the paper’s full protocol (all subjects, up to 100 epochs with early stopping) is what a released checkpoint would reflect.

import warnings

repo_id = "braindecode/plot_dance_event_detection"
try:
    from huggingface_hub import hf_hub_download

    model.load_state_dict(
        torch.load(hf_hub_download(repo_id, "model.pt"), map_location=device)
    )
    print(f"Loaded the official pretrained checkpoint from {repo_id}.")
except Exception as exc:
    warnings.warn(
        f"Could not load a pretrained checkpoint from {repo_id} ({exc}); "
        "predicting with the locally trained short-run model instead.",
        stacklevel=2,
    )

model.eval()
with torch.no_grad():
    example = test_samples[0][0].unsqueeze(0).to(device)
    out = model.detect(example)
predicted = detections_to_events(out, duration=WINDOW_S)[0]
print(f"{len(predicted)} events predicted on the first held-out window:")
for s, e, label, conf in sorted(predicted, key=lambda ev: ev[0])[:10]:
    print(f"  [{s:5.2f}s, {e:5.2f}s]  class={label}  confidence={conf:.2f}")
Loaded the official pretrained checkpoint from braindecode/plot_dance_event_detection.
51 events predicted on the first held-out window:
  [ 0.00s,  0.36s]  class=1  confidence=0.99
  [ 0.02s,  0.98s]  class=2  confidence=0.96
  [ 0.02s,  0.82s]  class=1  confidence=0.98
  [ 0.63s,  1.41s]  class=1  confidence=0.86
  [ 1.30s,  2.05s]  class=1  confidence=0.95
  [ 1.87s,  2.74s]  class=1  confidence=0.93
  [ 2.35s,  3.22s]  class=1  confidence=0.75
  [ 2.57s,  4.13s]  class=2  confidence=0.95
  [ 4.68s,  5.70s]  class=1  confidence=0.64
  [ 5.72s,  6.65s]  class=1  confidence=0.85

Conclusion#

This tutorial trained DANCE on real, continuous P300 EEG, detecting target and non-target flashes directly from unaligned windows, without ever being told where a flash starts. At this reduced scale (3 subjects, 2 epochs), the reported F1 scores are low and should not be compared to the paper’s numbers.

At full scale, following the paper’s protocol (BI2014a, all subjects, up to 100 epochs with early stopping and a OneCycle learning rate schedule), DANCE matches onset-informed models on this dataset without ever using the flash onset, and sets a new state of the art on the harder task of seizure monitoring (Temple University Seizure corpus), where onsets are unknown by construction. That is the core claim: one architecture performing on par with onset-informed baselines while requiring no onset information at all.

References#

Total running time of the script: (6 minutes 22.783 seconds)

Estimated memory usage: 8806 MB

Gallery generated by Sphinx-Gallery