Quality control#
See also
Acquisition quality control runs the checks on real data and shows their figures.
meeg_utils.qc answers how well was this recording made?, so that
problems can be fixed during the session or reported to whoever acquired
the data. Checks never modify the data.
import meeg_utils as meu
report = meu.qc.inspect(raw) # every check that applies to this recording
print(report) # verdicts and the findings that need attention
report.flags # findings that are "warn" or "fail", worst first
report.skipped # checks that did not apply, with the reason
figs = report.plot() # {check: {kind: Figure}}
Each check is a small class: parameters (thresholds included) are set in
the constructor, compute(raw) measures, and the results are in
metrics_ and findings_ (one Finding per
criterion, with a level and a message that states the threshold).
Verdicts and measurements#
Only some criteria have a threshold that holds for every dataset. The checks distinguish three kinds:
Problems by definition are judged (
"warn"or"fail") by default: flat, clipped or NaN channels, SQUID jumps, bridged electrodes (and groups too large to interpolate), a flat EOG, unreliable heartbeat detection on an ECG channel, missing positions or head transform, event counts that differ fromexpected, BIDS sidecars that disagree with the data.Comparisons within the recording are judged with the method’s own default: outlying channels (LOF), channels with excess line noise (robust z), muscle and jump z-scores, narrowband peaks.
Measurements whose acceptable range depends on the dataset are only reported, with level
"info"and a commonly used value in the message: head movement, share of time with muscle activity, blink rate, heart rate, electrode impedance, cHPI coil SNR, days between the empty room and the recording. Children move more than adults, trained people have slow hearts, passive electrodes have higher impedances than active ones.
"info" findings are not flags and do not raise the level of a report.
Two ways to judge them:
# 1. your lab's limits, for your population and equipment
meu.qc.inspect(raw, checks=[
meu.qc.Impedance(warn_kohm=10), # passive electrodes
meu.qc.HeadMovement(warn_mm=5),
meu.qc.Events(expected={"stimulus": 240}),
])
# 2. relative to the rest of the dataset: recordings that stand out
meu.qc.inspect_dataset("bids/").outliers # modified z > 3.5 on any metric
Checks#
Every default comes from MNE’s defaults or the literature, and is documented with its source in the class docstring; where no standard exists, the docstring says so and the default is to report only.
Check |
What it finds |
Figures |
|---|---|---|
flat channels, dropouts, clipping (amplifier saturation), NaN samples |
|
|
EEG electrodes shorted by gel (MNE’s electrical-distance method) |
|
|
electrodes above an impedance limit (BrainVision header or given values) |
|
|
channels unlike the others (Local Outlier Factor), any system |
|
|
line noise, other narrowband noise sources, channels with poor contact |
|
|
share of time with muscle activity |
|
|
blink rate, i.e. whether the EOG works |
|
|
heart rate, i.e. whether the ECG works |
|
|
MEG head movement (cHPI or a |
|
|
flux jumps of SQUID sensors (FieldTrip’s jump detection) |
|
|
HPI coils much weaker than the others (Neuromag) |
|
|
sensor noise floor and noisy sensors in the session’s empty-room recording (found in BIDS), empty room from another day |
|
|
missing electrode positions, implausible head shape, unmeasured head position, head-to-sensor distance |
|
|
event counts against what was expected, duplicated triggers |
|
|
BIDS sidecar disagreeing with the data or |
||
|
delay and jitter between triggers and the photodiode, stimuli not
shown: |
|
Bridged electrodes can also be repaired during preprocessing with
meeg_utils.steps.BridgedElectrodes.
A whole dataset#
result = meu.qc.inspect_dataset("bids/", n_jobs=8) # or a list of recordings
result.outliers # recordings that stand out on a metric (modified z > 3.5)
result.to_csv("qc/summary.csv")
result.plot()["levels"] # recordings x checks overview
Reports#
meeg_utils.report.build() collects QC findings and figures, and the
metrics and figures of every step of a fitted pipeline, in an
mne.Report:
rep = meu.report.build(pipe, inst=raw, qc=meu.qc.inspect(raw))
rep.save("sub-01_report.html")