Skip to content

Commit 124191e

Browse files
Polish tutorial per EEGLAB manager feedback
- Remove detailed trial balancing section, keep brief note only - Remove comparing conditions section (no plots shown) - Add code that generates the envelope visualization figure
1 parent 98a5b85 commit 124191e

File tree

1 file changed

+46
-199
lines changed

1 file changed

+46
-199
lines changed

tutorials/misc/EEGLAB_and_EMG_data.md

Lines changed: 46 additions & 199 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,49 @@ This figure shows the four processing stages for two representative channels (le
377377
3. **Rectified** (purple): Absolute value of filtered signal - all values positive
378378
4. **Low-pass filter / linear envelope** (magenta): Smooth envelope (20 Hz cutoff) capturing muscle activation amplitude
379379

380+
Code to generate this visualization:
381+
382+
```matlab
383+
%% Visualize the envelope computation process
384+
figure('Position', [100, 100, 1200, 800]);
385+
386+
% Select a time window with keystroke events (6 seconds)
387+
time_window = [60, 66];
388+
samples_window = round(time_window * EEG.srate);
389+
time_vec = (samples_window(1):samples_window(2)-1) / EEG.srate;
390+
391+
% Select two representative channels (one left, one right wristband)
392+
channels_to_plot = [1, 17]; % EMG0 (left), EMG16 (right)
393+
channel_names = {'Left Wristband (EMG0)', 'Right Wristband (EMG16)'};
394+
395+
% Colors for each stage
396+
colors = {[0.2 0.4 0.8], [0.3 0.7 0.3], [0.6 0.3 0.7], [0.8 0.2 0.5]};
397+
stage_labels = {'raw signal', 'band-pass filter', 'rectified', 'low-pass filter (envelope)'};
398+
399+
for col = 1:length(channels_to_plot)
400+
chan_idx = channels_to_plot(col);
401+
402+
% Get data for each stage (assuming EEG_raw, EEG_filtered, EEG are available)
403+
data_raw = EEG_raw.data(chan_idx, samples_window(1)+1:samples_window(2));
404+
data_bandpass = EEG_filtered.data(chan_idx, samples_window(1)+1:samples_window(2));
405+
data_rectified = abs(data_bandpass);
406+
data_envelope = EEG.data(chan_idx, samples_window(1)+1:samples_window(2));
407+
408+
all_data = {data_raw, data_bandpass, data_rectified, data_envelope};
409+
410+
for row = 1:4
411+
subplot(4, 2, (row-1)*2 + col);
412+
plot(time_vec, all_data{row}, 'Color', colors{row}, 'LineWidth', 1);
413+
414+
if row == 1, title(channel_names{col}); end
415+
if row == 4, xlabel('Time (s)'); end
416+
if col == 1, ylabel(stage_labels{row}); end
417+
grid on;
418+
end
419+
end
420+
sgtitle('Linear Envelope Computation Process');
421+
```
422+
380423
### Parameters for envelope computation
381424

382425
**Envelope low-pass cutoff frequency:**
@@ -422,167 +465,10 @@ baseline_window = [-0.5, -0.1]; % seconds
422465
EEG = pop_rmbase(EEG, baseline_window * 1000); % Convert to ms
423466
```
424467

425-
## Balancing trial counts for fair comparison
426-
427-
**Important:** When comparing ERPs across conditions, unequal trial counts can bias results due to different signal-to-noise ratios.
428-
429-
### Why trial balancing matters
430-
431-
In typing data, key occurrence varies dramatically:
432-
- Common keys (e, t, a): 200-500 occurrences
433-
- Rare keys (z, q, x): 5-20 occurrences
434-
435-
**Problems with unbalanced comparisons:**
436-
- High-trial-count condition has better SNR (lower noise)
437-
- Statistical comparisons are biased
438-
- Visual differences may reflect SNR, not true effects
439-
440-
### Strategy 1: Select keys with similar counts
441-
442-
```matlab
443-
% Count occurrences for each keystroke type
444-
keystroke_counts = struct();
445-
446-
for i = 1:length(EEG.event)
447-
if contains(EEG.event(i).type, 'keystroke_')
448-
key_type = EEG.event(i).type;
449-
if ~isfield(keystroke_counts, key_type)
450-
keystroke_counts.(key_type) = 0;
451-
end
452-
keystroke_counts.(key_type) = keystroke_counts.(key_type) + 1;
453-
end
454-
end
455-
456-
% Display sorted by count
457-
key_names = fieldnames(keystroke_counts);
458-
key_values = cellfun(@(x) keystroke_counts.(x), key_names);
459-
[sorted_values, sort_idx] = sort(key_values, 'descend');
460-
461-
fprintf('Keystroke occurrence counts:\n');
462-
for i = 1:length(key_names)
463-
fprintf(' %s: %d\n', key_names{sort_idx(i)}, sorted_values(i));
464-
end
465-
466-
% Select keys with similar counts for comparison
467-
% Example: Compare 'a' (left hand) vs 'k' (right hand)
468-
% Only if they have similar trial counts (within 20%)
469-
count_a = keystroke_counts.keystroke_a;
470-
count_k = keystroke_counts.keystroke_k;
471-
472-
if abs(count_a - count_k) / mean([count_a, count_k]) < 0.2
473-
fprintf('\nKeys "a" and "k" have similar trial counts - good for comparison\n');
474-
else
475-
fprintf('\nWarning: Keys "a" and "k" have different trial counts\n');
476-
fprintf('Consider subsampling the higher-count condition\n');
477-
end
478-
```
479-
480-
### Strategy 2: Subsample high-count condition
481-
482-
```matlab
483-
% Match trial counts by random subsampling
484-
% Example: Balance key 'e' (high count) with key 'x' (low count)
485-
486-
% Extract epochs
487-
EEG_e = pop_epoch(EEG, {'keystroke_e'}, [epoch_start epoch_end]);
488-
EEG_x = pop_epoch(EEG, {'keystroke_x'}, [epoch_start epoch_end]);
489-
490-
fprintf('Before balancing:\n');
491-
fprintf(' Key "e": %d trials\n', EEG_e.trials);
492-
fprintf(' Key "x": %d trials\n', EEG_x.trials);
493-
494-
% Determine minimum trial count
495-
min_trials = min(EEG_e.trials, EEG_x.trials);
496-
497-
% Randomly select trials to match
498-
if EEG_e.trials > min_trials
499-
% Subsample key 'e'
500-
rng(42); % Set seed for reproducibility
501-
selected_trials = randperm(EEG_e.trials, min_trials);
502-
EEG_e = pop_select(EEG_e, 'trial', selected_trials);
503-
end
504-
505-
if EEG_x.trials > min_trials
506-
% Subsample key 'x'
507-
rng(42);
508-
selected_trials = randperm(EEG_x.trials, min_trials);
509-
EEG_x = pop_select(EEG_x, 'trial', selected_trials);
510-
end
511-
512-
fprintf('After balancing:\n');
513-
fprintf(' Key "e": %d trials\n', EEG_e.trials);
514-
fprintf(' Key "x": %d trials\n', EEG_x.trials);
515-
516-
% Now compute ERPs with equal SNR
517-
ERP_e = mean(EEG_e.data, 3);
518-
ERP_x = mean(EEG_x.data, 3);
519-
```
520-
521-
### Strategy 3: Bootstrap confidence intervals
522-
523-
For unequal trial counts, use bootstrap to estimate uncertainty:
524-
525-
```matlab
526-
% Compute ERP with confidence intervals using bootstrap
527-
n_bootstrap = 1000;
528-
529-
% Example: Key 'a' with many trials
530-
EEG_a = pop_epoch(EEG, {'keystroke_a'}, [epoch_start epoch_end]);
531-
n_trials = EEG_a.trials;
532-
n_timepoints = EEG_a.pnts;
533-
534-
% Select channel and initialize
535-
chan_idx = 1;
536-
bootstrap_erps = zeros(n_bootstrap, n_timepoints);
537-
538-
% Bootstrap resampling
539-
rng(42);
540-
for i = 1:n_bootstrap
541-
% Resample trials with replacement
542-
trial_indices = randi(n_trials, n_trials, 1);
543-
bootstrap_erps(i, :) = mean(squeeze(EEG_a.data(chan_idx, :, trial_indices)), 2);
544-
end
545-
546-
% Compute mean and confidence intervals
547-
erp_mean = mean(bootstrap_erps, 1);
548-
erp_ci_lower = prctile(bootstrap_erps, 2.5, 1); % 95% CI
549-
erp_ci_upper = prctile(bootstrap_erps, 97.5, 1);
550-
551-
% Plot with confidence intervals
552-
time_vec = EEG_a.times / 1000;
553-
figure;
554-
fill([time_vec, fliplr(time_vec)], ...
555-
[erp_ci_lower, fliplr(erp_ci_upper)], ...
556-
'b', 'FaceAlpha', 0.2, 'EdgeColor', 'none');
557-
hold on;
558-
plot(time_vec, erp_mean, 'b', 'LineWidth', 2);
559-
plot([0, 0], ylim, 'r--', 'LineWidth', 1.5);
560-
xlabel('Time (s)');
561-
ylabel('Amplitude (\muV)');
562-
title('ERP with 95% Bootstrap Confidence Intervals');
563-
grid on;
564-
```
565-
566-
### Recommendations
567-
568-
For ERP comparisons:
569-
570-
1. **Same-hand comparisons** (e.g., different fingers): Use keys with similar occurrence
571-
- Example: 'a', 'e', 't' all have high counts for left hand
572-
573-
2. **Left vs right hand**: Balance trial counts by subsampling
574-
- Example: Match 'a' (left) with 'k' (right) by subsampling
575-
576-
3. **Statistical testing**: Always report trial counts
577-
- Use bootstrap or permutation tests for significance
578-
- Account for unequal variance if trial counts differ
579-
580-
4. **Minimum trial count**: Aim for at least 30 trials per condition
581-
- Fewer trials = noisy ERPs
582-
- More trials = better SNR but diminishing returns beyond ~100
583-
584468
## Computing EMG-ERPs
585469

470+
**Note on trial balancing:** When comparing ERPs across conditions with different trial counts (e.g., common keys like 'e' vs rare keys like 'x'), consider randomly subsampling the higher-count condition using `pop_select(EEG, 'trial', randperm(EEG.trials, n))` to match trial counts and ensure equal signal-to-noise ratios.
471+
586472
EMG-ERPs are computed by averaging **envelope data** across trials:
587473

588474
```matlab
@@ -621,52 +507,13 @@ This figure shows EMG-ERPs for all burst-initial keystrokes, separated by hand (
621507

622508
**Trial selection**: Only burst-initial keystrokes (preceded by >500ms pause) are included to avoid epoch overlap from rapid typing. From approximately 4,000 total keystrokes in this recording session, the burst-initial criterion selects ~200-400 epochs per hand—sufficient for reliable EMG-ERPs while ensuring clean baselines. The exact epoch counts are shown in the figure title.
623509

624-
Note the **contralateral activation pattern**: left-hand keys (a, s, d, e, r, ...) show stronger activation in the left wristband (top-left), while right-hand keys (k, l, j, i, o, ...) show stronger activation in the right wristband (bottom-right). When comparing conditions with different trial counts, use random subsampling as described in the [Balancing trial counts](#balancing-trial-counts-for-fair-comparison) section above.
510+
Note the **contralateral activation pattern**: left-hand keys (a, s, d, e, r, ...) show stronger activation in the left wristband (top-left), while right-hand keys (k, l, j, i, o, ...) show stronger activation in the right wristband (bottom-right).
625511

626512
**Important for EMG:**
627513
- EEGLAB's topoplot (scalp maps) is NOT meaningful for EMG data
628514
- Focus on channel ERPs and time-course plots
629515
- Compare ERPs across channels on the same limb
630516

631-
## Comparing conditions with EEGLAB
632-
633-
### Comparing multiple conditions
634-
635-
To compare ERPs across different conditions (e.g., different keys or hands), use EEGLAB's comparison tools:
636-
637-
**Method 1: Overlay plots for selected channels**
638-
639-
```matlab
640-
% Compare key 'a' (left hand) vs key 'k' (right hand) for specific channels
641-
% Use EEGLAB menu: Plot > Channel ERPs > With scalp maps
642-
% Then select specific channels and conditions
643-
644-
% Or from command line:
645-
% First, select left-hand channels for key 'a'
646-
figure; pop_plotdata(EEG_a, 1, [1 5 9], 'Key "a" - Left hand channels');
647-
648-
% Then, select right-hand channels for key 'k'
649-
figure; pop_plotdata(EEG_k, 1, [17 21 25], 'Key "k" - Right hand channels');
650-
```
651-
652-
**Method 2: Compare using EEGLAB's STUDY framework**
653-
654-
For systematic comparison across multiple subjects or conditions:
655-
656-
```matlab
657-
% Create a STUDY structure with multiple datasets
658-
% Use EEGLAB menu: File > Create study > Browse for datasets
659-
% Then use: Study > Precompute channel measures
660-
% Finally: Study > Plot channel measures
661-
662-
% This allows statistical comparison across conditions
663-
```
664-
665-
**Expected patterns:**
666-
- **Contralateral dominance**: Stronger activation in the hand performing the keystroke
667-
- **Timing differences**: Peak latency may differ between hands
668-
- **Amplitude differences**: May vary based on finger position and force
669-
670517
## Key differences: EMG vs EEG
671518

672519
| Aspect | EEG | EMG |

0 commit comments

Comments
 (0)