EEG · ERP · Time-frequency · HRV — MNE-Python pipeline

EEG & Cardiac Analysis Methodology

ผลของการฝึกตารางเก้าช่องต่อความสนใจ (attention) และการตอบสนองอัตโนมัติ (automatic response) ระหว่างการทำงานทางปัญญาของสมองEffects of nine-square (ตารางเก้าช่อง) training on attention and automatic response during brain cognitive performance. วิธีวิเคราะห์ทุกส่วนอิงงานตีพิมพ์จริงและ pipeline มาตรฐาน — ไม่แต่งตัวเลข/DOI

Doctoral thesis · methods chapter/ Analysis & write-up: PhD candidate/ Toolchain: MNE-Python · mne-icalabel · asrpy · specparam · NeuroKit2 · statsmodels

Conditions4 Baseline · Flanker · Go/NoGo · 9-square movement
Timepoints2 Pre & Post training — within-subject
Cells8 4 conditions × pre/post, repeated measures
Neural measures
ERP N2P3ERNFM-θ1/fITC
EEG spectral · ERP · time-frequency
Autonomic
RMSSDSDNNHF/LFHR
HRV per condition (NeuroKit2)

This chapter specifies, in reproducible detail, how the recorded signals — scalp EEG and continuous heart rate — are transformed into the two dependent constructs the study is built to measure: attention (the allocation of processing resources) and automaticity (the same task performed with less controlled effort). Each analysis condition is recorded pre and post nine-square training across four tasks — Baseline, Flanker, Go/NoGo, and 9-square movement — and every method below maps onto a specific, pre-registrable prediction. All code is a working MNE-Python skeleton against published, standard pipelines; nothing is invented.

00Mapping data to theoretical structure

แผนที่ข้อมูล → โครงสร้างเชิงทฤษฎี — ส่วนที่สำคัญที่สุด อ่านก่อน. The dependent variable is not one quantity but two dissociable ones. Attention and automatic response are distinct mechanisms, measured by different signals in different conditions, and they can move in opposite directions after training. Fixing this mapping first is what keeps every later statistical test interpretable.

ConstructMeasured inPrimary indicesPredicted direction (post 9-square)
Attention
(allocation / effort)
Baseline · Flanker · Go/NoGoP3/P300 amplitude (Pz/Cz), frontal-midline theta (Fz), parietal alpha desynchronisationP3↑ or theta↑ as resources are allocated more effectively (framework-dependent)
Automaticity
(automatic processing)
pre→post of conflict trials (incongruent, NoGo)N2↓ · conflict-theta↓ · P3 latency↓ together with RT↓ + RT-variability↓ + accuracy stable/bettersmaller frontal control signal, yet performance is as fast and accurate — “more automatic”
Operational definition — the heart of the thesis

หัวใจของ thesis: automaticity is declared only when several conditions hold simultaneously, never from a single index. After training, on the same conflict task, N2 and/or frontal-midline theta decrease and P3 latency shortens, while RT gets faster, RT variability falls, and accuracy / d′ do not drop. That joint pattern — “อัตโนมัติขึ้น” — is what separates genuine automatisation from merely getting better (accuracy↑ with effort unchanged).

The supporting theory is explicit: automaticity is a reduction in controlled processing, which lowers the conflict-monitoring signal generated near anterior cingulate cortex (N2 / frontal-midline theta) and lowers resource cost (P3). This follows Shiffrin & Schneider (controlled vs. automatic processing), Botvinick's conflict-monitoring account, and Cavanagh & Frank's demonstration that frontal-midline theta is a mechanism of cognitive control.

Figure 1Condition × time design matrix
Pre-trainingPost-training
Baseline
eyes-open rest
spectral θ/α1/fHRV rest
spectral θ/α1/fHRV rest
Flanker
cong / incong
N2P3FM-θHRV
N2P3FM-θHRV
Go/NoGo
go / nogo
N2P3ERNHRV
N2P3ERNHRV
9-square
movement
θ/α power*mean HR
*ASR-cleaned · exploratory
θ/α power*mean HR
*intensity covariate
EEG / ERP measureautonomic (HRV / HR)shaded row = movement (secondary)
Fig 1. The full 4×2 within-subject design. The three seated cells (Baseline, Flanker, Go/NoGo) carry the confirmatory pre→post contrasts; the movement row is deliberately treated as secondary/exploratory because in-motion EEG is the hardest signal to trust (see §1.6).
Figure 2The nine-square (ตารางเก้าช่อง) footwork grid
Fig 2. Nine-square training is coordinative, rhythmic footwork stepping out from and back to a home cell on cue. Numbers indicate one example step order; the intervention trains rapid, sequenced motor selection — the behaviour the thesis expects to become automatic and to transfer to cognitive conflict tasks.

01Preprocessing — one standard pipeline for every condition

“Makoto-style” pipeline. Order matters, and the same recipe is applied identically to all conditions so that any pre→post difference cannot be an artefact of differential cleaning. The canonical sequence is:

filter → record bad channels → ICA (high-pass 1 Hz) → ICLabel reject → interpolate → re-reference → epoch → reject / ASR

Figure 3Analysis pipeline — from raw recording to statistics
Fig 3. A single cleaning trunk feeds four analysis branches (spectral, ERP, time-frequency, cardiac), which converge on one mixed-model / permutation statistical stage. ICA is always fitted on a 1 Hz high-passed copy and the solution is applied to the 0.1 Hz ERP signal.
pythonListing 1 · preprocessing
import mne
from mne.preprocessing import ICA
import numpy as np

# 1.1 โหลด (ปรับ reader ตามเครื่อง: BrainVision .vhdr / EDF / EEGLAB .set)
raw = mne.io.read_raw_brainvision("subj01_pre_flanker.vhdr", preload=True)
raw.set_montage("standard_1020", on_missing="warn")

# 1.2 Filter — ไฟบ้านไทย 50 Hz (notch), band 0.1–40 Hz สำหรับ ERP
raw.notch_filter(50.0)
raw_erp = raw.copy().filter(0.1, 40.0, fir_design="firwin")

# 1.3 ICA ต้อง high-pass 1 Hz (ICA เกลียด drift) แล้ว "ยก" solution ไปใช้กับสัญญาณ 0.1 Hz
raw_ica = raw.copy().filter(1.0, 40.0)
ica = ICA(n_components=0.99, method="picard", max_iter="auto", random_state=97)
ica.fit(raw_ica)

# 1.4 จัด component อัตโนมัติด้วย ICLabel (แทนการเดา) — ตัด eye/muscle/heart/line
from mne_icalabel import label_components
labels = label_components(raw_ica, ica, method="iclabel")
bad_ic = [i for i, (lab, p) in enumerate(zip(labels["labels"], labels["y_pred_proba"]))
          if lab in ("eye blink", "muscle artifact", "heart beat", "line noise", "channel noise") and p > 0.8]
ica.exclude = bad_ic
ica.apply(raw_erp)          # ใช้กับสัญญาณ ERP (0.1 Hz)

# 1.5 bad channels → interpolate → average reference (report ทั้ง 2 อย่างในเล่ม)
# raw_erp.info["bads"] = [...]      # จากการตรวจ/มาตรฐาน (เช่น PREP pipeline)
raw_erp.interpolate_bads(reset_bads=True)
raw_erp.set_eeg_reference("average", projection=False)   # หรือ linked-mastoid TP9/TP10
Listing 1. Band-pass 0.1–40 Hz with a 50 Hz notch (Thai mains); ICA is fitted on a 1 Hz high-passed copy because ICA is intolerant of slow drift, then the unmixing is applied to the 0.1 Hz ERP data. mne-icalabel classifies components automatically (p > 0.8) rather than by eye.
Figure 410–20 montage — analysis electrodes
primary analysis site (FCz · Cz · Pz)recorded electrode
Fig 4. The international 10–20 layout. Frontocentral FCz/Fz carry the N2 and frontal-midline theta (control signals); central Cz carries the NoGo-P3 and ERN; parietal Pz carries the P3b (resource allocation). These are the a-priori sites; scalp-wide effects are confirmed by cluster permutation (§6).
Report in the Methods chapter — for reproducibility

รายงานให้ reproducible: the number of bad channels per participant, the number of ICs removed and the ICLabel criterion, the reference used, and the number of surviving epochs — following ARTEM-IS / ERP reporting standards.

1.6EEG during 9-square movement — the hardest problem

จุดยากที่สุด — ต้องมีแผนชัด. Vigorous stepping injects heavy motion, EMG, and electrode-cable artifact. Reading EEG “while moving” at face value is not credible; the plan is an explicit three-layer defence:

pythonListing 2 · ASR
# (A) ASR — ลบ transient burst artifact โดยไม่ทิ้งทั้ง epoch (ก่อนหรือแทน reject แข็ง)
import asrpy
asr = asrpy.ASR(sfreq=raw_erp.info["sfreq"], cutoff=20)   # cutoff 15–30 (ยิ่งต่ำยิ่ง aggressive)
asr.fit(raw_erp.copy().filter(1., 40.))                   # fit บนช่วง "สะอาด" (baseline)
raw_move = asr.transform(raw_move_raw)
Listing 2. Artifact Subspace Reconstruction repairs transient bursts sample-by-sample instead of discarding whole epochs, preserving far more movement data than hard rejection.

02Spectral power & the aperiodic–periodic split

For Baseline and 9-square, band power indexes attention and arousal. Welch PSD is computed on overlapping fixed-length epochs; power is expressed in decibels because that is more robust to outliers than raw µV². Absolute band power is complemented by relative band power (each band normalised by total) to remove global scale differences between sessions.

pythonListing 3 · Welch band power
epochs = mne.make_fixed_length_epochs(raw, duration=2.0, overlap=1.0, preload=True)
spec = epochs.compute_psd(method="welch", fmin=1, fmax=45, n_fft=int(2*epochs.info["sfreq"]))
psds, freqs = spec.get_data(return_freqs=True)          # (n_epochs, n_ch, n_freqs)
psds_db = 10*np.log10(psds)                              # dB — ทน outlier กว่า raw µV²

bands = {"delta":(1,4),"theta":(4,8),"alpha":(8,13),"beta":(13,30),"gamma":(30,45)}
def bp(lo,hi):
    idx=(freqs>=lo)&(freqs<hi); return psds[...,idx].mean(-1)
abs_bp={b:bp(lo,hi) for b,(lo,hi) in bands.items()}
total=sum(abs_bp.values()); rel_bp={b:abs_bp[b]/total for b in bands}   # relative = ตัดสเกลรวม

ch=epochs.ch_names; Fz=ch.index("Fz"); Pz=ch.index("Pz")
fm_theta = abs_bp["theta"][:,Fz].mean()          # frontal-midline theta = control/effort
parietal_alpha = abs_bp["alpha"][:,Pz].mean()    # alpha↓ = engagement มากขึ้น
Listing 3. Five canonical bands (δ θ α β γ). Frontal-midline theta at Fz indexes control/effort; parietal alpha desynchronisation (a decrease) indexes engagement.

2.1Separating aperiodic 1/f from true oscillations

สำคัญกับ theta/beta ratio. A change in “theta power” or the theta/beta ratio can be driven entirely by the 1/f aperiodic slope rather than a genuine oscillation. Parameterising the spectrum with specparam (FOOOF) splits the two — an aperiodic component (offset + exponent, the 1/f steepness) and periodic peaks (centre frequency, power, bandwidth) — so an “attention change” can be correctly attributed to an oscillation versus broadband arousal.

Figure 5Power spectrum — aperiodic 1/f vs. periodic peaks
full spectrumaperiodic (1/f) fitperiodic peak (θ, α)
Fig 5. On log–log axes the aperiodic component is a straight line whose slope is the exponent; oscillations appear as bumps above it. Reporting both band power and the aperiodic exponent + periodic peaks prevents mis-reading a 1/f shift as an oscillatory one.
pythonListing 4 · specparam / FOOOF
from specparam import SpectralModel
sm = SpectralModel(peak_width_limits=[1,8], max_n_peaks=6)
sm.fit(freqs, psds[:, Fz, :].mean(0), [1,45])
# แยก: aperiodic (offset, exponent = ความชัน 1/f) ↔ periodic peaks (theta/alpha center, power, bandwidth)
theta_peak = sm.get_params("peak_params")   # เอา oscillation จริง ไม่ปนกับ 1/f
Listing 4. The parameterised model returns the aperiodic exponent/offset and each periodic peak separately, isolating the genuine theta/alpha oscillation from the 1/f background.

03ERP — Flanker & Go/NoGo

The event-related potentials are the study's most powerful axis for both attention and automaticity. Epochs are cut −200 to +800 ms around stimulus onset, baseline-corrected on the pre-stimulus interval, and — critically — only correct-response trials are averaged for the standard N2/P3, so component amplitudes are not contaminated by error processing.

epoch=[−200 ms,  +800 ms], baseline = [−200, 0] msstimulus-locked
Mean amplitude over a window is preferred to peak amplitude because it is more robust to noise:
Ā=1t2−t1t1t2 V(t) dt
pythonListing 5 · epoching & averaging
events, event_id = mne.events_from_annotations(raw_erp)
epochs = mne.Epochs(raw_erp, events, event_id, tmin=-0.2, tmax=0.8,
                    baseline=(-0.2, 0.0), reject=dict(eeg=120e-6), preload=True)
# เฉลี่ยเฉพาะ trial ที่ตอบถูก (correct-only) สำหรับ N2/P3 ปกติ
erp_cong   = epochs["flanker/congruent/correct"].average()
erp_incong = epochs["flanker/incongruent/correct"].average()
erp_go     = epochs["go/correct"].average()
erp_nogo   = epochs["nogo/correct"].average()
Listing 5. Hierarchical event ids (task/condition/accuracy) let each cell be averaged with one selector. A 120 µV peak-to-peak reject rule removes residual gross artifacts after ICA.

Components to extract (Cai et al. 2025 scoping review: P3 dominates the field with 43 studies, N2 with 17):

ComponentWindowSiteMeaningAnswers
N2200–350 msFCz/Fzconflict monitoring / inhibitionFlanker-incong, NoGo → automaticity: N2↓ post
P3/P300300–600 msPz (P3b)/Czresource allocationattention: amplitude; NoGo-P3 = inhibition success
ERN/Ne · Pe0–150 ms post-errorFCz/Czerror detection / awarenesserror monitoring, adaptive control
pythonListing 6 · component quantification
def mean_amp(evoked, ch, tmin, tmax):            # mean amplitude = ทน noise > peak
    return evoked.copy().pick([ch]).crop(tmin,tmax).data.mean()
def peak(evoked, ch, tmin, tmax, mode):
    _,lat,amp = evoked.copy().pick([ch]).get_peak(tmin=tmin,tmax=tmax,mode=mode,return_amplitude=True)
    return amp, lat

p3_incong = mean_amp(erp_incong,"Pz",0.30,0.60);  n2_incong = mean_amp(erp_incong,"FCz",0.20,0.35)
p3_nogo   = mean_amp(erp_nogo,  "Pz",0.30,0.60);  n2_nogo   = mean_amp(erp_nogo,  "FCz",0.20,0.35)
_,p3_lat  = peak(erp_incong,"Pz",0.30,0.60,"pos")     # latency = ความเร็วประมวลผล (automaticity)
Listing 6. Amplitudes are mean-over-window; P3 latency is a peak measure — a shortening latency is one of the automaticity criteria (faster processing).

3.1Difference waves — isolating the conflict/inhibition signal

Subtracting the easy from the hard condition cancels the shared sensory response and leaves the pure conflict/inhibition signal, which is the most direct pre↔post contrast.

pythonListing 7 · difference waves
flanker_diff = mne.combine_evoked([erp_incong, erp_cong], weights=[1,-1])   # incong − cong
nogo_diff    = mne.combine_evoked([erp_nogo,  erp_go],   weights=[1,-1])    # NoGo − Go
n2_conflict  = mean_amp(flanker_diff,"FCz",0.20,0.35)   # N2-conflict effect
p3_nogo_eff  = mean_amp(nogo_diff,  "Cz", 0.30,0.60)    # NoGo-P3 inhibition effect
Listing 7. The Flanker (incongruent−congruent) and Go/NoGo (NoGo−Go) difference waves. Automaticity reads as a shrinking N2-conflict effect after training, provided behaviour stays as fast and accurate.
Figure 6ERP difference wave — congruent vs. incongruent
congruent (correct)incongruent (correct)difference (incong−cong)
Fig 6. Schematic Flanker ERPs at FCz/Pz (negative plotted up, ERP convention). The incongruent trace shows a larger fronto-central N2 (~250 ms) and modulated P3 (~400 ms); the dashed difference wave is the conflict signal. The small early deflection marks the response-locked ERN. Training is predicted to compress the difference wave.

04Time-frequency — conflict theta & ITC

Complementing the ERP, Morlet time-frequency decomposition recovers the frontal-midline theta burst that the averaged waveform partly hides, plus inter-trial coherence. Power is baseline-corrected as a log-ratio; the analysis window is the canonical FCz theta band, 4–8 Hz, 200–500 ms.

ITC(f,t)=|1Nk=1N e iφk(f,t)|inter-trial coherence ∈ [0,1]
ITC is the resultant length of single-trial phases φk: 0 = random phase, 1 = perfect phase-locking across trials.
pythonListing 8 · Morlet TFR + ITC
freqs = np.arange(3,31,1); n_cycles = freqs/2.
tfr = epochs["flanker/incongruent/correct"].compute_tfr(
        method="morlet", freqs=freqs, n_cycles=n_cycles, return_itc=True, average=True)
power, itc = tfr
power.apply_baseline((-0.2,0.0), mode="logratio")       # baseline-correct (dB-like)
# frontal-midline theta (4–8 Hz, 200–500 ms) ที่ FCz = conflict theta
fmt = power.copy().pick(["FCz"]).crop(0.2,0.5,fmin=4,fmax=8).data.mean()
fmt_itc = itc.copy().pick(["FCz"]).crop(0.2,0.5,fmin=4,fmax=8).data.mean()   # phase-locking
Listing 8. Power = the energy of conflict theta; ITC = phase consistency across trials (high = systematic processing). Griggs et al. 2023 found acute exercise increases frontal theta during Flanker — the same frame is applied here pre↔post.

Automaticity read-out: conflict-theta decreasing after training (less control engaged) is consistent with, and corroborates, the N2 decrease.

05Heart rate & HRV — an autonomic index of effort

Heart-rate variability indexes attentional effort / cognitive load through the neurovisceral integration model (Thayer & Lane): higher vagal / HF-HRV tracks better self-regulation. HRV is computed per condition × pre/post from the ECG (or PPG) R-peaks with NeuroKit2.

RMSSD=1N−1i=1N−1(RRi+1−RRi)2vagal / parasympathetic
SDNN=1N−1i=1N(RRiRR)2overall variability
LF/HF=PLF (0.04–0.15 Hz)PHF (0.15–0.40 Hz)·%HRmax=HR220−age
pythonListing 9 · NeuroKit2 HRV
import neurokit2 as nk
signals, info = nk.ecg_process(ecg_raw, sampling_rate=1000)      # หรือ nk.ppg_process ถ้าเป็น PPG
# ต่อ "เงื่อนไข" — segment ตาม event ของแต่ละ task แล้วคำนวณ HRV แยก
hrv = nk.hrv(info["ECG_R_Peaks"], sampling_rate=1000, show=False)
# ดัชนีหลัก: HRV_RMSSD, HRV_SDNN (overall/vagal) · HRV_HF, HRV_LF, HRV_LFHF (autonomic balance) · mean HR
Listing 9. R-peaks are detected once, then segmented per task so that each condition×time cell yields its own RMSSD, SDNN, HF/LF and mean HR.

06Statistics — linking EEG ↔ heart ↔ behaviour

The core design is 2 (Time: pre/post) × Condition. Because the data are trial-level, a linear mixed model is preferred to a subject-mean rmANOVA — it uses every trial and models per-subject random slopes of time.

pythonListing 10 · rmANOVA & LMM
import pingouin as pg          # หรือ statsmodels / lme4 ใน R
# rmANOVA (subject-level means)
aov = pg.rm_anova(dv="P3_amp", within=["time","condition"], subject="subj", data=df, detailed=True)
# LMM (trial-level, แนะนำ)
import statsmodels.formula.api as smf
m = smf.mixedlm("P3_amp ~ time*condition", df, groups=df["subj"],
                re_formula="~time").fit()      # random slope ของ time ต่อ subject
Listing 10. The rmANOVA gives the familiar factorial test on subject means; the mixed model (time*condition fixed, random slope of time per subject) is the recommended trial-level analysis.

Controlling multiple comparisons across EEG — the gold standard is cluster-based permutation, which needs no a-priori choice of window or channel. The cluster statistic sums the per-sample t within a supra-threshold cluster and compares it to a null built by permutation:

tcluster=(t,ch)∈C t(t,ch),p=# { tpermmax ≥ tcluster }Npermnperm = 5000
pythonListing 11 · cluster permutation
from mne.stats import permutation_cluster_1samp_test
# X = post − pre ต่อ subject, shape (n_subj, n_times[, n_ch]) → หา cluster ที่ต่างจาก 0
T_obs, clusters, p, _ = permutation_cluster_1samp_test(X, n_permutations=5000, tail=0, seed=42)
Listing 11. A one-sample test on the per-subject post−pre difference finds spatiotemporal clusters that differ from zero, controlling the family-wise error rate without pre-selecting windows.

Behaviour & brain–behaviour coupling

Behaviour: RT (correct), accuracy, RT variability (SD/CV), d′ (Go/NoGo sensitivity), the Flanker effect (incong−cong RT), and post-error slowing.

CVRT=SDRTRT·d′=z(H)−z(FA)·Flanker=RTincong−RTcong

Linking EEG↔behaviour: correlate Δ(N2/P3/theta) with Δ(RT / RT-CV / d′) to test whether a change in the wave explains the change in behaviour (mediation is available). Effect size & certainty: report partial η² / Cohen's dz with 95% CIs; consider Bayesian tests (JASP / pingouin.ttest(...bayesian)) to support the null (e.g. accuracy genuinely unchanged = accuracy really was held constant); and estimate power / sample size in advance.

6.1The operational “became automatic” decision framework

ต้องเข้าครบ ไม่ใช่ตัวเดียว. Automaticity is declared only if the pattern is joint. This is the pre-registrable rule the whole methodology exists to test:

  1. RT faster (Δ<0) and RT variability (CV) lower.
  2. N2 and/or conflict-theta decrease in the hard condition (difference wave).
  3. P3 latency shortens (and/or amplitude shifts per framework).
  4. Accuracy / d′ hold or improve — not fast-because-careless; the speed–accuracy trade-off must not be spent.
  5. (supporting) HRV falls less on the same task = reduced autonomic effort.
Decision rule

Criteria 1–4 met (plus 5)automaticity, not merely a performance improvement. Any single index moving alone is insufficient and is reported as such.

§Method references

งานจริง — resolve DOI ยืนยันก่อนลงเล่ม. Every DOI below is resolved and quartile-joined by the harvester before final submission; classic entries are flagged for a resolve-check.

Confirmed (3-vote)

Sourced (DOI/PMID; re-check)

Standard method / theory (classic — verify DOI before use)

!Integrity note

หมายเหตุความซื่อสัตย์ — honesty note
  1. In-motion EEG (9-square) is the hardest signal — the confirmatory results are the pre/post contrasts of resting + seated tasks; movement-EEG is secondary/exploratory with limitations stated plainly.
  2. Some classic DOIs/quartiles are cited from standard knowledge — the harvester (harvest.py) resolves every DOI and joins the real quartile before submission.
  3. The code is a skeleton — trigger names, channel labels, file format and sampling rate must match the actual recording rig, and reject thresholds / ICA settings must be tuned to signal quality.
  4. Pre-register + BIDS: where possible, pre-register the hypotheses and the operational definition of automaticity, and organise the data as EEG-BIDS for reproducibility.

Methods grounded in published work and standard pipelines (MNE-Python · EEGLAB · NeuroKit2) — no fabricated numbers or DOIs. Companion to the reference library. thesis.globmaps.com · expanded 2026-07-23.