步骤 05:单位校正
校正通道物理单位(如 V vs μV)
1. 目标
确保所有通道使用统一的物理单位。MNE-Python 的所有 IO 读取函数在加载时默认把数据换算成 SI 基本单位 V(伏特),EEG 因此以 V 而不是 µV 存储。mne.preprocessing.fix_channel_units 并不存在——本步骤给出真正可行的单位校正做法。
2. 何时使用 / 何时跳过
- 必须使用:当文件头记录的通道单位与实际不符,且 IO 函数未能正确换算时
- 可跳过:使用 MNE 内置 IO 函数(
read_raw_edf/read_raw_fif/read_raw_brainvision)后单位已正确换算到 V
3. 前置条件
- 已执行步骤 04:通道列表已确定
4. 核心代码
Python
import mne
import numpy as np
# ====== 1) 查看当前单位(诊断单位异常) ======
# MNE 中所有单位都是 mne.io.constants.FIFF_UNIT_* 之一
from collections import Counter
units = Counter([ch['unit'] for ch in raw.info['chs']])
print(f"通道单位分布: {dict(units)}")
# 期望:{'107': N} (107 即 FIFF_UNIT_V,V 伏特)
for ch in raw.info['chs'][:5]:
print(ch['ch_name'], ch['unit'], ch['range'], ch['cal'])
# ====== 2) 数据值是 V,但文件头标记为 µV,需要 × 1e6 反向校准 ======
# 这种情况表现为:raw.plot() 看不到任何信号(数据被压扁到几乎 0)
# 诊断:data.max() ~ 1e-5 而不是 1e-11(数量级异常)
data, _ = raw[0, :1000]
amp = data.max() - data.min()
print(f"振幅: {amp:.2e} V")
# 期望:1e-7 ~ 1e-3(EEG 量级)
# 如果是 ~1e-12 ~ 1e-10,说明单位换算时多乘了 1e6
if amp < 1e-8: # 异常小
raw.apply_function(lambda x: x * 1e6, picks='eeg')
print("已施加 × 1e6 校正")
# ====== 3) 反向:数据是 V,但文件头期望 µV,需要 ÷ 1e6 ======
elif amp > 1e-2: # 异常大
raw.apply_function(lambda x: x * 1e-6, picks='eeg')
print("已施加 × 1e-6 校正")
# ====== 4) 验证:单位现在应一致 ======
print("校准后单位分布:")
units_after = Counter([ch['unit'] for ch in raw.info['chs']])
print(dict(units_after))
⚠️ 关键事实:MNE 的 IO 函数(
read_raw_*)在加载时已把数据换算到 SI 基本单位 V。所以正常情况下不需要任何单位校正——直接raw.plot()即可观察。
5. 单位换算规则速查
| 场景 | 现象 | 处理 |
|---|---|---|
| IO 加载后数据正常 | data.max() ~ 1e-4 |
✅ 无需处理 |
| 数据被压扁到接近 0 | data.max() ~ 1e-12 |
apply_function(lambda x: x*1e6) |
| 数据异常大 | data.max() ~ 1e2 |
apply_function(lambda x: x*1e-6) |
📌 口诀:1 µV = 1e-6 V;从 µV 改 V 乘 1e-6;从 V 改 µV 乘 1e6。
6. 验证方法
Python
import numpy as np
# 验证 1:振幅数量级
data, _ = raw[:5, :int(raw.info['sfreq'])] # 前 5 通道、前 1 秒(必须 int 切片)
amp_max = np.abs(data).max()
print(f"EEG 振幅峰值: {amp_max:.2e} V")
# 期望:1e-7 ~ 1e-4
# 验证 2:可视化
raw.plot(duration=5, scalings=dict(eeg=20e-6))
# 期望:能用 µV 单位直观看到正常 ERP
# 验证 3:与设备规格对照
# 检查 raw.info['chs'][i]['range'] 单位
for ch in raw.info['chs'][:3]:
print(f"{ch['ch_name']}: range={ch['range']:.2e} V, cal={ch['cal']:.2e}")
7. 常见错误
| 错误 | 原因 | 解决方案 |
|---|---|---|
找不到 mne.preprocessing.fix_channel_units |
该函数不存在(从未在 mne.preprocessing 中出现过) |
用 apply_function 手动校正 |
调用 apply_function 后单位字段不变 |
apply_function 不修改 ch['unit'],仅缩放数据 |
数据正确即可;单位字段仅是元数据 |
raw.plot() 看不到 ERP |
数据被压扁 | 用诊断代码判断后 × 1e6 |
raw.plot() ERP 振幅极大 |
数据被放大 | 用诊断代码判断后 × 1e-6 |
| 拒绝阈值设置错 | 用了错误单位 | reject=dict(eeg=150e-6) 始终基于 V |
8. 官方来源
| 资源类型 | 链接 |
|---|---|
Raw.apply_function |
https://mne.tools/stable/generated/mne.io.Raw.html#mne.io.Raw.apply_function |
| 单位常量 | https://mne.tools/stable/python_reference.html |
| 单位换算说明 | https://mne.tools/stable/overview/implementation.html |
9. 学术引用
无需单独引用。单位规范属于实验数据元数据规范。