المحطة 9 · الدرس 2

سكربت لمشارك واحد

أساسي15 دقيقةقبل هذا الدرس: الأمر eegh وسجل الأوامر، ICLabel وحذف المكوّناتاطبع البطاقة
تبدأ من: eeglab_data.set (D1) · لا شيء: البيانات كما نزّلتها.تنزيل البيانات (يفتح في نافذة جديدة)
سكربت الوصول لهذه الحالة
MATLAB
% Start from a fresh MATLAB session: the script below does everything
clear
[ALLEEG, EEG, CURRENTSET, ALLCOM] = eeglab;

الفكرة باختصار

في المحطات 1 إلى 5 بنيت المسار خطوة خطوة: تحميل، ثم فلترfilterأداة تُضعف ترددات معيّنة في الإشارة وتُبقي غيرها.، ثم ضوضاء الكهرباءline noiseتداخل من شبكة الكهرباء عند 60 هرتز في السعودية أو 50 هرتز في بلدان أخرى.، ثم قناةchannelالإشارة المسجّلة من قطب واحد، وهي ما يعرضه EEGLAB. السيئة، ثم المرجعreferenceالنقطة التي يُقاس جهد كل قناة بالنسبة لها.، ثم ICAطريقة تفصل الإشارة المسجّلة إلى مصادر مستقلة، بعضها دماغي وبعضها آرتفاكت.، ثم ICLabelأداة تصنّف كل مكوّن ICA إلى سبع فئات مع احتمال لكل فئة.، ثم التقطيع والخط القاعدي رفضrejectionاستبعاد epochs أو بيانات لا تصلح للتحليل.. وكل درس كان فيه سكربتscriptملف أوامر MATLAB ينفّذ خطوات المعالجة بالترتيب. لخطوته فقط.

قبل وبعد

أقسام سكربت المشارك الواحد في محرّر MATLAB، كل قسم يبدأ بسطر %%، وسهم من كل قسم إلى درسه في الخريطة: Load إلى ملفات set و fdt في المحطة 1، والفلترة والقنوات السيئة والمرجع إلى دروس المحطة 3، و ICA و ICLabel إلى المحطة 4، والتقطيع إلى المحطة 5.

أسئلتك بعد هذا، كل جواب في قسمه:

لماذا أحتاج هذه الخطوة؟

السبب بالتفصيل، والمصطلحات التي تحتاجها

الآن تجمعها في ملف واحد. هذا الملف هو مشروعك الحقيقي: إذا ضاعت كل الملفات المعالجة، يعيدها كلها. وإذا سألك مراجع “كيف نظّفت البيانات؟”، فالجواب هذا الملف. وإذا أردت تغيير عتبة واحدة، تغيّر رقماً وتعيد التشغيل بدل أن تعيد عشرين ضغطة.

الفكرة الأساسية: كل سطر في السكربت هو نفس السطر الذي تعلّمته في درسه، بلا تغيير. لا شيء جديد في المعالجة، الجديد هو الترتيب والتنظيم.

كيف أطبّقها في EEGLAB؟

9 خطوة من القوائم

أولاً: افتح ملفاً جديداً بأقسام

  1. اكتب edit d1_pipeline.m في نافذة الأوامر. يفتح محرر MATLAB ملفاً فارغاً.
  2. قسّم الملف بأسطر تبدأ بـ %%. كل سطر منها بداية قسم، ويستطيع المحرر تشغيل كل قسم وحده (Run Section). اجعل قسماً لكل محطة.

ثانياً: املأ الأقسام من دروسك

  1. القسم الأول للإعدادات: مجلد البيانات، ومجلد الحفظ، ترددfrequencyعدد الدورات في الثانية، ويُقاس بالهرتز. الكهرباء. كل ما يتغيّر بين مشروع وآخر هنا، لا في وسط الكود.
  2. لكل خطوة، انسخ السطر من قسم “السكربت المقابل” في درسها، أو من eegh بعد أن تنفّذها من الواجهةGUIالنوافذ والقوائم التي نستخدمها بالفأرة بدل كتابة الأوامر. كما في درس سجل الأوامرcommand historyقائمة الأوامر التي نفّذتها الواجهة، ويعرضها الأمر eegh.. لا تنسخ سطور الرسم والتمارين، فقط سطور المعالجة.
  3. احفظ نسختين فقط: النسخة النظيفة المستمرة، والـ epochمقطع زمني قصير مقتطع حول حدث. النظيفة. كما في درس تنظيم الملفات، الباقي يعاد حسابه.
  4. أضف في النهاية قسماً يطبع ملخصاً: كم قناة سيئة، وكم مكوّناً حُذف، وكم epoch بقيت.

ثالثاً: شغّله من الصفر

  1. أغلق EEGLAB، واكتب clear في نافذة الأوامر، ثم شغّل الملف كله بـ Run في المحرر أو بكتابة d1_pipeline.
  2. أثناء التشغيل لا تضغط شيئاً. إذا فُتحت نافذة تنتظرك، فهناك سطر بلا معاملات كافية: أصلحه.
  3. قارن الناتج بما صنعته من الواجهة في المحطة 5: نفس عدد القنوات، ونفس عدد الـ epochs تقريباً.

كيف أكتبها بالكود؟

نفس الخطوات في 101 أسطر MATLAB
MATLAB
%% D1 single-subject pipeline: raw data to clean epochs
% Each line comes from the lesson of its step (station in the section title)

%% Settings
eeglab nogui
eeglabRoot = fileparts(which('eeglab'));
dataDir    = fullfile(eeglabRoot, 'sample_data');
outDir     = pwd;
lineFreq   = 60;   % 60 Hz in Saudi Arabia and the Americas, 50 Hz in Europe

%% Load (station 1)
EEG = pop_loadset('filename', 'eeglab_data.set', 'filepath', dataDir);
fullLocs = EEG.chanlocs;

%% Filtering and line noise (station 3)
% High-pass: passband edge at 0.1 Hz (removes slow drift)
EEG = pop_eegfiltnew(EEG, 'locutoff', 0.1);
% Notch: remove a narrow band around the line frequency (revfilt = 1)
EEG = pop_eegfiltnew(EEG, 'locutoff', lineFreq - 2, 'hicutoff', lineFreq + 2, 'revfilt', 1);

%% Bad channels and interpolation (station 3)
% Detection with clean_rawdata channel criteria only:
% no drift removal, no ASR, no window rejection, EOG channels ignored
EEG_test = pop_clean_rawdata(EEG, 'FlatlineCriterion', 5, 'ChannelCriterion', 0.8, ...
    'LineNoiseCriterion', 4, 'Highpass', 'off', 'BurstCriterion', 'off', ...
    'WindowCriterion', 'off', 'BurstRejection', 'off', 'Distance', 'Euclidian', ...
    'channels_ignore', {'EOG1', 'EOG2'});

% Names of the channels it removed
badChans = setdiff({EEG.chanlocs.labels}, {EEG_test.chanlocs.labels}, 'stable');

% Add channels you found by eye, e.g. badChans = [badChans {'T7'}];
fprintf('Bad channels: %s\n', strjoin(badChans, ', '));

% Remove them by name, then rebuild them from the full channel list (fullLocs)
if ~isempty(badChans)
    EEG = pop_select(EEG, 'rmchannel', badChans);
end
if ~isempty(badChans)
    EEG = pop_interp(EEG, fullLocs, 'spherical');
end

%% Average reference (station 3)
% Average reference, EOG channels excluded
eogIdx = find(ismember({EEG.chanlocs.labels}, {'EOG1', 'EOG2'}));
EEG = pop_reref(EEG, [], 'exclude', eogIdx);
EEG.setname = 'D1 main';

%% ICA on a 1-Hz copy, weights copied back (station 4)
% Main dataset (high-pass 0.1 Hz, line noise removed, average reference) is in EEG
EEG_main = EEG;

% ICA copy: same data, high-pass at 1 Hz
EEG_ica = pop_eegfiltnew(EEG_main, 'locutoff', 1);
EEG_ica.setname = 'D1 ica1hz';

% Expected rank: channels - interpolated channels - 1 (average reference)
expectedRank = EEG_ica.nbchan - numel(badChans) - 1;
fprintf('Expected data rank: %d\n', expectedRank);

% Extended Infomax on the ICA copy
EEG_ica = pop_runica(EEG_ica, 'icatype', 'runica', 'extended', 1);
% If "Data rank (N)" is missing or N differs from expectedRank, run instead:
% EEG_ica = pop_runica(EEG_ica, 'icatype', 'runica', 'extended', 1, 'pca', expectedRank);

% Copy the decomposition back to the main dataset
EEG_main.icaweights  = EEG_ica.icaweights;
EEG_main.icasphere   = EEG_ica.icasphere;
EEG_main.icachansind = EEG_ica.icachansind;
EEG_main.icawinv     = [];   % recomputed by eeg_checkset
EEG_main.icaact      = [];
EEG = eeg_checkset(EEG_main);
clear EEG_main EEG_ica EEG_test   % free memory

%% ICLabel: remove eye and muscle components, then low-pass (station 4)
% Classify all components with the default ICLabel classifier
EEG = pop_iclabel(EEG, 'default');

% Flag components above 90% Muscle or Eye
% Row order: Brain, Muscle, Eye, Heart, Line Noise, Channel Noise, Other
thresh = [NaN NaN; 0.9 1; 0.9 1; NaN NaN; NaN NaN; NaN NaN; NaN NaN];
EEG = pop_icflag(EEG, thresh);
artifactComps = find(EEG.reject.gcompreject);

% Remove them from the main dataset
if ~isempty(artifactComps)
    EEG = pop_subcomp(EEG, artifactComps, 0);
end
EEG.setname = 'D1 main clean';

% Low-pass at 30 Hz, postponed until after ICA cleaning
EEG = pop_eegfiltnew(EEG, 'hicutoff', 30);
EEG.setname = 'D1 main clean lp30';
EEG = pop_saveset(EEG, 'filename', 'D1_main_clean_lp30.set', 'filepath', outDir);

%% Epochs, baseline, rejection (station 5)
% Cut 3-s epochs around every 'square' event: 1 s before to 2 s after
EEG = pop_epoch(EEG, {'square'}, [-1 2], 'newname', 'D1 epochs', 'epochinfo', 'yes');
nEpochsAll = EEG.trials;

% Subtract the mean of -200 to 0 ms from every channel and epoch
EEG = pop_rmbase(EEG, [-200 0]);
EEG.setname = 'D1 epochs bl';

% Mark epochs where any channel goes beyond +/-100 uV anywhere in the epoch
EEG = pop_eegthresh(EEG, 1, 1:EEG.nbchan, -100, 100, EEG.xmin, EEG.xmax, 0, 0);
badEpochs = find(EEG.reject.rejthresh);
fprintf('Marked %d of %d epochs\n', numel(badEpochs), EEG.trials);

% Remove the marked epochs
if ~isempty(badEpochs)
    EEG = pop_rejepoch(EEG, EEG.reject.rejthresh, 0);
end
EEG.setname = 'D1 epochs clean';
EEG = eeg_checkset(EEG);
EEG = pop_saveset(EEG, 'filename', 'D1_epochs_clean.set', 'filepath', outDir);

%% Summary
fprintf('\n--- D1 pipeline summary ---\n');
fprintf('Bad channels interpolated: %d (%s)\n', numel(badChans), strjoin(badChans, ', '));
fprintf('ICA components removed:    %d (%s)\n', numel(artifactComps), num2str(artifactComps(:)'));
fprintf('Epochs kept:               %d of %d\n', EEG.trials, nEpochsAll);
fprintf('Saved: D1_main_clean_lp30.set and D1_epochs_clean.set in %s\n', outDir);

أي قيمة أختار؟

القيم المناسبة حسب هدف تحليلك
القرارالاختيار في الدرسلماذا
الإعداداتفي قسم Settings في أول الملفما يتغيّر بين المشاريع (المجلدات، تردد الكهرباء) في مكان واحد. لا رقم مخفي في وسط الكود.
تقسيم الملفقسم %% لكل محطةتشغّل قسماً وحده عند التجربة، وتعرف من العنوان أي درس تراجع إذا ظهر خطأ.
ما يُحفظنسختان: المستمرة النظيفة والـ epochs النظيفةالأولى تكفي لأي تقطيع جديد، والثانية تدخل المتوسط والإحصاء (درس تنظيم الملفات).
القنوات السيئةالكشف الآلي، مع سطر لإضافة ما وجدته بعينكالسكربت لا يرى كما ترى. راجع البيانات مرة من الواجهة، واكتب ما وجدته في badChans (درس القنوات السيئة).
تكرار ICAيختلف قليلاً في كل تشغيلrunica يبدأ من حالة عشوائية مأخوذة من الساعة (runica.m). لتشغيل مطابق تماماً أضف 'reset_randomseed', 'no' إلى pop_runica.

كيف أتأكد أنها نجحت؟

4 فحوص سريعة
  • السكربت يعمل بعد clear من البداية للنهاية بلا أي نافذة تنتظرك.
  • الملخص يطبع عدد القنوات السيئة والمكوّنات المحذوفة والـ epochs الباقية من 80.
  • في مجلد العمل ملفان: D1_main_clean_lp30.set و D1_epochs_clean.set.
  • size(EEG.data) في النهاية 32 × 384 × عدد الـ epochs الباقية، كما في درس رفض المحاولات.

ما الأخطاء التي يقع فيها كثيرون؟

5 أخطاء، وكيف تتجنّبها
  • تغيير السطور أثناء النسخ. كل سطر هنا مأخوذ كما هو من درسه. إذا “حسّنت” قيمة أثناء النسخ، فلن تطابق النتيجة ما شرحه الدرس.
  • الاعتماد على متغيّرات من جلسة سابقة. السكربت الذي يعمل فقط لأن EEG أو badChans موجود من قبل سيفشل غداً. شغّله دائماً بعد clear.
  • إبقاء سطور الرسم. pop_viewprops و figure وغيرها تفتح نوافذ وتبطئ التشغيل، وتوقف السكربت على عنقود الحوسبة. السكربت للمعالجة، والرسم في سكربت منفصل.
  • ترتيب خاطئ. الـ low-pass بعد ICA، والتقطيع بعد ICA، والمرجع بعد الاستيفاء. هذا الترتيب مشروح في دروسه، ولا تنقل خطوة من مكانها بدون سبب.
  • مسارات ثابتة من جهازك. fullfile(eeglabRoot, 'sample_data') يعمل على أي جهاز. C:\Users\... يعمل على جهازك فقط.

كيف أكتبها في ورقتي؟

قالب Methods: عدّل البيانات والإعدادات والإصدارات لتطابق ما نفّذته

قالب: عدّل البيانات والإعدادات والإصدارات لتطابق ما نفّذته.

Data were high-pass filtered (passband edge 0.1 Hz; firfilt plugin 2.8, EEGLAB 2026.1), and line noise was attenuated with a band-stop filter (passband edges 58 and 62 Hz). Bad channels were identified with clean_rawdata (clean_rawdata plugin 2.11; flatline 5 s, channel correlation 0.8) and interpolated ([n] channels), then data were average-referenced, EOG excluded. Independent component analysis (extended Infomax; Lee et al., 1999) was computed on a 1 Hz high-passed copy, and components labelled Eye or Muscle with probability ≥ 0.9 (ICLabel 1.7; Pion-Tonachini et al., 2019) were removed ([n] components). After low-pass filtering (passband edge 30 Hz), epochs from −1 to 2 s were baseline-corrected (−200 to 0 ms), and epochs beyond ±100 µV were rejected ([n] of 80).

أين أقرأ أكثر؟

3 مراجع: التوثيق الرسمي أولاً

مكتوب لـ EEGLAB 2026.1 · 2026-10-28

تحتاج مساعدة؟

اسأل، أو شاركنا رأيك، أو بلّغ عن خطأ