المحطة 9 · الدرس 3
معالجة دفعات (batch)
sub-002_task-FaceRecognition_eeg.set (D4) · لا شيء: البيانات كما نزّلتها.تنزيل البيانات (يفتح في نافذة جديدة)سكربت الوصول لهذه الحالة
% Start from a fresh MATLAB session: the batch script loads every subject itself
clear
[ALLEEG, EEG, CURRENTSET, ALLCOM] = eeglab;زر الرجوع في متصفحك يعيدك لنفس المكان الذي كنت فيه، فافتح ما تشاء بلا قلق.
الفكرة باختصار
سكربتscriptملف أوامر MATLAB ينفّذ خطوات المعالجة بالترتيب. مشاركparticipantالشخص الذي سُجّلت بياناته في التجربة. الواحد يعمل على D1. مشروعك الحقيقي فيه عشرون مشاركاً أو أكثر، بنفس الخطوات ونفس الإعدادات لكل واحد. تشغيل السكربت عشرين مرة وتغيير اسم الملف بيدك كل مرة بطيء، ويفتح باب الخطأ.
الحل حلقة (loop): بأسماء المشاركين، ونفس الخطوات تُطبّق على كل اسم. هذا اسمه معالجة دفعاتbatch processingتطبيق نفس السكربت على ملفات عدة مشاركين تلقائياً. (batch processing).
قبل وبعد
أسئلتك بعد هذا، كل جواب في قسمه:
لماذا أحتاج هذه الخطوة؟
السبب بالتفصيل، والمصطلحات التي تحتاجها
لكن الحلقة البسيطة فيها مشكلة: إذا فشل المشارك السابع لسبب ما (ملف ناقص، قناةchannelالإشارة المسجّلة من قطب واحد، وهي ما يعرضه EEGLAB. غريبة، ذاكرة)، يتوقف كل شيء، وتجد الصباح أن ستة فقط انتهوا. لهذا نلفّ كل مشارك بـ try و catch: إذا فشل، يُسجَّل الخطأ في ملف، وتكمل الحلقة للمشارك التالي.
في هذا الدرس نعالج ثلاثة مشاركين من D4 فقط (حوالي 700 ميجابايت للتنزيل). نفس السكربت يعمل على الثمانية عشر إذا نزّلتهم.
لماذا أحتاج هذه الخطوة؟
لكن الحلقة البسيطة فيها مشكلة: إذا فشل المشارك السابع لسبب ما (ملف ناقص، قناة غريبة، ذاكرة)، يتوقف كل شيء، وتجد الصباح أن ستة فقط انتهوا. لهذا نلفّ كل مشارك بـ try و catch: إذا فشل، يُسجَّل الخطأ في ملف، وتكمل الحلقة للمشارك التالي.
في هذا الدرس نعالج ثلاثة مشاركين من D4 فقط (حوالي 700 ميجابايت للتنزيل). نفس السكربت يعمل على الثمانية عشر إذا نزّلتهم.
كيف أطبّقها في EEGLAB؟
10 خطوة من القوائم
أولاً: نزّل ثلاثة مشاركين من D4
- أنشئ مجلداً اسمه
faces_projectوفيه مجلدrawdata، واجعلfaces_projectمجلد العمل في MATLAB (cd). شغّل كل سكربتات المحطة 9 من داخل هذا المجلد. درس تنظيم الملفات يشرح بقية المجلدات. - من صفحة ds002718 في OpenNeuro نزّل إلى
rawdataالملفات العامة (dataset_description.jsonوREADMEوtask-FaceRecognition_events.json) ومجلداتsub-002وsub-003وsub-004كاملة. أو من سطر أوامر النظام، إذا كان عندك AWS CLI:
aws s3 sync --no-sign-request s3://openneuro.org/ds002718 rawdata --exclude "sub-*" --include "sub-002/*" --include "sub-003/*" --include "sub-004/*"ثانياً: جرّب المسار على مشارك واحد من الواجهةGUIالنوافذ والقوائم التي نستخدمها بالفأرة بدل كتابة الأوامر.
- افتح File ثم Import data ثم Using EEGLAB functions and plugins ثم From BIDS folder structure، واختر مجلد
rawdata. فعّل استيراد حدثeventعلامة في التسجيل تحدد لحظة شيء مهم، مثل ظهور مثير. ومواقع قطبelectrodeالقطعة المعدنية التي توضع على الرأس لالتقاط الإشارة. من ملفات BIDSمعيار موحّد لتسمية ملفات البيانات العصبية وتنظيمها في مجلدات.، واجعل نوع الحدثevent typeاسم أو رقم يميّز نوع الحدث، مثل "square". من العمودtrial_type. اضغط Ok. تُنشأ STUDYبنية في EEGLAB تجمع داتاست عدة مشاركين لتحليلها معاً. فيها المشاركون الثلاثة. - اختر مشاركاً واحداً من قائمة Datasets، وطبّق عليه الخطوات من الواجهة كما في المحطات 3 إلى 5، مع تغيير ثلاثة إعدادات فقط: الكهرباء في بريطانيا 50 هرتز لا 60، وقنوات العين
EEG061وEEG062، وقنوات القلبheart (ECG)نبض القلب الكهربائي الذي قد يظهر في EEG كقمم منتظمة.EEG063وEEG064تُحذف. - اكتب
eegh. هذه السطور هي ما ستضعه داخل الحلقة.
ثالثاً: حوّل المسار إلى حلقة
- انسخ سكربت المشارك الواحد إلى ملف جديد
batch_d4.m. انقل خطوات المعالجة إلى دالة في آخر الملف اسمهاprocess_subject، تأخذ ملف المشارك والإعدادات، وتحفظ النتائج، وتعيد ملخصاً. - في أعلى الملف: قائمة المشاركين، والإعدادات، وحلقة
forتستدعي الدالة داخلtry. - في
catch: اكتب اسم المشارك ورسالة الخطأ فيlogs/batch_log.txt، وسجّل الحالةfailedفي جدول الملخص. - شغّل الملف. ICA على 72 قناة (70 EEG واثنتان EOG) وخمسين دقيقة تسجيل يأخذ وقتاً لكل مشارك، فاتركه يعمل. إذا كان مشاركوك كثيرين، انظر درس الحوسبة على HPC.
- بعد الانتهاء، افتح
batch_summary.csv. كل صف مشارك: الحالة، والقنوات السيئة، والمكوّنات المحذوفة، والـ epochs الباقية.
كيف أطبّقها في EEGLAB؟
أولاً: نزّل ثلاثة مشاركين من D4
- أنشئ مجلداً اسمه
faces_projectوفيه مجلدrawdata، واجعلfaces_projectمجلد العمل في MATLAB (cd). شغّل كل سكربتات المحطة 9 من داخل هذا المجلد. درس تنظيم الملفات يشرح بقية المجلدات. - من صفحة ds002718 في OpenNeuro نزّل إلى
rawdataالملفات العامة (dataset_description.jsonوREADMEوtask-FaceRecognition_events.json) ومجلداتsub-002وsub-003وsub-004كاملة. أو من سطر أوامر النظام، إذا كان عندك AWS CLI:
aws s3 sync --no-sign-request s3://openneuro.org/ds002718 rawdata --exclude "sub-*" --include "sub-002/*" --include "sub-003/*" --include "sub-004/*"ثانياً: جرّب المسار على مشارك واحد من الواجهة
- افتح File ثم Import data ثم Using EEGLAB functions and plugins ثم From BIDS folder structure، واختر مجلد
rawdata. فعّل استيراد الأحداث ومواقع الأقطاب من ملفات BIDS، واجعل نوع الحدث من العمودtrial_type. اضغط Ok. تُنشأ STUDY فيها المشاركون الثلاثة. - اختر مشاركاً واحداً من قائمة Datasets، وطبّق عليه الخطوات من الواجهة كما في المحطات 3 إلى 5، مع تغيير ثلاثة إعدادات فقط: الكهرباء في بريطانيا 50 هرتز لا 60، وقنوات العين
EEG061وEEG062، وقنوات القلبEEG063وEEG064تُحذف. - اكتب
eegh. هذه السطور هي ما ستضعه داخل الحلقة.
ثالثاً: حوّل المسار إلى حلقة
- انسخ سكربت المشارك الواحد إلى ملف جديد
batch_d4.m. انقل خطوات المعالجة إلى دالة في آخر الملف اسمهاprocess_subject، تأخذ ملف المشارك والإعدادات، وتحفظ النتائج، وتعيد ملخصاً. - في أعلى الملف: قائمة المشاركين، والإعدادات، وحلقة
forتستدعي الدالة داخلtry. - في
catch: اكتب اسم المشارك ورسالة الخطأ فيlogs/batch_log.txt، وسجّل الحالةfailedفي جدول الملخص. - شغّل الملف. ICA على 72 قناة (70 EEG واثنتان EOG) وخمسين دقيقة تسجيل يأخذ وقتاً لكل مشارك، فاتركه يعمل. إذا كان مشاركوك كثيرين، انظر درس الحوسبة على HPC.
- بعد الانتهاء، افتح
batch_summary.csv. كل صف مشارك: الحالة، والقنوات السيئة، والمكوّنات المحذوفة، والـ epochs الباقية.
كيف أكتبها بالكود؟
نفس الخطوات في 118 أسطر MATLAB
%% D4 batch pipeline: same steps for every subject, errors logged
eeglab nogui
%% Settings
projectRoot = pwd;
bidsRoot = fullfile(projectRoot, 'rawdata');
derivRoot = fullfile(projectRoot, 'derivatives', 'eeglab');
logRoot = fullfile(projectRoot, 'logs');
subjects = {'sub-002', 'sub-003', 'sub-004'};
cfg.lineFreq = 50; % UK recording: 50 Hz mains
cfg.eogLabels = {'EEG061', 'EEG062'}; % HEOG, VEOG
cfg.dropLabels = {'EEG063', 'EEG064'}; % ECG channels
cfg.epochEvents = {'famous_new', 'unfamiliar_new', 'scrambled_new'};
cfg.epochWindow = [-1 2];
cfg.thresholdUv = 100;
if ~exist(logRoot, 'dir'), mkdir(logRoot); end
logFile = fullfile(logRoot, 'batch_log.txt');
%% Import BIDS data once: events from trial_type, locations from electrodes.tsv
[STUDY, ALLEEG] = pop_importbids(bidsRoot, 'eventtype', 'trial_type', ...
'bidsevent', 'on', 'bidschanloc', 'on', 'subjects', subjects, ...
'outputdir', derivRoot, 'studyName', 'faces');
clear STUDY ALLEEG % each subject is reloaded from disk inside the loop
%% Loop over subjects
nSub = numel(subjects);
status = repmat({'not run'}, nSub, 1);
nBad = nan(nSub, 1);
nIcRem = nan(nSub, 1);
nEpochs = nan(nSub, 1);
for s = 1:nSub
subj = subjects{s};
fprintf('\n===== %s (%d of %d) =====\n', subj, s, nSub);
try
inFile = fullfile(derivRoot, subj, 'eeg', sprintf('%s_task-FaceRecognition_eeg.set', subj));
res = process_subject(inFile, cfg);
status{s} = 'ok';
nBad(s) = res.nBadChans;
nIcRem(s) = res.nIcRemoved;
nEpochs(s) = res.nEpochs;
catch err
status{s} = 'failed';
fid = fopen(logFile, 'a');
fprintf(fid, '%s %s %s\n', datestr(now, 'yyyy-mm-dd HH:MM'), subj, err.message);
fclose(fid);
fprintf(2, 'FAILED %s: %s\n', subj, err.message);
end
end
%% Summary table, one row per subject
summary = table(subjects(:), status, nBad, nIcRem, nEpochs, ...
'VariableNames', {'subject', 'status', 'bad_channels', 'ics_removed', 'epochs_kept'});
disp(summary);
writetable(summary, fullfile(derivRoot, 'batch_summary.csv'));
%% ------------------------------------------------------------------
function res = process_subject(inFile, cfg)
% Single-subject pipeline from stations 3 to 5, with project settings in cfg
[inDir, baseName] = fileparts(inFile);
baseName = strrep(baseName, '_eeg', '');
EEG = pop_loadset('filename', [baseName '_eeg.set'], 'filepath', inDir);
% Remove ECG channels, keep EOG for ICA
EEG = pop_select(EEG, 'rmchannel', cfg.dropLabels);
fullLocs = EEG.chanlocs;
% 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', cfg.lineFreq - 2, 'hicutoff', cfg.lineFreq + 2, 'revfilt', 1);
% 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', cfg.eogLabels);
badChans = setdiff({EEG.chanlocs.labels}, {EEG_test.chanlocs.labels}, 'stable');
fprintf('Bad channels: %s\n', strjoin(badChans, ', '));
if ~isempty(badChans)
EEG = pop_select(EEG, 'rmchannel', badChans);
end
if ~isempty(badChans)
EEG = pop_interp(EEG, fullLocs, 'spherical');
end
clear EEG_test
% Average reference, EOG channels excluded
eogIdx = find(ismember({EEG.chanlocs.labels}, cfg.eogLabels));
EEG = pop_reref(EEG, [], 'exclude', eogIdx);
% ICA copy: same data, high-pass at 1 Hz
EEG_main = EEG;
EEG_ica = pop_eegfiltnew(EEG_main, 'locutoff', 1);
EEG_ica = pop_runica(EEG_ica, 'icatype', 'runica', 'extended', 1);
% 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
% Flag components above 90% Muscle or Eye, then remove them
EEG = pop_iclabel(EEG, 'default');
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);
if ~isempty(artifactComps)
EEG = pop_subcomp(EEG, artifactComps, 0);
end
% Low-pass at 30 Hz, postponed until after ICA cleaning
EEG = pop_eegfiltnew(EEG, 'hicutoff', 30);
EEG.setname = [baseName ' clean'];
EEG = pop_saveset(EEG, 'filename', [baseName '_desc-clean_eeg.set'], 'filepath', inDir);
% Epochs, baseline, rejection of epochs beyond the threshold
EEG = pop_epoch(EEG, cfg.epochEvents, cfg.epochWindow, 'epochinfo', 'yes');
EEG = pop_rmbase(EEG, [-200 0]);
EEG = pop_eegthresh(EEG, 1, 1:EEG.nbchan, -cfg.thresholdUv, cfg.thresholdUv, EEG.xmin, EEG.xmax, 0, 0);
if any(EEG.reject.rejthresh)
EEG = pop_rejepoch(EEG, EEG.reject.rejthresh, 0);
end
EEG.setname = [baseName ' epochs'];
EEG = pop_saveset(EEG, 'filename', [baseName '_desc-epochs_eeg.set'], 'filepath', inDir);
res.nBadChans = numel(badChans);
res.nIcRemoved = numel(artifactComps);
res.nEpochs = EEG.trials;
endكيف أكتبها بالكود؟
%% D4 batch pipeline: same steps for every subject, errors logged
eeglab nogui
%% Settings
projectRoot = pwd;
bidsRoot = fullfile(projectRoot, 'rawdata');
derivRoot = fullfile(projectRoot, 'derivatives', 'eeglab');
logRoot = fullfile(projectRoot, 'logs');
subjects = {'sub-002', 'sub-003', 'sub-004'};
cfg.lineFreq = 50; % UK recording: 50 Hz mains
cfg.eogLabels = {'EEG061', 'EEG062'}; % HEOG, VEOG
cfg.dropLabels = {'EEG063', 'EEG064'}; % ECG channels
cfg.epochEvents = {'famous_new', 'unfamiliar_new', 'scrambled_new'};
cfg.epochWindow = [-1 2];
cfg.thresholdUv = 100;
if ~exist(logRoot, 'dir'), mkdir(logRoot); end
logFile = fullfile(logRoot, 'batch_log.txt');
%% Import BIDS data once: events from trial_type, locations from electrodes.tsv
[STUDY, ALLEEG] = pop_importbids(bidsRoot, 'eventtype', 'trial_type', ...
'bidsevent', 'on', 'bidschanloc', 'on', 'subjects', subjects, ...
'outputdir', derivRoot, 'studyName', 'faces');
clear STUDY ALLEEG % each subject is reloaded from disk inside the loop
%% Loop over subjects
nSub = numel(subjects);
status = repmat({'not run'}, nSub, 1);
nBad = nan(nSub, 1);
nIcRem = nan(nSub, 1);
nEpochs = nan(nSub, 1);
for s = 1:nSub
subj = subjects{s};
fprintf('\n===== %s (%d of %d) =====\n', subj, s, nSub);
try
inFile = fullfile(derivRoot, subj, 'eeg', sprintf('%s_task-FaceRecognition_eeg.set', subj));
res = process_subject(inFile, cfg);
status{s} = 'ok';
nBad(s) = res.nBadChans;
nIcRem(s) = res.nIcRemoved;
nEpochs(s) = res.nEpochs;
catch err
status{s} = 'failed';
fid = fopen(logFile, 'a');
fprintf(fid, '%s %s %s\n', datestr(now, 'yyyy-mm-dd HH:MM'), subj, err.message);
fclose(fid);
fprintf(2, 'FAILED %s: %s\n', subj, err.message);
end
end
%% Summary table, one row per subject
summary = table(subjects(:), status, nBad, nIcRem, nEpochs, ...
'VariableNames', {'subject', 'status', 'bad_channels', 'ics_removed', 'epochs_kept'});
disp(summary);
writetable(summary, fullfile(derivRoot, 'batch_summary.csv'));
%% ------------------------------------------------------------------
function res = process_subject(inFile, cfg)
% Single-subject pipeline from stations 3 to 5, with project settings in cfg
[inDir, baseName] = fileparts(inFile);
baseName = strrep(baseName, '_eeg', '');
EEG = pop_loadset('filename', [baseName '_eeg.set'], 'filepath', inDir);
% Remove ECG channels, keep EOG for ICA
EEG = pop_select(EEG, 'rmchannel', cfg.dropLabels);
fullLocs = EEG.chanlocs;
% 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', cfg.lineFreq - 2, 'hicutoff', cfg.lineFreq + 2, 'revfilt', 1);
% 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', cfg.eogLabels);
badChans = setdiff({EEG.chanlocs.labels}, {EEG_test.chanlocs.labels}, 'stable');
fprintf('Bad channels: %s\n', strjoin(badChans, ', '));
if ~isempty(badChans)
EEG = pop_select(EEG, 'rmchannel', badChans);
end
if ~isempty(badChans)
EEG = pop_interp(EEG, fullLocs, 'spherical');
end
clear EEG_test
% Average reference, EOG channels excluded
eogIdx = find(ismember({EEG.chanlocs.labels}, cfg.eogLabels));
EEG = pop_reref(EEG, [], 'exclude', eogIdx);
% ICA copy: same data, high-pass at 1 Hz
EEG_main = EEG;
EEG_ica = pop_eegfiltnew(EEG_main, 'locutoff', 1);
EEG_ica = pop_runica(EEG_ica, 'icatype', 'runica', 'extended', 1);
% 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
% Flag components above 90% Muscle or Eye, then remove them
EEG = pop_iclabel(EEG, 'default');
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);
if ~isempty(artifactComps)
EEG = pop_subcomp(EEG, artifactComps, 0);
end
% Low-pass at 30 Hz, postponed until after ICA cleaning
EEG = pop_eegfiltnew(EEG, 'hicutoff', 30);
EEG.setname = [baseName ' clean'];
EEG = pop_saveset(EEG, 'filename', [baseName '_desc-clean_eeg.set'], 'filepath', inDir);
% Epochs, baseline, rejection of epochs beyond the threshold
EEG = pop_epoch(EEG, cfg.epochEvents, cfg.epochWindow, 'epochinfo', 'yes');
EEG = pop_rmbase(EEG, [-200 0]);
EEG = pop_eegthresh(EEG, 1, 1:EEG.nbchan, -cfg.thresholdUv, cfg.thresholdUv, EEG.xmin, EEG.xmax, 0, 0);
if any(EEG.reject.rejthresh)
EEG = pop_rejepoch(EEG, EEG.reject.rejthresh, 0);
end
EEG.setname = [baseName ' epochs'];
EEG = pop_saveset(EEG, 'filename', [baseName '_desc-epochs_eeg.set'], 'filepath', inDir);
res.nBadChans = numel(badChans);
res.nIcRemoved = numel(artifactComps);
res.nEpochs = EEG.trials;
endأي قيمة أختار؟
القيم المناسبة حسب هدف تحليلك
| الإعداد | القيمة في الدرس | لماذا |
|---|---|---|
| تردد الكهرباء | 50 هرتز | D4 مسجّلة في كامبريدج ببريطانيا، و PowerLineFrequency في ملف eeg.json يساوي 50. بياناتك من السعودية 60. هذا الإعداد هو الفرق الأوضح بين D1 و D4. |
| قنوات العين والقلب | EEG061 و EEG062 للعين، و EEG063 و EEG064 للقلب | من ملف channels.tsv في D4. قنوات القلب تُحذف، وقنوات العين تبقى لتساعد ICA ولا تدخل المرجع. |
| الأحداث للتقطيع | famous_new و unfamiliar_new و scrambled_new | العرض الأول لكل صورة، 150 لكل نوع في sub-002. التكرارات لها أسماء أخرى (_second_early و _second_late). |
| الاستيراد | pop_importbids مرة واحدة قبل الحلقة | يقرأ الأحداث من events.tsv ومواقع الأقطاب من electrodes.tsv، كما في درس EEGLAB الرسمي لنفس البيانات. |
| مكان الإعدادات | المتغيّر cfg في الأعلى | كل ما يختلف بين المشاريع في مكان واحد، والدالة نفسها لا تتغيّر. |
| ما يحدث عند الخطأ | سطر في batch_log.txt، والحالة failed، ثم المشارك التالي | تعرف في الصباح من فشل ولماذا، وتعيد تشغيله وحده. |
أي قيمة أختار؟
| الإعداد | القيمة في الدرس | لماذا |
|---|---|---|
| تردد الكهرباء | 50 هرتز | D4 مسجّلة في كامبريدج ببريطانيا، و PowerLineFrequency في ملف eeg.json يساوي 50. بياناتك من السعودية 60. هذا الإعداد هو الفرق الأوضح بين D1 و D4. |
| قنوات العين والقلب | EEG061 و EEG062 للعين، و EEG063 و EEG064 للقلب | من ملف channels.tsv في D4. قنوات القلب تُحذف، وقنوات العين تبقى لتساعد ICA ولا تدخل المرجع. |
| الأحداث للتقطيع | famous_new و unfamiliar_new و scrambled_new | العرض الأول لكل صورة، 150 لكل نوع في sub-002. التكرارات لها أسماء أخرى (_second_early و _second_late). |
| الاستيراد | pop_importbids مرة واحدة قبل الحلقة | يقرأ الأحداث من events.tsv ومواقع الأقطاب من electrodes.tsv، كما في درس EEGLAB الرسمي لنفس البيانات. |
| مكان الإعدادات | المتغيّر cfg في الأعلى | كل ما يختلف بين المشاريع في مكان واحد، والدالة نفسها لا تتغيّر. |
| ما يحدث عند الخطأ | سطر في batch_log.txt، والحالة failed، ثم المشارك التالي | تعرف في الصباح من فشل ولماذا، وتعيد تشغيله وحده. |
كيف أتأكد أنها نجحت؟
4 فحوص سريعة
- الجدول المطبوع في النهاية فيه ثلاثة صفوف، والحالة
okفي كل صف. logs/batch_log.txtغير موجود أو فارغ. إذا وُجد سطر، فاسم المشارك والسبب فيه.- في مجلد كل مشارك داخل
derivatives/eeglabملفان جديدان:desc-cleanوdesc-epochs. - عدد الـ epochs الباقية لكل مشارك قريب من 450 (150 لكل نوع)، ناقص المرفوضة وأي epoch يقطع حدث
boundary(5 في sub-002)، لأنpop_epochيحذفها. إذا كان أقل بكثير لمشارك واحد، فراجعه.
كيف أتأكد أنها نجحت؟
- الجدول المطبوع في النهاية فيه ثلاثة صفوف، والحالة
okفي كل صف. logs/batch_log.txtغير موجود أو فارغ. إذا وُجد سطر، فاسم المشارك والسبب فيه.- في مجلد كل مشارك داخل
derivatives/eeglabملفان جديدان:desc-cleanوdesc-epochs. - عدد الـ epochs الباقية لكل مشارك قريب من 450 (150 لكل نوع)، ناقص المرفوضة وأي epoch يقطع حدث
boundary(5 في sub-002)، لأنpop_epochيحذفها. إذا كان أقل بكثير لمشارك واحد، فراجعه.
ما الأخطاء التي يقع فيها كثيرون؟
5 أخطاء، وكيف تتجنّبها
- حلقة بلا
tryوcatch. خطأ واحد في مشارك واحد يوقف المعالجة كلها، وتضيع ساعات الليل. catchصامت. التقاط الخطأ بلا تسجيله أسوأ من التوقف: تظن أن كل المشاركين انتهوا. سجّل الاسم والرسالة دائماً، وانظر جدول الملخص.- إعدادات مكتوبة داخل الدالة. إذا كتبت 60 داخل
process_subjectونسيتها، تعالج بيانات بريطانية بفلتر سعودي. كل إعداد فيcfg. - نسيان الفرق بين مجموعات البيانات. قنوات العين في D1 اسمها
EOG1وEOG2، وفي D4EEG061وEEG062. نسخ السكربت بلا مراجعة هذه الأسماء يجعل ICA والمرجع يعاملان قنوات العين كقنوات رأس. - الثقة بالحلقة بلا فحص. ملخص يقول
okلا يعني أن التنظيف جيد. افتح ملفاً أو اثنين من الواجهة، وانظر عدد المكوّنات المحذوفة والـ epochs الباقية لكل مشارك. القيمة الشاذة في الجدول هي المشارك الذي تراجعه أولاً.
ما الأخطاء التي يقع فيها كثيرون؟
- حلقة بلا
tryوcatch. خطأ واحد في مشارك واحد يوقف المعالجة كلها، وتضيع ساعات الليل. catchصامت. التقاط الخطأ بلا تسجيله أسوأ من التوقف: تظن أن كل المشاركين انتهوا. سجّل الاسم والرسالة دائماً، وانظر جدول الملخص.- إعدادات مكتوبة داخل الدالة. إذا كتبت 60 داخل
process_subjectونسيتها، تعالج بيانات بريطانية بفلتر سعودي. كل إعداد فيcfg. - نسيان الفرق بين مجموعات البيانات. قنوات العين في D1 اسمها
EOG1وEOG2، وفي D4EEG061وEEG062. نسخ السكربت بلا مراجعة هذه الأسماء يجعل ICA والمرجع يعاملان قنوات العين كقنوات رأس. - الثقة بالحلقة بلا فحص. ملخص يقول
okلا يعني أن التنظيف جيد. افتح ملفاً أو اثنين من الواجهة، وانظر عدد المكوّنات المحذوفة والـ epochs الباقية لكل مشارك. القيمة الشاذة في الجدول هي المشارك الذي تراجعه أولاً.
متصفحك لا يسمح بحفظ التقدّم. كل الدروس متاحة، لكن علامات الإكمال لن تُحفظ.
تم. حُفظ تقدّمك في هذا المتصفح.تراجعكيف أكتبها في ورقتي؟
قالب Methods: عدّل البيانات والإعدادات والإصدارات لتطابق ما نفّذته
قالب: عدّل البيانات والإعدادات والإصدارات لتطابق ما نفّذته.
Data from [n] participants (ds002718; EEG-BIDS plugin 10.5, EEGLAB 2026.1) were high-pass filtered (passband edge 0.1 Hz; firfilt plugin 2.8), and line noise was attenuated with a band-stop filter (passband edges 48 and 52 Hz). Bad channels were identified with clean_rawdata (clean_rawdata plugin 2.11; flatline 5 s, channel correlation 0.8) and interpolated, 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. 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.كيف أكتبها في ورقتي؟
قالب: عدّل البيانات والإعدادات والإصدارات لتطابق ما نفّذته.
Data from [n] participants (ds002718; EEG-BIDS plugin 10.5, EEGLAB 2026.1) were high-pass filtered (passband edge 0.1 Hz; firfilt plugin 2.8), and line noise was attenuated with a band-stop filter (passband edges 48 and 52 Hz). Bad channels were identified with clean_rawdata (clean_rawdata plugin 2.11; flatline 5 s, channel correlation 0.8) and interpolated, 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. 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.أين أقرأ أكثر؟
3 مراجع: التوثيق الرسمي أولاً
- EEGLAB Tutorialتوثيق10. Group analysis > a. Batch processing
- EEGLAB Tutorialتوثيق11. Scripting > f. BIDS pipeline
- Makoto's preprocessing pipelineBatch processing (§17.4)
مكتوب لـ EEGLAB 2026.1 · 2026-10-28
للتوسّع
- EEGLAB Tutorial: https://eeglab.org/tutorials/10_Group_analysis/multiple_subject_proccessing_overview.html (10. Group analysis > a. Batch processing)
- EEGLAB Tutorial: https://eeglab.org/tutorials/11_Scripting/Analyzing_EEG_BIDS_data_in_EEGLAB.html (11. Scripting > f. BIDS pipeline)
- Makoto's preprocessing pipeline: https://eeglab.ucsd.edu/wiki/Makoto%27s_preprocessing_pipeline (Batch processing (§17.4))
تحتاج مساعدة؟
اسأل، أو شاركنا رأيك، أو بلّغ عن خطأ
- بلّغ عن خطأ علمي · يحتاج حساباً على GitHub. لا تملك حساباً؟ استخدم "شاركنا رأيك".