非官方中文翻译 本页译自 MNE-Python 官方文档: 查看英文原文 ↗。 文档版权归 MNE Developers 所有(BSD-3-Clause)。

使用 LIMO 数据集进行单试次线性回归分析#

这里探索 LIMO 数据集 中包含的数据结构。本示例复现并扩展了 LIMO MEEG 中集成的部分主要分析与工具;LIMO MEEG 是一个最初为对接 EEGLAB 而设计的 MATLAB 工具箱。

概括而言,本示例会:

  • 获取 LIMO 数据集 [1] 中单个受试者的分段数据文件。若本地找不到 LIMO 文件,获取函数 mne.datasets.limo.load_data() 会自动从远程仓库下载。

  • 导入过程中,会从磁盘上的 LIMO .mat 文件中提取数据信息(如采样率、各条件的分段数、每个受试者的 EEG 通道数量与名称等),并作为元数据加入 epochs 结构。

  • 在单个受试者的数据上拟合线性模型,并可视化推断指标,以评估所估计效应的显著性。

# Authors: Jose C. Garcia Alanis <alanis.jcg@gmail.com>
#
# License: BSD-3-Clause
# Copyright the MNE-Python contributors.
import matplotlib.pyplot as plt
import numpy as np

from mne import combine_evoked
from mne.datasets.limo import load_data
from mne.stats import linear_regression
from mne.viz import plot_compare_evokeds, plot_events

print(__doc__)

# subject to use
subj = 1

关于数据#

在原始 LIMO 实验(见 [2])中,被试执行二选一强迫选择任务,对两张面孔刺激进行辨别。整个实验使用同样的两张面孔,并加入不同程度的噪声,使面孔对被试而言更易或更难辨认(类似方法见 [3] 图 1)。

呈现的面孔沿噪声—信号(或相位一致性)连续体变化,范围从 0 到 85%,以 5% 为步长递增。换言之,相位一致性高(如 85%)的面孔容易识别,而相位一致性低(如 5%)的面孔难以识别,也就更难辨别。

加载数据#

首先加载 LIMO 数据集中第 1 名受试者的数据。

# This step can take a little while if you're loading the data for the
# first time.
limo_epochs = load_data(subject=subj)

注意,加载结果是 mne.EpochsArray,其中的数据已可直接与 MNE-Python 对接。

print(limo_epochs)
<EpochsArray | 1055 events (all good), -0.3 – 0.5 s (baseline off), ~213.7 MiB, data loaded, with metadata,
 'Face/A': 524
 'Face/B': 531>

可视化事件#

我们可以可视化 limo_epochs 结构中面孔事件的分布。由于分段按条件排序,事件应呈现清晰的分组。

fig = plot_events(limo_epochs.events, event_id=limo_epochs.event_id)
fig.suptitle("Distribution of events in LIMO epochs")
Distribution of events in LIMO epochs

如上所示,条件编码为 Face/A 与 Face/B。所呈现面孔的相位一致性信息保存在 epochs 元数据中,可通过 limo_epochs.metadata 方便地访问。如下所示,元数据还包含所呈现面孔的信息,便于使用。

  face  phase-coherence
0    A         0.491210
1    A        -1.235572
2    A        -1.043707
3    A         1.642398
4    A        -0.659978

现在进一步查看 epochs 元数据中的信息。

# We want include all columns in the summary table
epochs_summary = limo_epochs.metadata.describe(include="all").round(3)
print(epochs_summary)
        face  phase-coherence
count   1055         1055.000
unique     2              NaN
top        B              NaN
freq     531              NaN
mean     NaN           -0.000
std      NaN            1.000
min      NaN           -1.619
25%      NaN           -0.852
50%      NaN           -0.084
75%      NaN            0.875
max      NaN            1.642

上表第一列提供的信息与之前运行的 print(limo_epochs) 大致相同:共有 1055 个面孔(即分段),分为 2 个条件(Face A 与 Face B);对该受试者而言,Face B 条件的分段更多。

此外,第二列显示相位一致性变量的取值范围为 -1.619 到 1.642。这是因为 LIMO 数据集以 z 分数形式提供相位一致性数值,其均值为 0、标准差为 1。

可视化各条件的 ERP#

绘制 Face A 与 Face B 诱发的 ERP,看看二者有多相似。

# only show -250 to 500 ms
ts_args = dict(xlim=(-0.25, 0.5))

# plot evoked response for face A
limo_epochs["Face/A"].average().plot_joint(
    times=[0.15], title="Evoked response: Face A", ts_args=ts_args
)
# and face B
limo_epochs["Face/B"].average().plot_joint(
    times=[0.15], title="Evoked response: Face B", ts_args=ts_args
)
  • Evoked response: Face A, 0.150 s
  • Evoked response: Face B, 0.150 s
No projector specified for this dataset. Please consider the method self.add_proj.
No projector specified for this dataset. Please consider the method self.add_proj.

也可以计算对比 Face A 与 Face B 的差异波。不过从上面的诱发响应看,这两种面孔刺激之间不应有太大差异。

# Face A minus Face B
difference_wave = combine_evoked(
    [limo_epochs["Face/A"].average(), limo_epochs["Face/B"].average()], weights=[1, -1]
)

# plot difference wave
difference_wave.plot_joint(times=[0.15], title="Difference Face A - Face B")
Difference Face A - Face B, 0.150 s
No projector specified for this dataset. Please consider the method self.add_proj.

正如预期,对比 Face A 与 Face B 时没有出现明显模式。不过还可以进一步缩小范围:由于这是“视觉范式”,最好查看枕叶附近的电极,因为刺激差异(若有)在视觉区更容易观察到。

# Create a dictionary containing the evoked responses
conditions = ["Face/A", "Face/B"]
evokeds = {condition: limo_epochs[condition].average() for condition in conditions}

# concentrate analysis an occipital electrodes (e.g. B11)
pick = evokeds["Face/A"].ch_names.index("B11")

# compare evoked responses
plot_compare_evokeds(evokeds, picks=pick, ylim=dict(eeg=(-15, 7.5)))
B11

我们确实看到 Face A 与 Face B 之间存在差异,但幅度很小。

可视化刺激相位一致性的效应#

由于相位一致性决定面孔刺激是否容易识别,可以预期相位一致性高的面孔会在枕叶电极上诱发更强的激活模式。

phase_coh = limo_epochs.metadata["phase-coherence"]
# get levels of phase coherence
levels = sorted(phase_coh.unique())
# create labels for levels of phase coherence (i.e., 0 - 85%)
labels = [f"{i:.2f}" for i in np.arange(0.0, 0.90, 0.05)]

# create dict of evokeds for each level of phase-coherence
evokeds = {
    label: limo_epochs[phase_coh == level].average()
    for level, label in zip(levels, labels)
}

# pick channel to plot
electrodes = ["C22", "B11"]
# create figures
for electrode in electrodes:
    fig, ax = plt.subplots(figsize=(8, 4))
    plot_compare_evokeds(
        evokeds,
        axes=ax,
        ylim=dict(eeg=(-20, 15)),
        picks=electrode,
        cmap=("Phase coherence", "magma"),
    )
  • C22
  • B11

如上所示,在所选电极上,低相位一致性与高相位一致性刺激诱发的激活模式存在明显差异。

为线性回归分析准备数据#

在用线性回归检验这些差异的显著性之前,我们先对预处理阶段被剔除的缺失通道进行插值;此外,还会剔除数据中以“EXG”为前缀的 EOG 通道:

limo_epochs.interpolate_bads(reset_bads=True)
limo_epochs.drop_channels(["EXG1", "EXG2", "EXG3", "EXG4"])
Setting channel interpolation method to {'eeg': 'spline'}.
Interpolating bad channels.
    Automatic origin fit: head of radius 95.0 mm
Computing interpolation matrix from 113 sensor positions
Interpolating 15 sensors
General
MNE object type EpochsArray
Measurement date Unknown
Participant Unknown
Experimenter Unknown
Acquisition
Total number of events 1055
Events counts Face/A: 524
Face/B: 531
Time range -0.300 – 0.500 s
Baseline off
Sampling frequency 250.05 Hz
Time points 201
Metadata 1055 rows × 2 columns
Channels
EEG
Head & sensor digitization 131 points
Filters
Highpass 0.00 Hz
Lowpass 125.02 Hz


定义预测变量与设计矩阵#

运行回归分析需要构建设计矩阵,其中包含用于预测脑活动模式的变量(即预测变量)。为此,我们使用 limo_epochs.metadata 中的信息:相位一致性以及 Face A 与 Face B 的对比。

# name of predictors + intercept
predictor_vars = ["face a - face b", "phase-coherence", "intercept"]

# create design matrix
design = limo_epochs.metadata[["phase-coherence", "face"]].copy()
design["face a - face b"] = np.where(design["face"] == "A", 1, -1)
design["intercept"] = 1
design = design[predictor_vars]

现在可以使用 MNE-Python 的 linear_regression() 函数建立分析所用的线性模型。

Fitting linear model to epochs, (25728 targets, 3 regressors)
Done

提取回归系数#

结果保存在对象 reg 中;它是一个由诱发对象组成的字典,为设计矩阵中的每个预测变量提供多种推断指标。

print("predictors are:", list(reg))
print("fields are:", [field for field in getattr(reg["intercept"], "_fields")])
predictors are: ['face a - face b', 'phase-coherence', 'intercept']
fields are: ['beta', 'stderr', 't_val', 'p_val', 'mlog10_p_val']

绘制模型结果#

现在可以通过 reg['<name of predictor>'].<measure of interest> 访问并绘制线性回归结果,并像使用其他诱发对象一样调用 plot_joint()。下图可见明显的相位一致性效应:相位一致性越高(即“面孔可见性”越好),刺激后约 200 至 250 ms 枕叶电极测得的活动越呈负向变化。

reg["phase-coherence"].beta.plot_joint(
    ts_args=ts_args, title="Effect of Phase-coherence", times=[0.23]
)
Effect of Phase-coherence, 0.230 s
No projector specified for this dataset. Please consider the method self.add_proj.

也可以绘制相应的 T 值。

# use unit=False and scale=1 to keep values at their original
# scale (i.e., avoid conversion to micro-volt).
ts_args = dict(xlim=(-0.25, 0.5), unit=False)
topomap_args = dict(scalings=dict(eeg=1), average=0.05)

fig = reg["phase-coherence"].t_val.plot_joint(
    ts_args=ts_args, topomap_args=topomap_args, times=[0.23]
)
fig.axes[0].set_ylabel("T-value")
EEG (128 channels), 0.208 – 0.256 s
No projector specified for this dataset. Please consider the method self.add_proj.

相反,比较 Face A 与 Face B 刺激时似乎没有(或仅有很小的)系统性效应,这与前文的差异波分析基本一致。

ts_args = dict(xlim=(-0.25, 0.5))

reg["face a - face b"].beta.plot_joint(
    ts_args=ts_args, title="Effect of Face A vs. Face B", times=[0.23]
)
Effect of Face A vs. Face B, 0.230 s
No projector specified for this dataset. Please consider the method self.add_proj.

参考文献#

脚本总运行时间:(0 分 6.044 秒)

图库由 Sphinx-Gallery 生成