MNE中数据结构Raw及其用法简介
1. 前言
最近在根据【脑机接口社区】的博客和公众号的推文学习python中mne库的用法,也算是真正开始上手EEG信号处理的代码了吧。本系列的推文所用的脑机接口均为OpenBCI,欢迎各位大佬一同交流!
2. 初始Raw对象结构
Raw对象主要是用来存储连续型的数据,其核心的数据主要为n_channels和n_times(时间×频率),其中也包括了Info对象。
- Raw结构查看
# 引入python库
import mne
from mne.datasets import sample
import matplotlib.pyplot as plt
# sample的存放地址
data_path = sample.data_path()
# 该fif文件存放地址
fname = data_path + '/MEG/sample/sample_audvis_raw.fif'
"""
如果上述给定的地址中存在该文件,则直接加载本地文件,
如果不存在则在网上下载改数据
"""
raw = mne.io.read_raw_fif(fname)
print(raw.info)
- 通过打印raw查看数据
<Info | 19 non-empty fields
bads : list | 0 items
ch_names : list | MEG 0113, MEG 0112, MEG 0111, MEG 0122, MEG 0123, EEG 053, EEG 052, EEG 051
chs : list | 8 items (EEG: 8)
comps : list | 0 items
custom_ref_applied : bool | False
dev_head_t : Transform | 3 items
dig : Digitization | 0 items
events : list | 0 items
file_id : dict | 4 items
highpass : float | 0.0 Hz
hpi_meas : list | 0 items
hpi_results : list | 0 items
lowpass : float | 128.0 Hz
meas_date : NoneType | unspecified
meas_id : dict | 4 items
nchan : int | 8
proc_history : list | 0 items
projs : list | 0 items
sfreq : float | 256.0 Hz
acq_pars : NoneType
acq_stim : NoneType
ctf_head_t : NoneType
description : NoneType
dev_ctf_t : NoneType
device_info : NoneType
experimenter : NoneType
gantry_angle : NoneType
helium_info : NoneType
hpi_subsystem : NoneType
kit_system_id : NoneType
line_freq : NoneType
proj_id : NoneType
proj_name : NoneType
subject_info : NoneType
utc_offset : NoneType
xplotter_layout : NoneType
>
- 从上述信息中可以看出不良通道bads,通道名称ch_names,采样频率sfreq等等。
3. 从头创建Raw对象
- 函数:mne.io.RawArray,其构造函数只接受矩阵和info对象
- 创建一个Raw对象时,需要准备两种数,一种是data数据,一种是info数据,其中data数据是一个二维数据,格式为(n_channels,n_times)
(1)获取OpenBCI的脑电数据
- 利用brainflow库进行数据的获取。以下代码用的是(SYNTHETIC_BOARD)合成板
import time
import numpy as np
import matplotlib
#matplotlib.use ('Agg')
from brainflow.board_shim import BoardShim, BrainFlowInputParams, BoardIds
import mne
BoardShim.enable_dev_board_logger ()
# use synthetic board for demo
params = BrainFlowInputParams ()
board = BoardShim (BoardIds.SYNTHETIC_BOARD.value, params)
board.prepare_session ()
board.start_stream ()
time.sleep (10) #获取10s的数据
data = board.get_board_data ()
board.stop_stream ()
board.release_session ()
eeg_channels = BoardShim.get_eeg_channels (BoardIds.SYNTHETIC_BOARD.value)
eeg_data = data[eeg_channels, :]
- 如果直接用脑机接口,可以将board = BoardShim (BoardIds.SYNTHETIC_BOARD.value, params)改成board = BoardShim (BoardIds.CYTON_BOARD_BOARD.value, params) 然后再改变端口,改变端口有两种办法,一种是暴力法,直接改boardshim库中的代码;另一种是采用另一种获取数据的代码。
方法一
boardshim.py >>> serial_port=‘COM3’
方法二
def main ():
parser = argparse.ArgumentParser ()
# use docs to check which parameters are required for specific board, e.g. for Cyton - set serial port
parser.add_argument ('--ip-port', type = int, help = 'ip port', required = False, default = 0)
parser.add_argument ('--ip-protocol', type = int, help = 'ip protocol, check IpProtocolType enum', required = False, default = 0)
parser.add_argument ('--ip-address', type = str, help = 'ip address', required = False, default = '')
parser.add_argument ('--serial-port', type = str, help = 'serial port', required = False, default = 'COM3')
parser.add_argument ('--mac-address', type = str, help = 'mac address', required = False, default = '')
parser.add_argument ('--other-info', type = str, help = 'other info', required = False, default = '')
parser.add_argument ('--streamer-params', type = str, help = 'other info', required = False, default = '')
parser.add_argument ('--board-id', type = int, help = 'board id, check docs to get a list of supported boards',default=0)
parser.add_argument ('--log', action = 'store_true')
args = parser.parse_args ()
params = BrainFlowInputParams ()
params.ip_port = args.ip_port
params.serial_port = args.serial_port
params.mac_address = args.mac_address
params.other_info = args.other_info
params.ip_address = args.ip_address
params.ip_protocol = args.ip_protocol
if (args.log):
BoardShim.enable_dev_board_logger ()
else:
BoardShim.disable_board_logger ()
board = BoardShim (args.board_id, params)
board.prepare_session ()
board.start_stream ()
time.sleep (1)
board.config_board ('/2') # enable analog mode only for Cyton Based Boards!
time.sleep (5)
data = board.get_board_data ()
如果不清楚端口是否连接成功,可以采用以下代码
def get_port():
import serial.tools.list_ports
port_list = list(serial.tools.list_ports.comports())
if len(port_list) == 0:
print('找不到串口')
else:
for i in range(0,len(port_list)):
print(port_list[i])
(2)创建Raw对象
- 创建info结构
- 内容包括:通道类型ch_types和通道名称ch_names
- 设置采样频率sfreq为合成板默认的频率256Hz
# Creating MNE objects from brainflow data arrays
ch_types = ['eeg', 'eeg', 'eeg', 'eeg', 'eeg', 'eeg', 'eeg', 'eeg']
ch_names = ['MEG 0113', 'MEG 0112', 'MEG 0111', 'MEG 0122', 'MEG 0123', 'EEG 053', 'EEG 052', 'EEG 051']#通道一般自行设定
sfreq = BoardShim.get_sampling_rate (BoardIds.SYNTHETIC_BOARD.value)
info = mne.create_info (ch_names = ch_names, sfreq = sfreq, ch_types = ch_types)
- 利用mne.io.RawArray类创建Raw对象
raw = mne.io.RawArray (eeg_data, info)
- 保存,载入刚才保存的数据并打印信息
picks = mne.pick_types(
info, meg=False, eeg=True, stim=False,
include=ch_names
)
raw.save("raw.fif", picks=picks, overwrite=True)
raw_fif_data = mne.io.read_raw_fif("raw.fif", preload=True, verbose='ERROR')
print(raw_fif_data.info)
(3)展示数据信号图
scalings = {'eeg': 1000}#采用1000倍缩小。
#scalings = 'auto' 也可以采用自动缩放比例
raw.plot(n_channels=8, scalings=scalings, title='Data from arrays',
show=True, block=True)
以下展示1000倍和8000倍缩小的数据波形。
4. 数据的相关绘制
- 通常raw的数据访问方式如下:
data, times = raw[picks, time_slice]
picks:是根据条件挑选出来的索引;
time_slice:时间切片 - 想要获取raw中所有数据,以下两种方式均可:
data,times=raw[:]
data,times=raw[:,:]
(1)获取0-10秒内的良好的EEG数据
- 根据type来选择 那些良好的EEG信号(良好的EEG信号,通过设置exclude=“bads”) channel,结果为 channels所对应的的索引
picks = mne.pick_types(raw.info, eeg=True, exclude='bads')
t_idx = raw.time_as_index([0., 10.])
data, times = raw[picks, t_idx[0]:t_idx[1]]
plt.plot(times,data.T)
plt.title("Sample channels")
plt.show()
- 这图是真滴丑,应该需要改改颜色。
(2)获取采样频率sfreq
采样频率,也称为采样速度或者采样率,定义了每秒从连续信号中提取并组成离散信号的采样个数,它用赫兹(Hz)来表示。
采样频率的倒数是采样周期或者叫作采样时间,它是采样之间的时间间隔。
通俗的讲采样频率是指计算机每秒钟采集多少个信号样本。
sfreq=raw.info['sfreq']
"""
获取索引为m到n的样本,每个样本从第k次到第h次.
data,times=raw[m:n,k:h]
其中data为索引为m到n的样本,每个样本从第k次到第h次.
times是以第k次采样的时间作为开始时间,第h次采样时的时间为结束时间的时间数组。
"""
data,times=raw[:3,int(sfreq*1):int(sfreq*3)]
plt.plot(times,data.T)
plt.title("Sample channels")
以下几幅图因为用的是合成板,所以无法确定通道的位置,因而借用以下脑机接口社区的图片
(3)绘制各通道的功率谱密度
raw.plot_psd()
plt.show()
(4)绘制SSP矢量图
raw.plot_projs_topomap()
plt.show()
(5)绘制通道频谱图作为topography
raw.plot_psd_topo()
plt.show()
(6)绘制电极位置
raw.plot_sensors()
plt.show()