【脑电数据处理】小波变换

转载于知乎
参考

1、连续小波变换

  连续小波变换(CWT)叫做连续小波或者分析小波,其中ϕ叫做基本小波或者母小波,a,b均为实数,分别称为尺度和平移因子。



  连续小波{ϕa,b}中,a称为尺度,是表征频率的参数,b是表征时间或空间位置的参数。它的时、频域窗口中心及宽度均随尺度a的变化而伸缩,而连续小波基函数的窗口面积不随参数a,b而变。尺度的倒数1/a对应于频率ω,即尺度越小,对应频率越高,a尺度越大,对应频率越低。假设将尺度理解为时间窗口,则大尺度信号为长时间信号,小尺度信号为短时间信号。

窗口宽度为:

  通常称这个比值称为带通滤波器的品质因数。

  由以上分析可知,小波基函数ϕa,b(t)作为带通滤波器,其品质因数不随尺度a变化,是一组频率特性等Q的带通滤波器组。当尺度增加时,时间窗变宽,频率窗变窄,这时适合于提取多成份信号的低频成份;当尺度减小时,时间窗变窄,频率窗变宽,此时就适合提取多成份信号的高频成份。图 2.1 给出小波变换的时间-频率窗的示意图:

  在实际应用中,以计算机为中心的测量控制系统所能处理的信号是离散信号,另外,对于f(t)的连续小波变换,不但它的系数(Wϕf)(a,b)的信息量是冗余的,而且在计算量大。离散小波变换是将尺度和位移进行离散取值,在不丢失信息的情况下减少小波基的数量,符合实际情况,同时能较好的减少计算量。

  这里使用连续小波变换进行时频域分析,数据只是示例,代码中的参数在实际应用的时候需要根据实际情况进行调整。代码中有关小波尺度的计算很有意思,这里单独拿出来详细说明下。

  一般用小波的尺度来衡量小波的频率,两者之间的转换关系为:

scale*f=Fs*wcf

  其中Fs为采样频率,wcf为小波的中心频率。

我们以下文代码中的参数为例,当一共想要分析totalscal个频率的时候,第i个频率fi对应的是

带入上面的等式中

该部分对应的代码如下:

def TimeFrequencyWP(data, fs, wavelet, maxlevel = 8):
    # 小波包变换这里的采样频率为250,如果maxlevel太小部分波段分析不到
    wp = pywt.WaveletPacket(data=data, wavelet=wavelet, mode='symmetric', maxlevel=maxlevel)
    # 频谱由低到高的对应关系,这里需要注意小波变换的频带排列默认并不是顺序排列,所以这里需要使用’freq‘排序。
    freqTree = [node.path for node in wp.get_level(maxlevel, 'freq')]
    # 计算maxlevel最小频段的带宽
    freqBand = fs/(2**maxlevel)
    #######################根据实际情况计算频谱对应关系,这里要注意系数的顺序
    # 绘图显示
    fig, axes = plt.subplots(len(iter_freqs)+1, 1, figsize=(10, 7), sharex=True, sharey=False)
    # 绘制原始数据
    axes[0].plot(data)
    axes[0].set_title('原始数据')
    for iter in range(len(iter_freqs)):
        # 构造空的小波包
        new_wp = pywt.WaveletPacket(data=None, wavelet=wavelet, mode='symmetric', maxlevel=maxlevel)
        for i in range(len(freqTree)):
            # 第i个频段的最小频率
            bandMin = i * freqBand
            # 第i个频段的最大频率
            bandMax = bandMin + freqBand
            # 判断第i个频段是否在要分析的范围内
            if (iter_freqs[iter]['fmin']<=bandMin and iter_freqs[iter]['fmax']>= bandMax):
                # 给新构造的小波包参数赋值
                new_wp[freqTree[i]] = wp[freqTree[i]].data
        # 绘制对应频率的数据
        axes[iter+1].plot(new_wp.reconstruct(update=True))
        # 设置图名
        axes[iter+1].set_title(iter_freqs[iter]['name'])
    plt.show()

if __name__ == "__main__":
	epochsCom = mne.read_epochs(r'epoch_raw.fif')
    #三层list get_data()[0][i]其中i是通道口
    print(epochsCom[0].get_data()[0])
    dataCom = epochsCom[0].get_data()[0][0]
  	TimeFrequencyCWT(dataCom,fs=250,totalscal=10,wavelet='cgau8')

2、小波包分解重构

  小波包分解可以同时分析低频和高频部分的数据,分析结果的频率分辨率和小波树的深度有关,在使用的时候建议输入的数据个数最好为2的次方(如果不是2的次方重构后数据点数可能会和原始数据不同)。小波树节点的排序非常重要需要注意,在默认情况下并非由低频向高频排列。以深度为3的小波树为例,默认的节点排序为:[‘aaa’, ‘aad’, ‘ada’, ‘add’, ‘daa’, ‘dad’, ‘dda’, ‘ddd’],但是其对应频率由低到高的排序应为:[‘aaa’, ‘aad’, ‘add’, ‘ada’, ‘dda’, ‘ddd’, ‘dad’, ‘daa’]。程序中使用了一种判断方法,可以判断分析目标频率对应的小波参数,不足之处就是如果分析的目标频率的范围较低,受小波分析的分辨率限制,小波树的深度就要加深。具体小波深度的选取要结合采样频率,数据点数,分析目标频率的范围等因素综合考虑来确定。

对应的示例代码如下:

import numpy as np
import matplotlib.pyplot as plt
import pywt
import mne
 
# 需要分析的四个频段
iter_freqs = [
    {'name': 'Delta', 'fmin': 0, 'fmax': 4},
    {'name': 'Theta', 'fmin': 4, 'fmax': 8},
    {'name': 'Alpha', 'fmin': 8, 'fmax': 13},
    {'name': 'Beta', 'fmin': 13, 'fmax': 35},
]
 
plt.rcParams['font.sans-serif'] = ['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False #用来正常显示负号
mne.set_log_level(False)
########################################小波包变换-重构造分析不同频段的特征(注意maxlevel,如果太小可能会导致)#########################
def TimeFrequencyWP(data, fs, wavelet, maxlevel = 8):
    # 小波包变换这里的采样频率为250,如果maxlevel太小部分波段分析不到
    wp = pywt.WaveletPacket(data=data, wavelet=wavelet, mode='symmetric', maxlevel=maxlevel)
    # 频谱由低到高的对应关系,这里需要注意小波变换的频带排列默认并不是顺序排列,所以这里需要使用’freq‘排序。
    freqTree = [node.path for node in wp.get_level(maxlevel, 'freq')]
    # 计算maxlevel最小频段的带宽
    freqBand = fs/(2**maxlevel)
    #######################根据实际情况计算频谱对应关系,这里要注意系数的顺序
    # 绘图显示
    fig, axes = plt.subplots(len(iter_freqs)+1, 1, figsize=(10, 7), sharex=True, sharey=False)
    # 绘制原始数据
    axes[0].plot(data)
    axes[0].set_title('原始数据')
    for iter in range(len(iter_freqs)):
        # 构造空的小波包
        new_wp = pywt.WaveletPacket(data=None, wavelet=wavelet, mode='symmetric', maxlevel=maxlevel)
        for i in range(len(freqTree)):
            # 第i个频段的最小频率
            bandMin = i * freqBand
            # 第i个频段的最大频率
            bandMax = bandMin + freqBand
            # 判断第i个频段是否在要分析的范围内
            if (iter_freqs[iter]['fmin']<=bandMin and iter_freqs[iter]['fmax']>= bandMax):
                # 给新构造的小波包参数赋值
                new_wp[freqTree[i]] = wp[freqTree[i]].data
        # 绘制对应频率的数据
        axes[iter+1].plot(new_wp.reconstruct(update=True))
        # 设置图名
        axes[iter+1].set_title(iter_freqs[iter]['name'])
    plt.show()
 
 
if __name__ == '__main__':
    # 读取筛选好的epoch数据
    epochsCom = mne.read_epochs(r'epoch_raw.fif')
    dataCom = epochsCom[0].get_data()[0][0]
    TimeFrequencyWP(dataCom,250,wavelet='db4', maxlevel=8)

3、基于小波包分解计算不同频段的能量和

这里利用小波包的方法计算区间能量的累加和。

import numpy as np
import matplotlib.pyplot as plt
import pywt
import mne
 
# 需要分析的四个频段
iter_freqs = [
    {'name': 'Delta', 'fmin': 0, 'fmax': 4},
    {'name': 'Theta', 'fmin': 4, 'fmax': 8},
    {'name': 'Alpha', 'fmin': 8, 'fmax': 13},
    {'name': 'Beta', 'fmin': 13, 'fmax': 35},
]
 
plt.rcParams['font.sans-serif'] = ['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False #用来正常显示负号
mne.set_log_level(False)
#############################################小波包计算四个频段的能量分布
def WPEnergy(data, fs, wavelet, maxlevel=6):
    # 小波包分解
    wp = pywt.WaveletPacket(data=data, wavelet=wavelet, mode='symmetric', maxlevel=maxlevel)
    # 频谱由低到高的对应关系,这里需要注意小波变换的频带排列默认并不是顺序排列,所以这里需要使用’freq‘排序。
    freqTree = [node.path for node in wp.get_level(maxlevel, 'freq')]
    # 计算maxlevel最小频段的带宽
    freqBand = fs / (2 ** maxlevel)
    # 定义能量数组
    energy = []
    # 循环遍历计算四个频段对应的能量
    for iter in range(len(iter_freqs)):
        iterEnergy = 0.0
        for i in range(len(freqTree)):
            # 第i个频段的最小频率
            bandMin = i * freqBand
            # 第i个频段的最大频率
            bandMax = bandMin + freqBand
            # 判断第i个频段是否在要分析的范围内
            if (iter_freqs[iter]['fmin'] <= bandMin and iter_freqs[iter]['fmax'] >= bandMax):
                # 计算对应频段的累加和
                iterEnergy += pow(np.linalg.norm(wp[freqTree[i]].data, ord=None), 2)
        # 保存四个频段对应的能量和
        energy.append(iterEnergy)
    # 绘制能量分布图
    plt.plot([xLabel['name'] for xLabel in iter_freqs], energy, lw=0, marker='o')
    plt.title('能量分布')
    plt.show()
 
 
if __name__ == '__main__':
    # 读取筛选好的epoch数据
    epochsCom = mne.read_epochs(r'epoch_raw.fif')
    dataCom = epochsCom[0].get_data()[0][0]
    WPEnergy(dataCom, fs=250, wavelet='db4', maxlevel=6)
 


今日之事已毕,滚去好好研究代码学习计组了[Hurt]

  • 14
    点赞
  • 148
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值