@yibeichan/claude-skills 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +98 -0
  3. package/cli.js +272 -0
  4. package/install.py +240 -0
  5. package/package.json +44 -0
  6. package/skills/bidsapp-nidm-standards/SKILL.md +202 -0
  7. package/skills/bidsapp-nidm-standards/references/babs_config.md +20 -0
  8. package/skills/bidsapp-nidm-standards/references/cli_arguments.md +76 -0
  9. package/skills/bidsapp-nidm-standards/references/container_patterns.md +53 -0
  10. package/skills/bidsapp-nidm-standards/references/nidm_integration.md +403 -0
  11. package/skills/bidsapp-nidm-standards/references/repo_structure.md +121 -0
  12. package/skills/bidsapp-nidm-standards/references/testing_patterns.md +82 -0
  13. package/skills/dicom2fmriprep/SKILL.md +377 -0
  14. package/skills/dicom2fmriprep/evals/evals.json +26 -0
  15. package/skills/dicom2fmriprep/references/babs-details.md +407 -0
  16. package/skills/dicom2fmriprep/references/fmriprep-details.md +250 -0
  17. package/skills/dicom2fmriprep/references/heudiconv-details.md +243 -0
  18. package/skills/fmri-ssm/SKILL.md +317 -0
  19. package/skills/fmri-ssm/references/code_templates.md +1570 -0
  20. package/skills/fmri-ssm/references/downstream_analysis.md +680 -0
  21. package/skills/fmri-ssm/references/group_inference.md +608 -0
  22. package/skills/fmri-ssm/references/hrf_modeling.md +447 -0
  23. package/skills/fmri-ssm/references/model_catalog.md +436 -0
  24. package/skills/fmri-ssm/references/paradigm_guide.md +406 -0
  25. package/skills/fmri-ssm/references/preprocessing.md +614 -0
  26. package/skills/fmri-ssm.zip +0 -0
  27. package/skills/neuroimaging-qc/SKILL.md +203 -0
  28. package/skills/neuroimaging-qc/references/eeg_qc.md +400 -0
  29. package/skills/neuroimaging-qc/references/fmri_qc.md +343 -0
  30. package/skills/neuroimaging-qc/references/fnirs_qc.md +430 -0
  31. package/skills/neuroimaging-qc/references/structural_qc.md +454 -0
  32. package/skills/neuroimaging-qc/scripts/parse_fmriprep_confounds.py +153 -0
  33. package/skills/neuroimaging-qc/scripts/parse_mriqc.py +114 -0
  34. package/skills/neuroimaging-qc/scripts/qc_report.py +295 -0
  35. package/skills/scientific-writer/SKILL.md +202 -0
  36. package/skills/scientific-writer/references/citation_styles.md +163 -0
  37. package/skills/scientific-writer/references/field_conventions.md +245 -0
  38. package/skills/scientific-writer/references/figures_tables.md +225 -0
  39. package/skills/scientific-writer/references/reporting_guidelines.md +225 -0
  40. package/skills.json +54 -0
@@ -0,0 +1,203 @@
1
+ ---
2
+ name: neuroimaging-qc
3
+ description: Evidence-based QC decision-making for neuroimaging data. Interpret QC metrics from any pipeline (fMRIPrep, MRIQC, FreeSurfer, MNE-Python, Homer3, custom outputs) to make justified inclusion/exclusion decisions. Covers fMRI, EEG, fNIRS, and structural MRI across populations (adults, infants, adolescents, clinical) and paradigms (resting-state, task, naturalistic, sleep). Use when filtering subjects based on QC outputs, setting exclusion thresholds, justifying QC criteria for methods sections, or parsing QC files programmatically with Python.
4
+ ---
5
+
6
+ # Neuroimaging QC Decision-Making
7
+
8
+ Evidence-based guidance for interpreting QC metrics and making principled inclusion/exclusion decisions.
9
+
10
+ ## Core Principles
11
+
12
+ ### 1. No Universal Thresholds
13
+ QC thresholds are study-specific. Factors affecting appropriate cutoffs:
14
+ - **Population**: Infants tolerate higher motion than adults
15
+ - **Paradigm**: Task fMRI has different constraints than resting-state
16
+ - **Analysis**: Connectivity analyses are more motion-sensitive than activation
17
+ - **Sample size**: Stricter thresholds with larger N; lenient with small N
18
+
19
+ ### 2. Distribution-Based Decisions
20
+ Always examine your sample's QC distribution before applying thresholds:
21
+ 1. Plot histograms of key metrics
22
+ 2. Identify natural breakpoints/outliers (>2-3 SD from mean)
23
+ 3. Apply literature-based thresholds as starting points, adjust based on distribution
24
+ 4. Report both threshold AND resulting exclusion rate
25
+
26
+ ### 3. Multi-Metric Assessment
27
+ Never exclude based on single metric. Combine:
28
+ - Motion metrics (FD, DVARS)
29
+ - Signal quality metrics (tSNR, SNR)
30
+ - Artifact indicators (outlier volumes, registration quality)
31
+ - Visual inspection for edge cases
32
+
33
+ ## Decision Workflow
34
+
35
+ ```
36
+ 1. IDENTIFY your QC source
37
+ ├── Known pipeline (fMRIPrep, MRIQC, etc.) → See modality references
38
+ └── Custom/unknown output → Parse available metrics, map to known categories
39
+
40
+ 2. CHARACTERIZE your study
41
+ ├── Population: adult / pediatric / infant / clinical
42
+ ├── Paradigm: rest / task / naturalistic / sleep
43
+ └── Analysis: activation / connectivity / other
44
+
45
+ 3. ESTABLISH thresholds
46
+ ├── Start with literature recommendations (see references)
47
+ ├── Examine your sample distribution
48
+ └── Adjust based on trade-off: data quality vs. statistical power
49
+
50
+ 4. APPLY and DOCUMENT
51
+ ├── Generate exclusion summary
52
+ ├── Report thresholds with citations
53
+ └── Conduct sensitivity analysis with stricter/lenient thresholds
54
+ ```
55
+
56
+ ## Quick Reference: Common Thresholds
57
+
58
+ ### fMRI Motion (FD)
59
+
60
+ | Population | Conservative | Standard | Lenient | Citation |
61
+ |------------|-------------|----------|---------|----------|
62
+ | Adults (rest) | 0.2 mm | 0.3 mm | 0.5 mm | Power et al., 2012, 2014 |
63
+ | Adults (task) | 0.5 mm | 0.9 mm | 1.0 mm | Siegel et al., 2014 |
64
+ | Children (6-12y) | 0.3 mm | 0.4 mm | 0.5 mm | Fair et al., 2012 |
65
+ | Infants | 0.3 mm | 0.5 mm | — | Population-dependent |
66
+ | Neonates | 0.2 mm | 0.5 mm | — | Smyser et al., 2010 |
67
+
68
+ **Additional motion criteria:**
69
+ - fd_perc (% volumes > threshold): typically exclude if >20-50%
70
+ - Maximum FD spike: consider >3-5 mm as problematic
71
+ - Minimum usable data: ≥5 min for resting-state, task-dependent for task fMRI
72
+
73
+ ### EEG Amplitude (Peak-to-Peak)
74
+
75
+ | Channel Type | Reject Threshold | Flat Threshold | Notes |
76
+ |--------------|-----------------|----------------|-------|
77
+ | EEG | 100-200 µV | 1 µV | Hardware-dependent |
78
+ | EOG | 200-250 µV | — | Blink detection |
79
+ | MEG (mag) | 3000-4000 fT | 1 fT | Magnetometers |
80
+ | MEG (grad) | 3000-4000 fT/cm | 1 fT/cm | Gradiometers |
81
+
82
+ **Additional EEG criteria:**
83
+ - Channel rejection: >20-30% bad epochs → mark as bad channel
84
+ - Epoch rejection: typically accept 10-30% epoch loss; >50% problematic
85
+ - Interpolation limit: ≤10% of channels can be interpolated
86
+
87
+ ### Structural MRI
88
+
89
+ | Metric | Direction | Concern Level | Notes |
90
+ |--------|-----------|---------------|-------|
91
+ | CNR (GM/WM) | Higher better | <2.5 | Tissue contrast |
92
+ | SNR | Higher better | Site-dependent | Compare within-site |
93
+ | QI1 | Lower better | >0.1 | Artifact detection |
94
+ | EFC | Lower better | Outlier in distribution | Ghosting indicator |
95
+
96
+ ## Modality-Specific References
97
+
98
+ For detailed metrics, thresholds, and Python code:
99
+
100
+ - **fMRI (fMRIPrep/MRIQC)**: See [references/fmri_qc.md](references/fmri_qc.md)
101
+ - **EEG/MEG (MNE-Python)**: See [references/eeg_qc.md](references/eeg_qc.md)
102
+ - **fNIRS (Homer3/MNE-NIRS)**: See [references/fnirs_qc.md](references/fnirs_qc.md)
103
+ - **Structural MRI**: See [references/structural_qc.md](references/structural_qc.md)
104
+
105
+ ## Python Utilities
106
+
107
+ Scripts for parsing QC outputs and applying thresholds:
108
+
109
+ - `scripts/parse_mriqc.py`: Parse MRIQC group TSV, flag subjects
110
+ - `scripts/parse_fmriprep_confounds.py`: Summarize fMRIPrep confounds
111
+ - `scripts/qc_report.py`: Generate QC summary reports
112
+
113
+ ## Methods Section Templates
114
+
115
+ ### fMRI QC Methods
116
+ ```
117
+ Quality control was performed using [MRIQC/fMRIPrep] outputs. Subjects were
118
+ excluded based on the following criteria: (1) mean framewise displacement
119
+ (FD) > X mm [cite Power et al., 2012], (2) >Y% of volumes exceeding FD
120
+ threshold of Z mm, or (3) visual inspection revealing [registration
121
+ failures/artifacts]. This resulted in N subjects excluded (X% of sample),
122
+ yielding a final sample of M participants.
123
+ ```
124
+
125
+ ### EEG QC Methods
126
+ ```
127
+ Continuous EEG data underwent artifact rejection using MNE-Python. Epochs
128
+ containing peak-to-peak amplitudes exceeding X µV were rejected. Channels
129
+ with >Y% rejected epochs were marked as bad and interpolated using spherical
130
+ spline interpolation. Participants with >Z% rejected epochs or >N bad
131
+ channels were excluded from analysis.
132
+ ```
133
+
134
+ ## Handling Unknown QC Outputs
135
+
136
+ When encountering unfamiliar QC metrics:
137
+
138
+ 1. **Identify metric category**:
139
+ - Motion/movement: Look for displacement, rotation, translation terms
140
+ - Signal quality: SNR, tSNR, CNR, variance-related
141
+ - Artifacts: Outlier counts, spike detection, artifact indices
142
+
143
+ 2. **Determine directionality**:
144
+ - Higher-is-better: SNR, tSNR, CNR
145
+ - Lower-is-better: FD, DVARS, artifact indices, outlier counts
146
+
147
+ 3. **Establish thresholds**:
148
+ - Plot distribution, identify outliers
149
+ - If metric has known analog, use those thresholds
150
+ - Otherwise: use ±2-3 SD from mean as starting point
151
+
152
+ 4. **Validate**:
153
+ - Cross-reference with visual inspection
154
+ - Check correlation with known metrics
155
+ - Verify excluded subjects are actually problematic
156
+
157
+ ## Population-Specific Considerations
158
+
159
+ ### Infants (0-24 months)
160
+ - Higher baseline motion expected; adjust FD thresholds upward
161
+ - Shorter usable data segments acceptable
162
+ - Age-appropriate templates critical for registration QC
163
+ - Sleep state affects data quality (deep sleep preferred)
164
+
165
+ ### Pediatric (3-12 years)
166
+ - Motion decreases with age; consider age as covariate
167
+ - Task compliance affects data quality
168
+ - Mock scanner training reduces motion
169
+ - Consider breaks during long protocols
170
+
171
+ ### Adolescents
172
+ - Motion intermediate between children and adults
173
+ - Developmental stage affects hemodynamics
174
+ - Consider puberty stage as potential confound
175
+
176
+ ### Clinical Populations
177
+ - Disease-specific considerations (lesions, atrophy)
178
+ - Medication effects on signal
179
+ - May need population-specific templates
180
+ - Balance data quality vs. already-reduced sample sizes
181
+
182
+ ## Paradigm-Specific Considerations
183
+
184
+ ### Resting-State
185
+ - Scrubbing viable (can remove timepoints)
186
+ - Need minimum continuous/total duration (≥5 min recommended)
187
+ - Strict motion thresholds (FD < 0.2-0.3 mm)
188
+
189
+ ### Task fMRI
190
+ - Cannot arbitrarily remove timepoints
191
+ - Consider motion relative to task timing
192
+ - More lenient thresholds acceptable (FD < 0.5-0.9 mm)
193
+ - Ensure sufficient trials survive exclusion
194
+
195
+ ### Naturalistic (movies, stories)
196
+ - Long durations increase motion likelihood
197
+ - Consider segment-wise QC
198
+ - Drift artifacts more relevant
199
+
200
+ ### Sleep Studies
201
+ - State-dependent QC (arousal events)
202
+ - EEG quality for sleep staging
203
+ - Movement during state transitions
@@ -0,0 +1,400 @@
1
+ # EEG/MEG QC Reference
2
+
3
+ Comprehensive guide for QC of EEG and MEG data, primarily using MNE-Python.
4
+
5
+ ## Table of Contents
6
+ 1. [Channel-Level QC](#channel-level-qc)
7
+ 2. [Epoch-Level QC](#epoch-level-qc)
8
+ 3. [Artifact Detection](#artifact-detection)
9
+ 4. [Subject-Level Exclusion](#subject-level-exclusion)
10
+ 5. [Population Considerations](#population-considerations)
11
+ 6. [Python Examples](#python-examples)
12
+
13
+ ## Channel-Level QC
14
+
15
+ ### Bad Channel Detection
16
+
17
+ **Indicators of bad channels:**
18
+ - Flat/dead channels (no signal variance)
19
+ - Noisy channels (excessive high-frequency activity)
20
+ - Channels with persistent artifacts
21
+ - Disconnected/bridged electrodes
22
+
23
+ **Detection strategies:**
24
+
25
+ 1. **Amplitude-based**: Channels with abnormal variance
26
+ 2. **Correlation-based**: Channels poorly correlated with neighbors
27
+ 3. **Spectral-based**: Abnormal power spectrum shape
28
+ 4. **Visual inspection**: Always recommended as final check
29
+
30
+ **MNE-Python thresholds (peak-to-peak):**
31
+
32
+ | Channel Type | Reject (Max PTP) | Flat (Min PTP) | Notes |
33
+ |--------------|-----------------|----------------|-------|
34
+ | EEG | 100-200 µV | 1 µV | Hardware-dependent |
35
+ | EOG | 200-250 µV | — | Blink detection |
36
+ | ECG | 5 mV | — | Heartbeat detection |
37
+ | MEG (magnetometer) | 3000-4000 fT | 1 fT | |
38
+ | MEG (gradiometer) | 3000-4000 fT/cm | 1 fT/cm | |
39
+
40
+ ### Interpolation Guidelines
41
+
42
+ - **Maximum interpolatable channels**: 10% of total (e.g., ≤6 for 64-channel system)
43
+ - **Avoid interpolating**: Reference electrode neighbors, critical ROI channels
44
+ - **Method**: Spherical spline interpolation (MNE default)
45
+ - **Document**: Always report number and location of interpolated channels
46
+
47
+ ## Epoch-Level QC
48
+
49
+ ### Rejection Thresholds
50
+
51
+ **Standard EEG thresholds:**
52
+ ```python
53
+ reject = dict(
54
+ eeg=100e-6, # 100 µV - conservative
55
+ # eeg=150e-6, # 150 µV - standard
56
+ # eeg=200e-6, # 200 µV - lenient
57
+ eog=200e-6 # 200 µV for EOG
58
+ )
59
+
60
+ flat = dict(
61
+ eeg=1e-6 # 1 µV minimum
62
+ )
63
+ ```
64
+
65
+ **MEG thresholds:**
66
+ ```python
67
+ reject = dict(
68
+ mag=4000e-15, # 4000 fT
69
+ grad=4000e-13, # 4000 fT/cm
70
+ eog=200e-6
71
+ )
72
+
73
+ flat = dict(
74
+ mag=1e-15, # 1 fT
75
+ grad=1e-13 # 1 fT/cm
76
+ )
77
+ ```
78
+
79
+ ### Acceptable Epoch Loss
80
+
81
+ | Context | Acceptable Loss | Concern Level |
82
+ |---------|-----------------|---------------|
83
+ | High-trial designs (>200 per condition) | <30% | >40% |
84
+ | Standard designs (50-100 per condition) | <20% | >30% |
85
+ | Low-trial designs (<50 per condition) | <10% | >20% |
86
+
87
+ **Minimum trials for reliable ERPs:**
88
+ - Simple components (P1, N1): ~30-40 trials
89
+ - Later components (P300, N400): ~20-30 trials
90
+ - Complex conditions: varies by effect size
91
+
92
+ ## Artifact Detection
93
+
94
+ ### EOG (Eye Movement) Artifacts
95
+
96
+ **Types:**
97
+ - Blinks: Large amplitude, frontal distribution
98
+ - Saccades: Horizontal eye movements
99
+
100
+ **Detection (MNE):**
101
+ ```python
102
+ # Find EOG events
103
+ eog_events = mne.preprocessing.find_eog_events(raw)
104
+
105
+ # Create EOG epochs for inspection/ICA
106
+ eog_epochs = mne.preprocessing.create_eog_epochs(raw)
107
+ ```
108
+
109
+ **Handling strategies:**
110
+ 1. **Rejection**: Remove contaminated epochs (simple, loses data)
111
+ 2. **ICA**: Remove EOG components (preserves more data)
112
+ 3. **Regression**: Regress out EOG signal (SSP projections)
113
+
114
+ ### ECG (Cardiac) Artifacts
115
+
116
+ **More prominent in MEG than EEG.**
117
+
118
+ ```python
119
+ ecg_events = mne.preprocessing.find_ecg_events(raw)
120
+ ecg_epochs = mne.preprocessing.create_ecg_epochs(raw)
121
+ ```
122
+
123
+ ### Muscle Artifacts
124
+
125
+ **Characteristics:**
126
+ - High frequency (>20 Hz)
127
+ - Often temporal/neck electrodes
128
+ - Common during jaw clenching, swallowing
129
+
130
+ **Detection:**
131
+ - High-frequency power increase
132
+ - ICA component inspection
133
+
134
+ ### Line Noise (50/60 Hz)
135
+
136
+ **Handling:**
137
+ ```python
138
+ # Notch filter
139
+ raw_filtered = raw.notch_filter(freqs=[60, 120, 180]) # 60 Hz and harmonics
140
+
141
+ # Or use spectral interpolation for narrow-band removal
142
+ ```
143
+
144
+ ## Subject-Level Exclusion
145
+
146
+ ### Exclusion Criteria
147
+
148
+ **Quantitative:**
149
+ - >50% epochs rejected
150
+ - >10% channels interpolated
151
+ - Insufficient trials per condition (<minimum for analysis)
152
+ - Technical failures (disconnected electrodes, recording errors)
153
+
154
+ **Qualitative (requires visual inspection):**
155
+ - Persistent artifacts not removable by ICA
156
+ - Unusual waveform morphology suggesting hardware issues
157
+ - Excessive drowsiness/sleep (for wake studies)
158
+
159
+ ### QC Checklist
160
+
161
+ ```
162
+ □ Raw data visual inspection (scrolling)
163
+ □ Power spectrum check (line noise, muscle)
164
+ □ Bad channel identification and interpolation
165
+ □ Artifact detection (EOG, ECG)
166
+ □ ICA component inspection (if used)
167
+ □ Epoch rejection summary
168
+ □ Final epoch count per condition
169
+ □ ERP/ERF waveform check
170
+ ```
171
+
172
+ ## Population Considerations
173
+
174
+ ### Infants
175
+
176
+ **Unique challenges:**
177
+ - More movement artifacts
178
+ - Different reference electrode considerations
179
+ - Shorter attention spans → fewer trials
180
+ - Different frequency characteristics
181
+
182
+ **Adjusted thresholds:**
183
+ ```python
184
+ # More lenient for infant EEG
185
+ reject_infant = dict(
186
+ eeg=200e-6, # 200 µV (vs 100-150 for adults)
187
+ )
188
+ ```
189
+
190
+ **Minimum trials (infant ERPs):**
191
+ - May need only 10-20 good trials for robust components
192
+ - Consider trial-by-trial analysis approaches
193
+
194
+ ### Clinical Populations
195
+
196
+ **Considerations:**
197
+ - Pathological activity may look like artifacts
198
+ - Medication effects on EEG
199
+ - May have reduced compliance / more movement
200
+ - Adjust thresholds based on population characteristics
201
+
202
+ ### High-Density EEG (64+ channels)
203
+
204
+ - Can be more aggressive with channel interpolation
205
+ - ICA more effective with more channels
206
+ - Consider spatial filtering approaches
207
+
208
+ ## Python Examples
209
+
210
+ ### Complete QC Pipeline
211
+
212
+ ```python
213
+ import mne
214
+ import numpy as np
215
+
216
+ def run_eeg_qc(raw_path, events_path=None, event_id=None):
217
+ """
218
+ Complete EEG QC pipeline.
219
+
220
+ Returns dict with QC metrics and processed data.
221
+ """
222
+ # Load data
223
+ raw = mne.io.read_raw_fif(raw_path, preload=True)
224
+
225
+ # 1. Filter
226
+ raw.filter(l_freq=0.1, h_freq=40)
227
+ raw.notch_filter(freqs=[60])
228
+
229
+ # 2. Find bad channels by variance
230
+ data = raw.get_data(picks='eeg')
231
+ variance = np.var(data, axis=1)
232
+ z_var = (variance - np.mean(variance)) / np.std(variance)
233
+ bad_by_var = np.where(np.abs(z_var) > 3)[0]
234
+
235
+ # 3. Mark bad channels
236
+ eeg_names = raw.ch_names
237
+ bad_channels = [eeg_names[i] for i in bad_by_var if eeg_names[i].startswith('EEG')]
238
+ raw.info['bads'].extend(bad_channels)
239
+
240
+ # 4. Interpolate bad channels
241
+ if len(raw.info['bads']) > 0:
242
+ raw.interpolate_bads(reset_bads=True)
243
+
244
+ # 5. Re-reference (average reference)
245
+ raw.set_eeg_reference('average')
246
+
247
+ # 6. Create epochs (if events provided)
248
+ qc_results = {
249
+ 'n_bad_channels': len(bad_channels),
250
+ 'bad_channels': bad_channels,
251
+ 'n_total_channels': len([ch for ch in raw.ch_names if ch.startswith('EEG')]),
252
+ }
253
+
254
+ if events_path and event_id:
255
+ events = mne.read_events(events_path)
256
+
257
+ reject = dict(eeg=150e-6)
258
+ flat = dict(eeg=1e-6)
259
+
260
+ epochs = mne.Epochs(
261
+ raw, events, event_id,
262
+ tmin=-0.2, tmax=0.8,
263
+ reject=reject, flat=flat,
264
+ preload=True
265
+ )
266
+
267
+ # Calculate epoch stats
268
+ n_total = len(events)
269
+ n_dropped = n_total - len(epochs)
270
+
271
+ qc_results.update({
272
+ 'n_epochs_total': n_total,
273
+ 'n_epochs_dropped': n_dropped,
274
+ 'epoch_rejection_rate': 100 * n_dropped / n_total,
275
+ 'epochs_per_condition': {k: len(epochs[k]) for k in event_id.keys()},
276
+ })
277
+
278
+ return qc_results, raw, epochs
279
+
280
+ return qc_results, raw, None
281
+
282
+
283
+ def check_subject_inclusion(qc_results,
284
+ max_bad_channels_pct=10,
285
+ max_epoch_rejection_pct=30,
286
+ min_epochs_per_cond=30):
287
+ """
288
+ Check if subject meets inclusion criteria.
289
+ """
290
+ include = True
291
+ reasons = []
292
+
293
+ # Check bad channels
294
+ bad_pct = 100 * qc_results['n_bad_channels'] / qc_results['n_total_channels']
295
+ if bad_pct > max_bad_channels_pct:
296
+ include = False
297
+ reasons.append(f"Bad channels: {bad_pct:.1f}% > {max_bad_channels_pct}%")
298
+
299
+ # Check epoch rejection
300
+ if 'epoch_rejection_rate' in qc_results:
301
+ if qc_results['epoch_rejection_rate'] > max_epoch_rejection_pct:
302
+ include = False
303
+ reasons.append(f"Epoch rejection: {qc_results['epoch_rejection_rate']:.1f}% > {max_epoch_rejection_pct}%")
304
+
305
+ # Check epochs per condition
306
+ for cond, n in qc_results['epochs_per_condition'].items():
307
+ if n < min_epochs_per_cond:
308
+ include = False
309
+ reasons.append(f"Insufficient epochs for {cond}: {n} < {min_epochs_per_cond}")
310
+
311
+ return include, reasons
312
+ ```
313
+
314
+ ### ICA-Based Artifact Removal
315
+
316
+ ```python
317
+ def run_ica_artifact_removal(raw, n_components=15):
318
+ """
319
+ ICA-based artifact removal for EOG and ECG.
320
+ """
321
+ from mne.preprocessing import ICA
322
+
323
+ # Fit ICA
324
+ ica = ICA(n_components=n_components, random_state=42)
325
+ ica.fit(raw)
326
+
327
+ # Find EOG-related components
328
+ eog_indices, eog_scores = ica.find_bads_eog(raw)
329
+
330
+ # Find ECG-related components (if ECG channel available)
331
+ try:
332
+ ecg_indices, ecg_scores = ica.find_bads_ecg(raw)
333
+ except:
334
+ ecg_indices = []
335
+
336
+ # Mark components for exclusion
337
+ ica.exclude = list(set(eog_indices + ecg_indices))
338
+
339
+ # Apply ICA
340
+ raw_clean = ica.apply(raw.copy())
341
+
342
+ return raw_clean, ica, {
343
+ 'n_eog_components': len(eog_indices),
344
+ 'n_ecg_components': len(ecg_indices),
345
+ 'eog_indices': eog_indices,
346
+ 'ecg_indices': ecg_indices,
347
+ }
348
+ ```
349
+
350
+ ### Generate QC Report
351
+
352
+ ```python
353
+ def generate_eeg_qc_report(qc_results, output_path):
354
+ """Generate text QC report."""
355
+
356
+ report = f"""
357
+ EEG Quality Control Report
358
+ ==========================
359
+
360
+ Channel Quality
361
+ ---------------
362
+ Total EEG channels: {qc_results['n_total_channels']}
363
+ Bad channels identified: {qc_results['n_bad_channels']}
364
+ Bad channel rate: {100 * qc_results['n_bad_channels'] / qc_results['n_total_channels']:.1f}%
365
+ Bad channel names: {', '.join(qc_results['bad_channels']) if qc_results['bad_channels'] else 'None'}
366
+ """
367
+
368
+ if 'n_epochs_total' in qc_results:
369
+ report += f"""
370
+ Epoch Quality
371
+ -------------
372
+ Total epochs: {qc_results['n_epochs_total']}
373
+ Rejected epochs: {qc_results['n_epochs_dropped']}
374
+ Rejection rate: {qc_results['epoch_rejection_rate']:.1f}%
375
+
376
+ Epochs per condition:
377
+ """
378
+ for cond, n in qc_results['epochs_per_condition'].items():
379
+ report += f" {cond}: {n}\n"
380
+
381
+ if 'n_eog_components' in qc_results:
382
+ report += f"""
383
+ ICA Artifact Removal
384
+ --------------------
385
+ EOG components removed: {qc_results['n_eog_components']}
386
+ ECG components removed: {qc_results['n_ecg_components']}
387
+ """
388
+
389
+ with open(output_path, 'w') as f:
390
+ f.write(report)
391
+
392
+ return report
393
+ ```
394
+
395
+ ## Key References
396
+
397
+ - Jas M et al. (2017). Autoreject: Automated artifact rejection for MEG and EEG data. NeuroImage 159:417-429.
398
+ - Gramfort A et al. (2013). MEG and EEG data analysis with MNE-Python. Frontiers in Neuroscience 7:267.
399
+ - Delorme A & Makeig S. (2004). EEGLAB: an open source toolbox for analysis of single-trial EEG dynamics. Journal of Neuroscience Methods 134(1):9-21.
400
+ - Luck SJ. (2014). An Introduction to the Event-Related Potential Technique. MIT Press.