【python+E-prime+fNIRS】探究认知资源在情绪与认知灵活性中是否存在中介效应课题

【写在前面:这是笔者心理学的本科毕业设计内容,在此记录设计中所包含的部分思路、代码操作和设计步骤,方便后续查阅和学习,希望对大家有点帮助,对于不足之处,也希望各路大佬可以不吝赐教。本文为作者原创文章,文中所示的图片、代码皆来自网络或笔者自制,仅做学习、记录使用,如果某些东西涉及侵权,或者发现有任何写的不对的地方,请作者大大告知笔者,可以对此进行补充说明。如有人私自引入商业使用构成侵权或违法犯罪,则笔者概不负责。鉴于今年是卢雷元年,必须声明一下,如若抄袭本文作为毕设内容,笔者保留追究责任的权利。】

一、相关背景介绍

1.1 认知灵活性

认知灵活性(cognitive flexibility)就是对不断变化的环境条件有选择性地转换和适应行为的能力(Miyake, A. et al., 2000; Norman, D. A. et al., 1980)。
在这里插入图片描述
由于是关于认知灵活性的研究,故先用一幅图简单说明研究中常用的实验设计方法及设计中各任务之间的关系。而在认知灵活性的实验中,大多都是对被试在完成不同任务时的转换代价进行的研究。而常用的几种研究范式有交替转换范式、单一转换范式、自主转换范式和任务线索提示范式这几种。简要介绍下常用的两种范式。

任务线索提示范式
在任务线索提示范式中,我们需要提前设计好任务出场的顺序,尽可能地保证任务是随机(伪随机)出现的。打个比方,实验中有任务A和任务B,那么我们就可以按照ABABBABABBABAB…的顺序或者BAABABBABAABABA…的顺序安排任务,可以人为安排,或者用代码(如下python)或excel随机生成,例如下面这个思路。

# python 3.7.4 
# jupyter notebook
# AB任务随机

import random
trail_num = int(input("实验总共有几组trail:"))
half_num = int(trail_num / 2) 
a_num = 0
b_num = 0
trail = ""

for i in range(trail_num):
    if a_num == half_num:
        trail = trail + "B"
    elif b_num == half_num:
        trail = trail + "A"
    else:
        if random.random() < 0.5:
            trail = trail + "A"
            a_num = a_num + 1
        else:
            trail = trail + "B"
            b_num = b_num + 1
trail

直接运行代码,输入实验要生成几组trail,就能得到结果,执行结果如下:

在这里插入图片描述
之后,我们便可以按照这个顺序安排实验任务的出场顺序,但需要注意的是,安排的任务中需要注意几点问题:(1)对于两个任务,我们需要尽可能将任务难度设置成一致,比如任务A是判断大小,任务B是判断数字奇偶,不然如果任务A是问1+1=?,任务B是求解地球到太阳的距离,会出问题的。(2)由于被试是按照我们程序设定的提示做出相应反应,完成对应任务的,那么提示的线索需要做到足够简单,类型一致,放置方位一致,所以基本上常用的提示线索有简单几何图形,颜色等。(3)在设置任务按键的时候,要注意按键平衡,比如对于被试甲来说,第一个任务按q键是正确的,第二个任务按p键是正确的,那么对于被试乙来说,第一个任务则需要按p键是正确的,第二个任务则是按q键是正确的,以此类推。

当然,对于以上需要注意的点,若是有特殊的实验设计,则可以忽略。

自主选择范式
在自主选择范式中,我们需要让被试自主选择目标任务去完成,即假设第一个任务是A,那么被试可以自主选择第二个任务是A还是B。由于在我的实验中并没有涉及到这部分的设计,所以在此就不展开介绍了。

1.2 情绪

情绪是我们生活中非常常见的一种心理现象,开心,愤怒,痛苦…但在本篇文章中,在情绪的诱发方面主要用到的是Russell(1980)所提出的一个二维情绪模型,在这个模型中,情绪被划分成了两个维度,即效价和唤醒度。如下图所示,x轴表示情绪的效价,而y轴表示的是情绪的唤醒度。于是我们就可以根据这个维度信息来对情绪进行一个简单的划分,即积极情绪(Positive affect)、中性情绪(Neutral affect)、消极情绪(Negative affect)三种,也就是本文中用到的部分。当然,这里的三个部分是指在横坐标上的一个划分,即效度上的一个划分,而唤醒度部分在下文中再予以讨论。

在这里插入图片描述


1.3 认知资源

认知资源理论是一个非常经典的理论了,大致上描述起来就是,理论认为我们的认知是属于一种资源,是有限的,假设我们有100个单位的认知资源,那么码字可能就需要消耗掉80个单位,听歌消耗掉20个单位,那么这个时候,再叫我们去背个书,就做不到了。但是,我们可以人为地分配我们的认知资源,让它投入到我们希望投入的事情中去。

同时,我们知道记忆将会消耗一定量的认知资源,特别是处理目前手头事情的工作记忆,是前人探索认知资源时一个非常常用的锚点,即让你记住某些字词或图形一段时间,与此同时,你需要去做一些别的任务,完成任务后再对这部分的记忆内容去进行复述。

因为相信大家对这个概念相对来说都比较熟了,在这里就不展开说了。


1.4 功能性近红外成像(fNIRS)

总体介绍

功能近红外光谱设备(fNIRS, functional near-infrared spectroscopy)是一种新兴的脑成像探测技术,它是一项主要运用了近红外光,对大脑表层的血流动力学反应进行测量的功能性神经成像技术,其原理可类比依赖检测大脑血氧水平的功能磁共振技术,即是检测由大脑神经活动引起的血红蛋白浓度变化的一项技术(Logothetis, N. et al., 2001; Mehagnoul-Schipper D J et al., 2002; 杨茜, 2020)。大致原理为脑组织中生色团物质之间存在着对不同波长近红外光表现出不一样的吸收和散射关系,因此,我们可根据大脑表层对近红外光的吸收和散射程度来测定大脑血管中含氧血红蛋白 (HbO, Oxygenated hemoglobin) 和脱氧血红蛋白 (HbR, Deoxygenated hemoglobin) 的浓度,从而达到间接考察对应区域神经元活动、细胞代谢情况等大脑功能变化的目的 (Villringer A et al., 1997; 杨茜, 2020)。

基本原理

简单点说原理就是如上左图所示,图是从另一位博主[1]那粘贴的,概括来说就是近红外的最简结构是由一个近红外光源(Source)和一个探头(Detector)组成的,光线由S头发出,被D头接收到,经过电脑处理之后,就可以分析出S到D之间个区域的血氧情况。而由于在实验中通常都是由多个探头矩阵组成的,例如常见的8x8的组合方式,即8光源8探头,那么若全部S和D同时工作的话,就有可能出现多组信号源同时到达探测器,从而无法判定信号来源方向的问题。讲人话就是,如上右图所示,如果S1、S2、S3同时打开,那么D1就会在同一时间收到三个信号,那么这时候,就会出现两个bug,一是信号可能会叠加,从而引发共振或叠加造成数据错误;二是无法判断信号源,从而无法判定信号来的方向,还是以D1为例,它会无法判断现在拿到的信号是来自S1的、S2的还是S3的。所以为了解决这个bug,在近红外设备设计之初,就采取了差分发射的方式,用上右图的探头分布模板简单说就是,第一帧是S1发光,S2、S3、S4、S5不发光,第二帧是S2、S4发光,S1、S3、S5不发光,第三帧是S3、S5发光,S1、S2、S4不发光,以此类推,这样就可以保证对同一个探头(Detector)不会同时收到两个信号,即如下图所示。当然,如果是使用系统预设的探头分布模板的话,这部分可以不管,但如果需要自己设计探头分布模板的话,就需要注意这个问题。

在这里插入图片描述


1.5 事件相关设计

事件相关设计 (Event-related (ER)) 经常在认知灵活性的实验中被应用,且由于它们比封锁式任务范式更灵活,故常是研究认知过程的一个先决条件(Burock et al., 1998, Rosen et al., 1998, Pollmann et al., 2000; Plichta M M et al., 2007)。而其他常用的设计方法如图所示,如组块设计 (Block-design (BD)) 等。这个看图应该是很好理解的,就是一个在近红外和核磁实验中非常常见的一个任务设计模式。

在这里插入图片描述


二、实验设计部分

2.1 实验设计思路

情绪图片选择上

由于在本次的实验中,激活情绪的部分用的是CAPS情绪库(中国情绪图片库,Chinese Affective Picture System),在图片库中,有给出的每张图片对应的效度和唤醒度的评分,将所有效度和唤醒度数据以二维点图的形式导出,就会呈现如下左图所示的点阵。与上图相同的是,横坐标代表的是情绪的效价,纵坐标表示的是情绪的唤醒度。由于图片的作用是诱发对应的情绪,所以不难看出,情绪的唤醒度水平相对是比较高的,这也是为什么上文中没有对唤醒度进行区分的原因。随后,根据左图,我们人为地将CAPS情绪库分成右图中的三个部分,分别代表积极情绪、中性情绪、消极情绪三种目标诱发情绪。在之后的实验中,我们将在这三个分类框框中的点,所对应的图片,去代表对应的情绪,即比如说实验需要用到消极情绪诱发图片,那么就在红框框中随机抽取。图片抽取的操作直接在EXCEL中完成(筛选)即可。这种划分方式是我自己设定的,大伙可以根据自己的实验需求重新划分。

实验设计思路
在这里插入图片描述
我的实验思路如上图所示,由于大伙要研究的东西都不同,所以这里就不展开了,简单放个图,下一part。


2.2 E-prime部分

这一部分可以说是整个实验中最关键的部分,因为整个实验包括行为和近红外在内的所有控制操作都是在eprime中去完成的,相当于是问卷实验中的那份问卷。下图展示的是我eprime中设计的一整个完整流程。

在这里插入图片描述

好像是有那么一点长and复杂,化简一下就是下面这个流程图所表示的思路,对比着看应该好一些。

在这里插入图片描述

接下来就是一步步拆解开,对每个部分进行设计就可以了。

实验教学部分,这里巨简单,一步步插图就可以了,不废话,下一步。

训练组部分,这部分也不难,只不过我设计了个自动判定跳转的步骤,逻辑的话看下图应该很好理解,而这里的list,则可以看做是一个任务池,随机从里捞任务(random)做就可以了。当然这个设计方式不唯一,比如说把这个没能通过的提示文本放在判断正确率之后也是可以的。

在这里插入图片描述

实验组部分就相对比较复杂了,由于eprime的单行道模式,所以操作起来就会很麻烦。但是,咱们可以运用面向对象的编程思维,给他封装、多态、继承一下,这样操作起来就方便很多了。具体说呢,就是我们把eprime中提供的list,看成procedure(因为list会一行一行执行命令),然后把每一行所执行的procedure看成是一个封装好的函数,而后面加进去的列,就是我们要往函数里传的参数。(如下所示代码) 这样一来,procedure设计问题,就会变成一个list填写,和对每个对象(函数)进行封装的问题。

#下图所示的表格,即可理解成这样,仅举例说明理解思路,不完全正确
def procedure(emoLoad, trailMission, fTrailAnswer, cogLoad, Mark):

既然用到了面向对象的方法,那么首先第一步需要做的就是抽象出这个程序中的类,讲人话就是把相同的东西放一块,然后用一个函数(procedure)去解决它。按照我的实验设计思路,在实验组中,会依次按照下图所示的这个顺序,向被试呈现这一屏一屏的东西。
在这里插入图片描述
思路有了之后,我们再来聚类分析,把相似的东西凑一凑,很轻松就可以分成以下七个部分(空格中表示的是我在程序中设置的procedure名称),分别是呈现记忆词(cogRemenber)、情绪刺激(formalEmo)、注视点(fFocus)、认知任务(formalMission)、空白屏(fBlank)、记忆词复述(cogRecognize)、休息(rRest)。

在这里插入图片描述

这样一来,每个对象(函数)中的procedure,就会变的非常简单,看上面那个图可以看出来,每个procedure里面基本就一两个屏。而难点就变成了如何填写这个list,即如下图所示,我们要做的就是把左边这个list填满,变成右边的就可以了。而这个表格的建立思路是,第一行放你需要执行的第一个procedure,第二行放第二个procedure,以此类推,而后面新增的列,要填的是你需要往这个procedure中传进去的数据(参数)(参数的作用如下图所示),像我这里需要的情绪图片(文件名)、提示做哪个任务的任务图片(文件名)、这个任务的答案(按键)、记忆词(字符)、近红外mark号(数字)这几个。这样一来,我们就可以借助外界工具了,填表嘛,谁不会啊。

在这里插入图片描述

下面提供一个我的填表方法,因为我的实验总共有六个分组,每组里面要填708行x8列的表格,相当于就是6x708x8=33984个格子,虽然有规律可循,但是我选择敲代码,复制粘贴会死的orz。

"""
python 3.7.4 
jupyter notebook
正式list生成 2.0 函数封装

group_num输入 0 || 1:
    0表示先高负载,后低负载
    1表示先低负载,后高负载
    
emo_group输入 non_emotion || active_emotion || negetive_emotion:
	这里需要预先导入前文中划分好的情绪图片的表格
    non_emotion表示中性情绪
    active_emotion表示积极情绪
    negetive_emotion表示消极情绪

"""

def trail_list_creat(group_num, emo_group):

    trail_list = ['10011', '11010', '01001', '01010', '00000', '01111', '11000', '01100'] #1、0分别代表不同的任务
    
    high_load = ['SNKSG','UTOAJ','VTQBZ','BWPDK','BIQVS','XKCYV','SNGXW','CPEYR','VJFSM','XGKBW','MWJTA','MKLVG','MCKWV','XKNMC','YKSLM','AYPOZ']  #高负荷组记忆词
    low_load = ['RRRRR','QQQQQ','JJJJJ','SSSSS','UUUUU','ZZZZZ','HHHHH','MMMMM','LLLLL','NNNNN','IIIII','EEEEE','UUUUU','JJJJJ','UUUUU','OOOOO']  #低负荷组记忆词
    
    square = ['s1.png','s2.png','s3.png','s4.png','s6.png','s7.png','s8.png','s9.png']  #方形任务展示图片的文件名
    dimand = ['d1.png','d2.png','d3.png','d4.png','d6.png','d7.png','d8.png','d9.png']  #菱形任务展示图片的文件名


    fTrail_df = DataFrame()
    count_num = 0     #计负载组
    m = 0            #计休息组
    high_low_bool = group_num   #高低认知负荷布尔,切换高低认知负载


    #生成任务列表
    while True:

        #休息行
        if m == 8:
            
            fTrail_df = pd.concat([fTrail_df, DataFrame([1,'nan','rRest','rest','rest','rest','rest',23]).T])
            m = 0


        #非休息行,则执行trail行
        else:   

            # 认知负载行
            if high_low_bool % 2 == 0:  #输入高负载组记忆词
                n = count_num % 16
                a = [1, 'nan','cogRemenber','nan','nan','nan']
                a.append(high_load[n])
                a.append(11)  #添加近红外mark,mark数为11

            else:  #输入低负载组记忆词
                n = count_num % 16
                a = [1, 'nan','cogRemenber','nan','nan','nan']
                a.append(low_load[n])
                a.append(11)  #添加近红外mark,mark数为11

            fTrail_df = pd.concat([fTrail_df, DataFrame(a).T])



            j = count_num % 8 # 负荷"电平"跳转

            test_df = DataFrame()

            for k in range(5):
                a = []
                b = trail_list[j]
                
                
                # 情绪刺激
                pic_name = emo_group['pic_name_value'][np.random.randint(len(emo_group))]
                #由于是由random函数随机挑选图片
                #这两个判断是为了将 5.bmp 这种文件名改成 005.bmp 这种形式
                if len(pic_name) < 7:
                    pic_name = '0' + pic_name
                if len(pic_name) < 7:
                    pic_name = '0' + pic_name
                a.append(1)
                a.append('nan')
                a.append('formalEmo')
                a.append(pic_name)
                a.append('nan')
                a.append('nan')
                a.append('nan')
                a.append(19)  #添加近红外mark,mark数为19
                test_df = pd.concat([test_df, DataFrame(a).T])
                
                
                # 注视点行
                test_df = pd.concat([test_df, DataFrame([1, 'nan', 'fFocus', 'nan', 'nan', 'nan', 'nan', 196]).T])
                

                
                # 认知任务 + 答案
                a = [1,'nan','formalMission','nan']
                if b[k:k+1] == '0':  #判断任务类型,0则选择方形任务,1则选择菱形任务
                    sq = square[np.random.randint(len(square))]
                    a.append(sq)
                    
                    #生成该trail的答案
                    if sq.find('6')== 1 or sq.find('7')== 1 or sq.find('8')== 1 or sq.find('9')== 1:   # 正方形判断大小,less为小,more为大
                        a.append('[more]')
                    else:
                        a.append('[less]')


                if b[k:k+1] == '1':  #判断任务类型,0则选择方形任务,1则选择菱形任务
                    di = dimand[np.random.randint(len(dimand))]
                    a.append(di)

                    #生成该trail的答案
                    if di.find('2')== 1 or di.find('4')== 1 or di.find('6')== 1 or di.find('8')== 1:    # 菱形判断奇偶,less为奇数,more为偶数
                        a.append('[more]')
                    else:
                        a.append('[less]')

                a.append('nan')
                a.append(13)  #添加近红外mark,mark数为13
                test_df = pd.concat([test_df, DataFrame(a).T])
                
                
                # 任务后空屏
                test_df = pd.concat([test_df, DataFrame([1, 'nan', 'fBlank', 'nan', 'nan', 'nan', 'nan', 197]).T])  
            
            
            #让被试复述记忆词
            test_df = pd.concat([test_df, DataFrame([1,'nan','cogRecognize', 'answer','answer','answer','answer',22]).T])  
            fTrail_df = pd.concat([fTrail_df, test_df])
            
            
            #完成一轮生成,计数器加一操作
            m += 1
            count_num += 1

            if count_num == 16:
                high_low_bool += 1



        #判断退出条件
        if count_num == 32:
            break

            

    fTrail_df = pd.concat([fTrail_df, DataFrame([1,'nan','rRest','rest','rest','rest','rest',23]).T]) #最后一组休息
    fTrail_df.columns = ['weight','nested','procedure','ranEmotionPicName','trailPic','answer','cogLoadWord','mark']  #重置列名
    fTrail_df.reset_index(drop = True, inplace = True)  #重置行索引
    return fTrail_df

上面代码运行的结果如图所示

在这里插入图片描述

最后一个比较重要的点是,需要按照实验设计及去确定是否需要控制eprime程序的时长,即在random空白屏时间的时候,需要注意模式的设置,两种常见的模式如下图所示,例如在核磁的实验中,即需要用到C-Mode。由于我没用到,故这里不展开。

在这里插入图片描述


2.3 近红外部分

这一部分操作起来其实不算太难,简要来说就分几步。

第一步,接通电源(废话)。

第二步,需要在eprime程序中设置端口,讲人话就是eprime和fNIRS是两台电脑在跑,你得给这两台电脑接根线,且告诉电脑,你接的线在哪。端口的设置操作流程如下图所示,我没记错的话,咱们学校实验室是用的是DB25并口。(印象中好像还要设置个port3还是啥的,具体咋搞的我忘了)

在这里插入图片描述

第三步,设置mark。这个mark是用来标记你要研究的事件的,举个简单的例子(看图),不打mark就是左图,打了mark就是右图,如果不打mark的话,就根本不知道什么时候发生了什么事,那还研究个锤子。而mark的设置方法,通常有两种(如图),直接操作和用inline语句(代码)设置,而mark通常是一个8 bit的TTL信号,讲人话就是,可以用的mark数是0~255,共256(28)个。但是要注意,通常前十个mark会被预设,所以不用,咱们用11到255就好。 设置好mark之后最好检查一下,是不是你要的mark都打上了,有没有出现漏打的情况,mark打上了就会出现最后一幅图呈现的情况。如果发现mark漏了,我的建议是,直接换mark号,比如11换成12,原因就不解释了。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

第四步,给被试戴好设备(电极帽),选择测试模板(如下图是我这次用的),模板可用软件默认的或自行设定,然后测试各信道信号(看这个从操作手册上搞下来的图,右边那一排按钮),确认信号没问题了,且确认数据开始记录(record)了,即可开始实验。

在这里插入图片描述

在这里插入图片描述


三、数据处理部分

3.1 E-prime数据处理(行为数据)

这一部分的数据处理大体分为以下几步

在这里插入图片描述
输出这步在实验过程中就已经完成了,跳,spss分析数据要根据大伙的实验需求来处理,这里不展开。那么就这剩下整理数据这步,这一步按照往常的经验来说,可以用eprime中自带的那个数据处理软件来处理,但是这玩意好像没办法做批量处理(我也不知道,因为我没用过),但是我们的被试通常又会有非常非常多个,那么一个个搞的话还是很烦人的,所以这里提供一个博主自己写的数据整理demo(代码在最下面)。注意:这里要处理的文件是实验结束后会自己生成的那个txt文件!!!txt文件!!!txt文件!!!(很重要所以说三遍)

这个代码的思路很简单,主要就分以下几个模块,接下来我们一个模块一个模块地讲他们的用途。

在这里插入图片描述


纠错模式

在这一步中,是将一个被试文件拆解成一个excel表格,建议不清楚自己程序里设置了什么进程名和事件名的朋友先执行一下这一步,把这个excel表格调出来再进行下一步的操作。表格出来之后会是如图所示的样子,我们要找的就是如图所示中的两个东西。注意在复制粘贴的时候别把前后的空格复制上了!!如果前后有空格这程序会发生什么我也不是很清楚…

在这里插入图片描述


单被试处理

在找到要用的进程名、事件名之后,可以先看看单个被试中处理的结果是不是自己想要的,当然如果不需要看单个数据也可以直接跳过这步。

这里简单展示一下这一步处理中会出现的结果:

反应时与正确率(RT和ACC):

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述


被试输入检测:

在这里插入图片描述
在这里插入图片描述


快速处理

这一步是用列集成的方法,把所有的被试数据集中到了前几列中,方便筛选数据。

在这一步操作中,我们需要把所有实验的txt文件放到同一个文件夹里,如图所示。其次,需保证这个文件夹中有且仅有实验数据的txt文件,不能有其他文件!!也就是说,最终生成的excel文件最好保存到其他地方去。

在这里插入图片描述

运行结果:
在这里插入图片描述
在这里插入图片描述
像展示的这样,所有的结果就被集成到了前两列,那么我们就可以直接在excel中加个筛选,去筛除掉我们不需要的数据,例如这里的可以直接筛选掉ACC为0的行(就是没做对的试次)。被试输入模块的检测同理,这里就不放图了。


实验数据查验

这一步是用行集成的方法,把所有的被试数据集中到了前几行中,方便对实验结果进行检查。

在这一步操作中,我们需要把所有实验的txt文件放到同一个文件夹里,如图所示。其次,需保证这个文件夹中有且仅有实验数据的txt文件,不能有其他文件!!也就是说,最终生成的excel文件最好保存到其他地方去。

在这个模块中,是将所有被试的数据,按照行集成的方式集中在了一块,方便检查数据是否有问题,第一行中表示的是大伙在实验时输入的那个Subject和Session,对应的列就是该被试做出来的结果。

运行结果:

在这里插入图片描述

在这里插入图片描述

例如如图所示的以反应时和正确率为例,1、2列为一个被试的实验结果,第一列中表示的是反应时,第二列中表示的是正确率,以此类推。被试输入模块的检测同理,这里就不放图了。


其他数据分析

由于可能需要处理的数据不仅仅是反应时、正确率、被试输入这些常用的,所以博主在此留了一个其他数据的窗口,即下图中的这个其他选项,在这个选项中,要找的东西与之前的稍稍有点不同,但原理一致。

在这里插入图片描述

还是在一开始的这个纠错文件中,同理我们需要找到进程名和事件名,即下图中红线画出来的的这两个,进程名同样是formalMission,但事件名变成了Imagedisplay5.RTTime,就是要把冒号前的所有字符复制进去(注意别把前面的空格复制上了),然后其他操作同理即可。

在这里插入图片描述

运行结果:

在这里插入图片描述

在这里插入图片描述

这里就是拿这个 formalMission 和 Imagedisplay5.RTTime 举个例子给大家看看结果,大伙具体情况具体分析,在此不啰嗦了。


最后贴上数据处理的代码

'''
eprime数据处理代码

简介:主要解决了eprime中无法批量处理的问题,功能包括:对单个被试数据分析、数据批量处理、程序半自动自检

代码版本:python  3.7.4
编译器:jupyer notebook

各调用包版本:
numpy  1.16.5
pandas  0.25.1

'''


import pandas as pd
import numpy as np
from pandas import Series,DataFrame
import os


#路径优化函数
def path_correct(file_path):
    
    corrected_path = file_path.replace('\\', '/')
    
    return corrected_path




#变量输入模块
def var_input_Function():
    
    event_name = str(input("进程名(Procedure):"))  #输入进程名
    var_name = str(input("事件名:"))  #输入事件名(RTACC\RESP\其他(需要区分大小写))
    
    return event_name, var_name




#反应时与正确率筛选函数
def RT_ACC(event_name, var_name, origin_dataF):
    
    transition_dataF = origin_dataF
    target_RT_list = []
    target_ACC_list = []
    
    for i in range(len(transition_dataF)):
        execute_row = transition_dataF[0][i]

        if execute_row.count('\tProcedure: '+ event_name +'\r') == 1:
            target_ACC_data = execute_row[execute_row.rfind(var_name+'.ACC:'):]
            target_RT_data = target_ACC_data
            target_ACC_data = target_ACC_data[target_ACC_data.find(':')+2:target_ACC_data.find("\r\n\t")]
            target_RT_data = target_RT_data[target_RT_data.rfind(var_name+'.RT:'):]
            target_RT_data = target_RT_data[target_RT_data.find(':')+2:target_RT_data.find("\r\n\t")]
            
            target_RT_list.append(str(target_RT_data))
            target_ACC_list.append(str(target_ACC_data))
            
    return_parameter = pd.concat([DataFrame(target_RT_list), DataFrame(target_ACC_list)], axis=1)
    return return_parameter




#输入筛选函数
def User_input(event_name, var_name, origin_dataF):
    
    transition_dataF = origin_dataF
    target_answer_list = []
    
    for i in range(len(transition_dataF)):
        execute_row = transition_dataF[0][i]

        if execute_row.count('\tProcedure: '+ event_name +'\r') == 1:
            target_answer_data = execute_row[execute_row.rfind(var_name+'.RESP:'):]
            target_answer_data = target_answer_data[target_answer_data.find(':')+2:target_answer_data.find("{ENTER}\r\n\t")]
            
            target_answer_list.append(str(target_answer_data))
            
    return_parameter = DataFrame(target_answer_list)
    return return_parameter





#其他数据
def Extra_data(event_name, var_name, origin_dataF):
    
    transition_dataF = origin_dataF
    target_answer_list = []
    
    for i in range(len(transition_dataF)):
        execute_row = transition_dataF[0][i]

        if execute_row.count('\tProcedure: '+ event_name +'\r') == 1:
            target_answer_data = execute_row[execute_row.rfind(var_name+':'):]
            target_answer_data = target_answer_data[target_answer_data.find(':')+2:target_answer_data.find("\r\n\t")]
            
            target_answer_list.append(str(target_answer_data))
            
    return_parameter = DataFrame(target_answer_list)
    return return_parameter




#输出excel
def data_to_excel(target_dF):
    
    target_Path = str(input('输入保存路径'))
    excel_name = str(input('输入文件名'))
    target_Path = path_correct(target_Path)
    target_Path = target_Path + '/' + excel_name + '.xls'
    
    target_dF.to_excel(target_Path)


    

#数据初步分解
def data_pre_deconstruction(data_Path):
    
    with open(data_Path, "rb") as data:
        header = data.read(2)
        exp_data = data.read().decode('utf-16-le')

    short_exp_data = DataFrame(exp_data.split('*** LogFrame Start ***'))
    
    return short_exp_data

    

    
#判断处理模式(RTACC、输入、其他)
def tackle_mode_judge():
    
    tackle_mode = str(input('反应时和正确率(a)/ 被试输入检测(b)/ 其他(c)'))
    
    if tackle_mode == 'a' or tackle_mode == '(a)' or tackle_mode == '(a)' or tackle_mode == '反应时和正确率' or tackle_mode == '反应时和正确率(a)' or tackle_mode == '反应时':
        
        tackle_mode = 'A_mode'
        
    elif tackle_mode == 'b' or tackle_mode == '(b)' or tackle_mode == '(b)' or tackle_mode == '被试输入检测' or tackle_mode == '被试输入检测(b)' or tackle_mode == ' 被试输入检测' or tackle_mode == ' 被试输入检测(b)':
        
        tackle_mode = 'B_mode'
    
    elif tackle_mode == 'c' or tackle_mode == '(c)' or tackle_mode == '(c)' or tackle_mode == '其他' or tackle_mode == '其他(c)' or tackle_mode == ' 其他' or tackle_mode == ' 其他(c)':
        
        tackle_mode = 'C_mode'
    
    else:
        print('请正确输入指令')
    
    
    return tackle_mode
    
    



    
    


'''中控台__主程序'''
if __name__ == '__main__':
    
    inspect_mode = input(str("处理模式(a)/ 纠错模式(b)")).lower()
    
    #数据处理模式
    if inspect_mode == 'a' or inspect_mode == '(a)' or inspect_mode == '(a)' or inspect_mode == '处理' or inspect_mode == '处理模式(a)' or inspect_mode == '处理模式':
        
        single_or_quantity_mode = input(str("单被试模式(a)/ 批量处理模式(b)")).lower()
        
        
        #单被试模式
        if single_or_quantity_mode == 'a' or single_or_quantity_mode == '(a)' or single_or_quantity_mode == '(a)' or single_or_quantity_mode == '单被试' or single_or_quantity_mode == '单被试模式(a)' or single_or_quantity_mode == '单被试模式':
            
            
            single_Data_Path = str(input('输入目标文件路径'))
            single_Data_name = str(input('输入目标文件名'))
            
            if '.txt' in single_Data_name:
                pass
            else:
                single_Data_name = single_Data_name + '.txt'
            

            single_Data_Path = path_correct(single_Data_Path)
            single_Data_Path = single_Data_Path + '/' + single_Data_name
            
            tackling_dataF = data_pre_deconstruction(single_Data_Path)
            tackling_Mode = tackle_mode_judge()
            
            if tackling_Mode == 'A_mode':
                
                event_name_get, var_name_get = var_input_Function()
                tackled_dataF = RT_ACC(event_name_get, var_name_get, tackling_dataF)
                
                tackled_dataF.columns = ['RT', 'ACC']
                
                data_to_excel(tackled_dataF)
            
            elif tackling_Mode == 'B_mode':
                
                event_name_get, var_name_get = var_input_Function()
                tackled_dataF = User_input(event_name_get, var_name_get, tackling_dataF)
                
                data_to_excel(tackled_dataF)
                
            elif tackling_Mode == 'C_mode':
                
                event_name_get, var_name_get = var_input_Function()
                tackled_dataF = Extra_data(event_name_get, var_name_get, tackling_dataF)
                
                data_to_excel(tackled_dataF)
                
            
        
        
        
        #批量处理模式
        elif single_or_quantity_mode == 'b' or single_or_quantity_mode == '(b)' or single_or_quantity_mode == '(b)' or single_or_quantity_mode == '批量' or single_or_quantity_mode == ' 批量'or single_or_quantity_mode == '批量处理' or single_or_quantity_mode == ' 批量处理' or single_or_quantity_mode == '批量处理模式(b)' or single_or_quantity_mode == '批量处理模式' or single_or_quantity_mode == ' 批量处理模式' or single_or_quantity_mode == ' 批量处理模式(b)':
            
            
            
            
            row_or_line_mode = input(str("快速处理(a)/ 实验数据查验(b)")).lower()
            dir_Path = str(input('输入目标文件夹路径'))
            tackling_Mode = tackle_mode_judge()
            dir_Path = path_correct(dir_Path)
            file_names = os.listdir(dir_Path)
            
            
            
            
            #简易处理模式(行集成)
            if row_or_line_mode == 'a' or row_or_line_mode == '(a)' or row_or_line_mode == '(a)' or row_or_line_mode == '快速' or row_or_line_mode == '快速处理(a)' or row_or_line_mode == '快速处理':
                
                final_tackled_dataF = DataFrame()
                event_name_get, var_name_get = var_input_Function()
                
                for i in range(len(file_names)):
                    
                    point_file_path = dir_Path + '/' + file_names[i]
                    tackling_dataF = data_pre_deconstruction(point_file_path)
                    
                    if tackling_Mode == 'A_mode':
                
                        tackled_dataF = RT_ACC(event_name_get, var_name_get, tackling_dataF)

                    elif tackling_Mode == 'B_mode':

                        tackled_dataF = User_input(event_name_get, var_name_get, tackling_dataF)


                    elif tackling_Mode == 'C_mode':

                        tackled_dataF = Extra_data(event_name_get, var_name_get, tackling_dataF)
                        
                    final_tackled_dataF = pd.concat([final_tackled_dataF,tackled_dataF])
                    
                final_tackled_dataF.reset_index(drop = True, inplace = True)
                
                if tackling_Mode == 'A_mode':
                    final_tackled_dataF.columns = ['RT', 'ACC']
                
                data_to_excel(final_tackled_dataF)

                    
                    
            
            
            
            
            #查证模式(列集成)
            elif row_or_line_mode == 'b' or row_or_line_mode == '(b)' or row_or_line_mode == '(b)' or row_or_line_mode == '实验数据查验' or row_or_line_mode == ' 实验数据查验'or row_or_line_mode == '实验数据' or row_or_line_mode == ' 实验数据' or row_or_line_mode == '实验数据查验(b)' or row_or_line_mode == '实验数据查验' or row_or_line_mode == ' 实验数据查验' or row_or_line_mode == ' 实验数据查验(b)':
            
                Subjects_id = []
                event_name_get, var_name_get = var_input_Function()
                final_tackled_dataF = DataFrame()

                for i in range(len(file_names)):

                    point_file_path = dir_Path + '/' + file_names[i]
                    tackling_dataF = data_pre_deconstruction(point_file_path)

                    if tackling_Mode == 'A_mode':

                        header = tackling_dataF[0][0]

                        subject = header[header.rfind('Subject:'):header.rfind('\r\nSession')]
                        section = header[header.rfind('Session:'):header.rfind('\r\nDataFile.')]

                        Subjects_id.append(subject + '_' + section)
                        Subjects_id.append('')
                        
                        tackled_dataF = RT_ACC(event_name_get, var_name_get, tackling_dataF)

                    elif tackling_Mode == 'B_mode':

                        header = tackling_dataF[0][0]

                        subject = header[header.rfind('Subject:'):header.rfind('\r\nSession')]
                        section = header[header.rfind('Session:'):header.rfind('\r\nDataFile.')]

                        Subjects_id.append(subject + '_' + section)

                        tackled_dataF = User_input(event_name_get, var_name_get, tackling_dataF)


                    elif tackling_Mode == 'C_mode':

                        header = tackling_dataF[0][0]

                        subject = header[header.rfind('Subject:'):header.rfind('\r\nSession')]
                        section = header[header.rfind('Session:'):header.rfind('\r\nDataFile.')]

                        Subjects_id.append(subject + '_' + section)

                        tackled_dataF = Extra_data(event_name_get, var_name_get, tackling_dataF)

                    final_tackled_dataF = pd.concat([final_tackled_dataF,tackled_dataF], axis=1)

                final_tackled_dataF.reset_index(drop = True, inplace = True)
                final_tackled_dataF.columns = Subjects_id

                data_to_excel(final_tackled_dataF)

            
        
        
        #输入错误
        else:
            print("请正确输入指令")

    
    #纠错模式
    elif inspect_mode == 'b' or inspect_mode == '(b)' or inspect_mode == '(b)' or inspect_mode == '纠错' or inspect_mode == ' 纠错' or inspect_mode == '纠错模式(b)' or inspect_mode == '纠错模式' or inspect_mode == ' 纠错模式' or inspect_mode == ' 纠错模式(b)':
        
        cor_file_Path = str(input('输入目标文件路径'))
        cor_file_name = str(input('输入目标文件名'))
        cor_file_Path = path_correct(cor_file_Path) + '/' + cor_file_name
        data_to_excel(data_pre_deconstruction(cor_file_Path))
        
    
    
    #输入错误
    else:
        print("请正确输入指令")
    


3.2 近红外数据处理

给大伙贴个近红外设备代理商出的视频,可以按照视频的方法来,这是用nirs-lab去做的数据处理。当然还有用homer3、mne-python、matlab的方式进行处理,在这就不介绍了。
b站视频链接:https://www.bilibili.com/video/BV1gE41147Wp?spm_id_from=333.999.0.0




部分参考资料

[1] 图片引自:https://blog.csdn.net/weixin_40052256/article/details/105610528?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165614829316782184654979%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=165614829316782184654979&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allbaidu_landing_v2~default-4-105610528-null-null.142v21pc_rank_34,157v15new_3&utm_term=fNIRS&spm=1018.2226.3001.4187

  • 9
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值