flanker范式eprime_flanker范式图解[通俗易懂]

flanker范式eprime_flanker范式图解[通俗易懂]Psychopy|第3期:从flanker范式看完整的程序1相关概念的简单引入2各个部分的详细讲解3完整程序代码及结果展示4系列课程的总结Hello,这里是行上行下,我是喵君姐姐~最近在家实在无

img
img
Hello,
这里是行上行下,我是喵君姐姐~

最近在家实在无聊,所以只好安安心心学习啦。你最近在家干什么呢?

今天,继续邀请阿槑给你带来Psychopy系列教程,带来从flanker范式看完整的程序,希望你会继续喜欢并且一直支持哟~

1 相关概念的简单引入

从今天开始,我将使用2期左右带大家完整的完成一个心理学行为实验程序。并且在这个过程中能够让大家对 psychopy 有一个比较好的了解。

本期我们先来看有关刺激呈现的相关知识。
img


flanker范式任务最终呈现

对于 Python 来说,其功能的实现是由一个一个的模块(Module)来进行的。所谓模块,是前人为了实现某些功能而编写的一段代码,其中包括了我们实现功能所需要的东西。通过引用,相应的功能得以在我们的程序中实现。

当我们需要在程序中使用某一个模块时,我们一般使用 import <模块名> 来进行导入,而对于psychopy,我们使用from psychopy import <模块名> 来进行导入。与刺激呈现有关的是 psychopy 中的 visual 模块,那我们需要在开头编写:

from psychopy import visual

所需要的“工具”准备好以后我们先来回顾一下常用的flanker范式(Eriksen & Eriksen, 1974)的呈现过程,如图:
img
这是一个最简单常用的 flanker 范式的过程(未使用原文献中的字母刺激),我们就以此为例来看一下如何利用psychopy 实现 flanker 范式的呈现。

2 各个部分的详细讲解

我们要想进行刺激的呈现,首先要建立一个窗口。

根据流程图,我们建立一个以中灰为背景色的,1024 * 768 像素大小的窗口,代码如下:

from psychopy import visual  
Win = visual.Window((1024,768), color=(128, 128, 128), fullscr=False, units='pix',colorSpace='rgb255')   

我们使用 visual 模块中的 Window 方法进行窗口的定义以及相关参数的设置。该方法的参数如下:
img
其中,第二个参数是窗口的背景色,我们使用在 psychopy 中被定义为 ‘rgb255’ 的方式进行编写,这种方式将光学三原色(红绿蓝)以0–255表达出来,其中当红绿蓝三成分均为128时可以得到中灰,而 (255,255,255) 为白色,(0,0,0) 为黑色。使用这种方法时,参数colorSpace需要设置为 ‘rgb255’。

另外,第三个参数 fullscr 控制是否全屏显示,在日常的实验编写过程中建议保持非全屏False,这样如果编写过程中出现错误可以方便退出;而在正式实验的时候可以将其改为 True。

到此,我们设置出的窗口是一个 1024*768 的,单位为像素的,且中心坐标为(0,0)的窗口,如图:
img
设置完窗口以后,我们继续设置所需要的刺激。

首先,对于注视点,我们使用 visual 模块中的TextStim 方法,这种方法主要对文字刺激进行编写,而注视点可以使用文字“+”来替代;而对于我们需要的箭头,由于不同的试次有所不同,因此在后面我们进行与试次有关的设置时我们再进行编写。除了注视点,这里我们可以首先将结束语编写出来,代码如下:

 # -*- coding: utf-8 -*- 
 from psychopy import visual      
 Win = visual.Window((1024,768), color=(128,128,128), fullscr=False, units='pix',colorSpace='rgb255')     
 fix = visual.TextStim(Win, text='+', color='black', height=50,bold=True)
 endPrompt = visual.TextStim(Win, text='实验结束,谢谢!', color='black', height=60)  

各个参数的解析如下(以注视点为例):
img
结束语与其相类似。

其中,这里展示了另一种颜色的编写方式,即直接使用颜色的对应单词来进行编写。 这种方式虽然比较简单,但是如果需要编写的实验对颜色的精确性要求很高,则还是建议使用’rgb255’的方式进行编写。

同时,由于结束语是中文文本,因此需要进行文件编码类型的转化,因此代码开头加了

# -- coding: utf-8 --

这一小段特殊注释建议在编写python程序的过程中都在开头处加上。

目前,我们把除了反应屏以外的其他刺激都编写完成,下面需要对本范式中最复杂的部分进行编写。

首先,一般的 flanker 范式有两个自变量,即2(两侧:左,右)×2(中央:一致,不一致)实验设计,我们首先把这四种情况定义出来,并将其顺序打乱:

 import random   
 var = []  #建立自变量空列表 
 for flanker in ['left', 'right']:       
 	for center in ['same','diff']:           
 		var.append([flanker, center])  #在列表中加入相应list元素 
 random.shuffle(var)  #列表随机

这里打乱的方法我们使用 random 模块中的 shuffle() 方法,这种方法可以将列表中的所有元素以随机方式排列,此时由于我们使用了一个新的模块,需要在开头对应地将该模块进行 import。

自此,我们得到了一个具有四个元素的列表,其中每个元素又是一个两个元素的列表:
img
之后,我们对该屏的箭头位置进行定义。

同样以列表的方式进行:

 sites = []  #建立箭头位置空列表 
 for site in range(5):       
 	sites.append((-100+50*site,0))  #添加5个位置

这里我们用到了 range() 方法,该方法所实现的是一个整数的迭代,当其中只有一个参数时,与 for 循环相配合可以将从0开始的整数不停赋值给变量,赋值次数即为该整数,每次赋值都会在前一次的基础上加1。

如,例子中,该 for 循环会连续给变量 site 赋值 0,1,2,3,4,虽然看起来与列表有些类似,但是这种方法比列表所需要的内存更小

通过这种方式我们获取了具有5个元组的一个列表,即:
img
到此,与试次无关的变量基本设置完成,我们来进入每个试次的循环。在试次循环中,我们首先定义完整的反应屏5个箭头的刺激,代码如下:

 stims = []  #定义刺激空列表 
 for stim in range(5):        
 
 	if (trial[0] == 'left' and trial[1] == 'diff' and stim == 2) or\
    (trial[0] == 'right' and trial[1] == 'diff' and stim != 2) or\
    (trial[0] == 'right' and trial[1] == 'same'):          
		Horiz = True  #需要进行翻转的情况 
    else:           
    	Horiz = False  #不需要进行翻转的情况 
    arr = visual.TextStim(Win, text='←', color='black', height=50, pos=(-200+100*stim,0),flipHoriz=Horiz,bold=True)       
	stims.append(arr)  #在列表中加入定义好的箭头

我们给每个试次赋值列表var中的元素,即每个试次对应的水平类型,如 [‘right’, ‘same’],此时对于每个 trial 来说,其类型均为 list,并且该 list 中 0 号位置可代表两侧箭头的朝向,而 1 号位置可以规定出中间箭头的朝向。

如果我们假定箭头的初始位置为向左,那么需要进行水平翻转的有以下三种情况

Ø 两侧箭头向左,中央箭头与两侧不同时,中央箭头需要翻转

Ø 两侧箭头向右,中央箭头与两侧不同时,两侧的四个箭头需要翻转

Ø 两侧箭头向右,中央箭头与两侧相同时,所有箭头翻转

转化为程序语言即为:

 (trial[0] == 'left' and trial[1] == 'diff' and stim == 2) or\   
 (trial[0] == 'right' and trial[1] == 'diff' and stim != 2) or\   
 (trial[0] == 'right' and trial[1] == 'same') 

我们通过设置变量 Horiz 来限定箭头是否翻转:

 stims = []  #定义刺激空列表 
 for stim in range(5):        
 	if (trial[0] == 'left' and trial[1] == 'diff' and stim == 2) or\       
 	(trial[0] == 'right' and trial[1] == 'diff' and stim != 2) or\       
 	(trial[0] == 'right' and trial[1] == 'same'):           
 		Horiz = True  #需要进行翻转的情况 
 	else:           
 		Horiz = False  #不需要进行翻转的情况

之后,我们同样通过文本刺激 TextStim 来定义箭头刺激,并重复5次以定义5个箭头并放入列表 stims,参数解析如下:
img
其中, 运用参数flipHoriz 时需要注意,这一翻转是在原始状态进行翻转,而不是当前状态(The flip is relative to the original, not relative to the current state)。也就是说,如果对一个刺激进行两次翻转,其与进行一次翻转的效果相同。

到此我们所有的刺激都已定义完成,下一步我们需要把定义好的刺激呈现在屏幕上。

如果我们要呈现刺激,首先要知道这个刺激我们想要呈现的时间以及如何规定呈现时间。虽然 python 中有进行时间相关功能的模块 time,但是我们这里使用一个更为简便的方法来控制时间。

对于我们的计算机屏幕来说,其呈现画面是通过不停地刷新来进行的,刷新的频率我们称为刷新率(单位:赫兹,Hz)。

一般来说,我们普通的笔记本电脑的刷新率为60Hz,实验室等比较专业的屏幕刷新率可以达到100Hz。我们可以通过控制电脑屏幕的刷新次数来控制某个刺激的刷新时间。

在 psychopy 中,我们可以将电脑理解为有两个“屏幕”。一个是我们平时看到的屏幕,即“前屏”,它主要是对我们设置的刺激进行呈现;还有一个是虚拟的“后屏”,即电脑对刺激进行绘制的屏幕。

一个刺激的呈现有两个步骤:电脑先在“后屏”上绘制(draw())出我们想要的刺激,之后再通过刷新(flip())将其呈现到屏幕上。我们不断重复这个步骤,刺激就会不停地呈现出来,当我们规定刷新的次数,那么也就规定出了刷新的时间。

例如,如果我们想要设置的注视点呈现 300ms,那么我们只需要计算出 300ms需要刷新多少次即可。空屏 500ms的呈现时间同理。计算方法如下:

Rate = 60  #电脑刷新率 
Dura = 1000/Rate   
fix_Dura = 300  #注视点呈现时间 
blank_Dura = 500  #空屏呈现时间 
fix_times = int(round(fix_Dura/Dura))  #注视点刷次数 
blank_times = int(round(blank_Dura/Dura))  #空屏刷新次数

我们将 Rate 设置为 60 来指代屏幕的刷新率,即Hz,而 Hz 的实质为 次/秒,1/Rate 即为 秒/次 ,Dura为 1000*(1/Rate) 即为 毫秒/次,我们设置的呈现时间为 300ms,将其除以 毫秒/次,便可得出 300ms 对应的刷新次数。

之后通过 round() 进行四舍五入取整,再使用 int() 的确保其为整型,我们可以得到最终的刷新次数 fix_times,这里的刷新次数回到循环外进行定义即可。

当我们有了刷新次数,我们可以通过 for 循环来把一次次地刷新实现出来,从而把单个刺激呈现在屏幕上:

 for frame in range(fix_times):       
 	fix.draw()  #绘制 
 	Win.flip()  #翻转(刷新)

对于反应屏,我们需要刺激一直呈现直到被试做出判断(左 或 右)后消失,因此可以通过 while 循环并加上 psychopy 中的 event.getKeys() 来实现,同时需要在开头加上 from psychopy import event,代码如下:

 from psychopy import event   
 while True:       
 	[s.draw() for s in stims]       
 	Win.flip()       
 	if len(event.getKeys(['left','right'])) > 0: break  

我们在这里通过列表生成式对多个刺激进行****同时的呈现,所谓列表生成式,是指一种比一般 for 循环更加简便的表达形式,例如:

 [s.draw() for s in stims]

等价于:

 for s in stims:       s.draw()  

这里 stims 是我们规定好的有5个箭头的列表,因此可以同时将其呈现出来。

我们最后一个屏是需要空屏,则不需要draw(),直接flip()即可:

for frame in range(blank_times):      
	Win.flip()  

**这也是最为常见的三种呈现类型,**即单一刺激的呈现、多刺激的同时呈现、呈现空屏。

当所有试次进行完成,我们需要呈现结束语,并且被试按任意键退出,那么我们来定义一个变量 a 对试次数进行监控,当 a 与我们水平个数相等时,呈现结束语,并且被试按任意键后,程序结束,关闭窗口:

a = 0  
for trial in var:      
	if a==len(var):  #判断本试次是否为最终试次 
		while True:              
			endPrompt.draw()              
			Win.flip()              
			if len(event.getKeys()) > 0: break          
		Win.close()  #关闭对话框

3 完整程序代码及结果展示

整个程序完整的代码如下:

# -*- coding: utf-8 -*- 
from psychopy import visual, event  
import random    
Win = visual.Window((1024,768), color=(128,128,128), fullscr=False, units='pix',colorSpace='rgb255')    
fix = visual.TextStim(Win, text='+', color='black', height=50,bold=True)  
endPrompt = visual.TextStim(Win, text='实验结束,谢谢!', color='black', height=60)    

Rate = 60  
Dura = 1000/Rate  
fix_Dura = 300  
blank_Dura = 500  
fix_times = int(round(fix_Dura/Dura))  
blank_times = int(round(blank_Dura/Dura))    

var = []  
for flanker in ['left', 'right']:      
	for center in ['same','diff']:          
		var.append([flanker, center])  
random.shuffle(var)    

sites = []  
for site in range(5):      
	sites.append((-100+50*site,0))    

a = 0  
for trial in var:      
	a+=1            

	stims = []      
	for stim in range(5):                    
		if (trial[0] == 'left' and trial[1] == 'diff' and stim == 2) or\          
		(trial[0] == 'right' and trial[1] == 'diff' and stim != 2) or\          
		(trial[0] == 'right' and trial[1] == 'same'):              
			Horiz = True          
		else:              
			Horiz = False                        

		arr = visual.TextStim(Win, text='←', color='black', height=50, pos=(-200+100*stim,0),flipHoriz=Horiz, bold=True)          
		stims.append(arr)        
	
	for frame in range(fix_times):          
		fix.draw()          
		Win.flip()                
	
	while True:          
		[s.draw() for s in stims]          
		Win.flip()          
		if len(event.getKeys(['left','right'])) > 0: break                
	
	for frame in range(blank_times):          
		Win.flip()                
	
	if a==len(var):          
		while True:              
			endPrompt.draw()              
			Win.flip()              
			if len(event.getKeys()) > 0: break          
		Win.close()  

执行后效果如下:
img
本期,主要给大家介绍了有关 psychopy.visual 的一些内容以及一小部分 psychopy.event 和 random 模块的内容。

下期,我们将会继续以这个范式为模板,介绍一下关于如何收集数据等相关的操作。希望大家可以关注“行上行下”公众号,持续关注哟~

4 系列课程的总结

至此,我们已经学习了Psychopy入门、数据类型与运算符、条件与循环、flanker范式的完整编程。

基本学完了 Python 在 Psychopy 中需要用到的大多数知识,虽然难度不是很大,但是比较繁杂,建议通过练习以熟悉这些基本的语句和方法。

PS:文章首发于微信公众号行上行下,后台回复关键词“psychopy第3期”即可获得所述的资料及代码啦!

作者:阿槑

排版:喵君姐姐

参考文献:

Eriksen, B. A., & Eriksen, C. W. (1974). Effects of noise letters upon the identification of a target letter in a nonsearch task. Perception & Psychophysics, 16(1), 143-149.

今天的文章flanker范式eprime_flanker范式图解[通俗易懂]分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/89488.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注