EEG代码实践:数据集特征提取方法一览(以SEED为例)

本文介绍了如何使用SJTUSEED数据集进行脑电信号处理,包括频谱特征(如delta,theta,alpha,sigma,beta频段能量)、功率谱密度、相干性分析(如相关性和相干性矩阵)以及时域特征(如均值、方差等),通过mne库实现特征提取和预处理流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2023/8/12 -  2023/8/15 脑机接口学习内容一览:

        本文主要针对上海交通大学的SJTU(SEED)数据集,在前文SEED数据集批量读取的基础上进行闭门造车的特征提取,旨在之后通过更加标准的流程实现分类,顺便对以前学到的东西做一下梳理。


        tips.文章中代码包含库如下:

import mne
import numpy as np
# 引入前文中构建的文件读取函数
from read_file import read_all_files

1. 频谱特征

        操作主要是将EEG信号转换为频谱,提取不同频带的能量,如"delta": [0.5, 4.5], "theta": [4.5, 8.5], "alpha": [8.5, 11.5], "sigma": [11.5, 15.5], "beta": [15.5, 30]等。

        提取的频谱特征矩阵shape如下:

read_all_files start...
共读取了1个文件
共有15段数据
read ended...
频谱特征矩阵的shape为:(15, 310)

        呈现出这个shape很好理解,这里我们选取了5个频段,每个raw数据有62个通道,每个通道的数据都能计算一次频段能量,因此得到数据5*62=310个,代码如下:

def frequency_spectrum(raws):
    # 提取EEG数据在五个频段的能量特征
    # delta(0.5-4Hz) theta(4-8Hz) alpha(8-13Hz) beta(13-30Hz) gamma(30-100Hz)
    # 特定频带
    FREQ_BANDS = {"delta": [0.5, 4.5],
                  "theta": [4.5, 8.5],
                  "alpha": [8.5, 11.5],
                  "sigma": [11.5, 15.5],
                  "beta": [15.5, 30]}
    # 特征矩阵
    feature_matrix = []
    # 遍历每个raw
    for raw in raws:
        # 生成频谱特征向量
        feature_vector = []
        # 遍历每个频段
        for band in FREQ_BANDS:
            # 提取每个频段的数据,不打印信息
            raw_band = raw.copy().filter(l_freq=FREQ_BANDS[band][0], h_freq=FREQ_BANDS[band][1], verbose=False)
            # 计算能量
            power = np.sum(raw_band.get_data() ** 2, axis=1) / raw_band.n_times
            # 添加到特征向量
            feature_vector.extend(power)
        # 添加到特征矩阵
        feature_matrix.append(feature_vector)
    # 返回特征矩阵
    print("频谱特征矩阵的shape为:{}".format(np.array(feature_matrix).shape))
    # print("频谱特征矩阵内容为:{}".format(np.array(feature_matrix)))
    return np.array(feature_matrix)

raws, label = read_all_files("Preprocessed_EEG/", 1)
frequency_spectrum(raws)

2. 功率谱密度

        我在实验中最常用的特征,计算每个频带的功率,反映在不同频率范围内的能量分布情况。其实这个特征和上面的频谱能量很类似,但是关于计算功率谱密度的函数,因为使用了加窗和fft方法,有几个新手经常疑惑的点需要注意,当时我查了大量资料才差不多搞明白。

        在计算功率谱的时候主要用到了mne中的一个函数compute_psd,这个函数的详细解释和加窗这块的知识在往期博文中可见,即mne库函数compute_psd()记录 。

        计算功率谱密度的结果如下:

read_all_files start...
共读取了1个文件
共有15段数据
read ended...
功率谱密度特征提取开始...
功率谱密度特征矩阵的shape为:(15, 310)
功率谱密度特征提取结束

        之前我计算功率谱的时候用的是比较短的epoch(不多于30s),而这次使用的数据段较长,不知道会有什么影响。两种情况的区别在于epochs是一个raw中的不同时间段,如果以时间为轴依次生成epoch,再对生成的epochs进行功率谱计算,能够得到一个以时间序列排序的功率谱数据,可以反映各个频段随时间的变化。但是以下代码仅对一整段raw计算:

def power_spectrum(raws):
    print("功率谱密度特征提取开始...")
    # 计算每个频段的功率谱密度
    # 特定频带
    FREQ_BANDS = {"delta": [0.5, 4.5],
                  "theta": [4.5, 8.5],
                  "alpha": [8.5, 11.5],
                  "sigma": [11.5, 15.5],
                  "beta": [15.5, 30]}
    # 特征矩阵
    feature_matrix = []
    # 遍历每个raw
    for raw in raws:
        # 生成频谱特征向量
        feature_vector = []
        # 遍历每个频段
        for band in FREQ_BANDS:
            # 计算功率谱密度
            power = raw.compute_psd(picks='all', method='welch', fmin=FREQ_BANDS[band][0],
                                           fmax=FREQ_BANDS[band][1], verbose=False)
            # print(power.shape)
            # 添加到特征向量,在第二个维度方向扩展
            for i in range(power.shape[0]):
                feature_vector.extend(power[i])
        # 添加到特征矩阵
        # print(len(feature_vector))
        feature_matrix.append(feature_vector)
    # 将特征矩阵转换为numpy数组
    feature_matrix = np.array(feature_matrix, dtype=object)
    # 返回特征矩阵
    print("功率谱密度特征矩阵的shape为:{}".format(feature_matrix.shape))
    # print("功率谱密度特征矩阵内容为:{}".format(np.array(feature_matrix)))
    print("功率谱密度特征提取结束")
    return feature_matrix


raws, label = read_all_files("Preprocessed_EEG/", 1)
# frequency_spectrum(raws)
power_spectrum(raws)

3. 连接性

        分析不同脑区域之间的相互关系,如相关性、相干性、互信息等。这部分的输出可以在图神经网络中应用。在mne的新版本中,与这块相关的函数已经另开了一个包,即mne_connectivity,需要另外install。这个包好像比较新,网上没有对应的内容,需要自己看文档。

        这里贴一下相干性和相关性的概念,其实我自己也不是很清楚这些:

        相干性(coherence )和相关性(correlation) 有什么区别和联系?

        下面代码使用了 mne_connectivity.spectral_connectivity_epochs 函数,按翻译来看计算的是频域和时频域的连接性。在设定只读取单个raw之后,使用该函数进行计算,得到了如下的输出:

共读取了1个文件
共有15段数据
read ended...
相干性特征提取开始...
event.shape:  (127, 3)
Using data from preloaded Raw for 127 events and 401 original time points ...
0 bad epochs dropped
epochs.shape:  (127, 62, 401)
con info:  <SpectralConnectivity | freq : [2.493766, 99.750623], , nave : 127, nodes, n_estimated : 62, 3844, ~5.8 MB>
相干性特征矩阵的shape为:(1, 3844, 196)
相干性特征提取结束

         以上可知,计算一段raw数据得到的相干性特征矩阵的维度是(3844,196),3844易知为62*62,即通道数的平方,而196则与设定计算的频域范围有关。

        更具体地说,频率分析在频率范围内进行,每个频率点会产生一组连接性值,而时间分析则是在一系列时间点上进行,因此这两个维度的组合构成了相干性特征矩阵的形状。

  • 频率维度:这是从频谱分析中获得的。假设你的频率范围是 [2.493766, 99.750623] Hz,你可能在这个频率范围内进行了若干个频率点的计算,每个频率点都计算出了一组相干性值。这些值被组织成了频率维度。

  • 时间维度:这是从你的数据时间序列中获得的。你可能在每个时间点上计算了一组相干性值,这些时间点的数量由你的原始数据的时间点数量决定。

        因此,196 表示了频率和时间点的组合,指的是在频率范围内的每个频率点上,你在一系列时间点上计算了相干性值,这些值被组织成了 196 维的时间维度。这个组合反映了你在频率和时间领域内进行的相干性分析,以便更好地理解信号的变化和交互。说实话不是很好理解,但是这个数据量较大,可能不太好处理,而且代码提取特征花费的时间也比较长久,如果能知道自己的任务需要用到哪个特定频域范围的话可以设置范围小一点的 fmin 和 fmax 参数来让计算速度快一点(提取一个文件中的15段raw数据计算特征就花了近一两个分钟左右)。

        代码如下:

def spectral_connectivity(raws):
    print("相干性特征提取开始...")
    # 计算EEG数据的相干性特征
    # 特征矩阵
    feature_matrix = []
    # 遍历每个raw
    for raw in raws:
        # 生成events, 2s为一个event,重叠0.2s
        events = mne.make_fixed_length_events(raw, duration=2.0, overlap=0.2)
        # print('event.shape: ', events.shape)
        # 生成epochs
        epochs = mne.Epochs(raw, events, tmin=0, tmax=2.0, baseline=None, verbose=False)
        # print('epochs.shape: ', epochs.get_data().shape)
        # 计算相干性矩阵
        con = spectral_connectivity_epochs(epochs, method='coh', mode='multitaper', verbose=False)
        # print('con info: ', con)
        feature_matrix.append(con.get_data())
    # 将特征矩阵转换为numpy数组
    feature_matrix = np.array(feature_matrix, dtype=object)
    # 返回特征矩阵
    print("相干性特征矩阵的shape为:{}".format(feature_matrix.shape))
    # print("相干性特征矩阵内容为:{}".format(np.array(feature_matrix)))
    print("相干性特征提取结束")
    return feature_matrix


raws, label = read_all_files("Preprocessed_EEG/", 1)
spectral_connectivity(raws)

 4. 时域特征

        这里提取的时域特征为均值、方差、标准差、最大最小值与差、斜率、偏度、峰度以及能量等,很简单。输出信息如下,其中15为raw个数,682 = 62(通道数) * 11(特征数)。

read_all_files start...
共读取了1个文件
共有15段数据
read ended...
时域特征提取开始...
时域特征矩阵的shape为:(15, 682)

        代码:

def temporal_feature(raws):
    # 提取EEG数据的时域特征
    print("时域特征提取开始...")
    # 特征矩阵
    feature_matrix = []
    # 遍历每个raw
    for raw in raws:
        # 生成时域特征向量
        feature_vector = []
        # 计算EEG数据的时域特征
        # 1.均值
        mean = raw.get_data().mean(axis=1)
        # 2.方差
        var = raw.get_data().var(axis=1)
        # 3.标准差
        std = raw.get_data().std(axis=1)
        # 4.最大值
        max = raw.get_data().max(axis=1)
        # 5.最小值
        min = raw.get_data().min(axis=1)
        # 6.峰峰值
        p_p = max - min
        # 7.斜率
        slope = np.diff(raw.get_data(), axis=1)
        # 8.峭度
        kurtosis = np.mean((raw.get_data() - mean[:, None]) ** 4, axis=1) / var ** 2 - 3
        # 9.偏度
        skewness = np.mean((raw.get_data() - mean[:, None]) ** 3, axis=1) / var ** (3 / 2)
        # 10.能量
        power = np.sum(raw.get_data() ** 2, axis=1) / raw.n_times
        # 11.方根幅值
        rms = np.sqrt(np.mean(raw.get_data() ** 2, axis=1))
        # 12.脑电活动时域特征向量
        feature_vector.extend(mean)
        feature_vector.extend(var)
        feature_vector.extend(std)
        feature_vector.extend(max)
        feature_vector.extend(min)
        feature_vector.extend(p_p)
        feature_vector.extend(slope)
        feature_vector.extend(kurtosis)
        feature_vector.extend(skewness)
        feature_vector.extend(power)
        feature_vector.extend(rms)
        # 添加到特征矩阵
        feature_matrix.append(feature_vector)
    # 将特征矩阵转换为numpy数组
    feature_matrix = np.array(feature_matrix, dtype=object)
    # 返回特征矩阵
    print("时域特征矩阵的shape为:{}".format(feature_matrix.shape))
    # print("时域特征矩阵内容为:{}".format(np.array(feature_matrix)))
    return feature_matrix

### 使用Matlab进行EEG信号特征提取 #### EEG信号预处理 为了有效提取EEG信号中的有用信息,在特征提取之前通常需要对原始数据进行预处理。这一步骤主要包括去除噪声和其他伪迹,如肌电干扰、眼动伪影等。 ```matlab % 去除线性趋势并应用带通滤波器 data_detrended = detrend(data); [b,a] = butter(4,[0.5 30]/(fs/2),'bandpass'); filtered_data = filtfilt(b,a,data_detrended); ``` 此过程可以显著提高后续分析的质量和准确性[^1]。 #### 特征提取方法概述 常见的EEG特征提取技术包括但不限于: - **频域特征**:通过傅里叶变换或其他谱估计方法计算功率谱密度。 - **时域统计量**:均值、方差、峰度等简单描述符可用于捕捉时间序列特性。 - **非线性动力学指标**:如分形维数、Lyapunov指数能够反映复杂系统的内在结构。 这些不同类型的特征可以从多个角度揭示大脑活动模式的变化规律。 #### 实际操作指南 下面给出一段简单的代码片段来展示如何利用MATLAB内置函数实现基本的时间频率表示——短时傅立叶变换(STFT),这是研究瞬态事件非常有用的工具之一。 ```matlab windowSize = round(fs*0.5); % 半秒窗口大小 noverlap = floor(windowSize * 0.75); % 重叠比设为75% [S,F,T,P] = spectrogram(filtered_data,hamming(windowSize),... noverlap,[],fs,'yaxis'); imagesc(T,F,log(P)); xlabel('Time (s)') ylabel('Frequency (Hz)') title('Spectrogram of Filtered EEG Signal') colorbar; ``` 上述程序绘制了经过滤波后的EEG信号随时间和频率变化的能量分布图,有助于直观理解信号内部结构及其动态演变过程。
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值