配置心理学实验时所需要的自变量是一个麻烦的问题,在这里,介绍一种简单快速配置实验变量的方法。这个方法确保了程序的简单、可读,减少了编程出bug的可能。
实验过程
呈现一个注视屏,上面有三个框,中间的框里有注视点,800ms
然后左边或右边的框中出现一个圆,圆的颜色是红、绿、蓝其中一种,200ms
接着继续呈现注视屏,400ms或700ms
再呈现目标刺激,是一个"*",被试需要立即按下'j'键。如果被试在"*"出现前按下按键,那么反馈"请看到*后再按键",如果被试超过1000ms都没有按键,那么反馈"请在*出现1秒内反馈"。反馈信息显示1000ms
最后呈现一个700ms的空屏
试次分为正常试次和探测试次。正常试次按以上流程,探测试次在呈现目标刺激阶段时,不呈现"*"。
自变量
color 线索颜色: 红、绿、蓝
cloc 线索位置: 左边 右边
soa :400ms 700ms
tloc 目标刺激: 左边 右边 不呈现
因变量
被试的按键:"j"
按键反应时
其中,反应时为-1000表示被试提前按键,按键为timeout表示被试超时。
实验设计
共360试次,其中80%正常试次,20%探测试次。在这两种试次中,自变量的各种情况都均匀分布。
需要记录的结果
被试id 试次 color cloc soa tloc key RT
编程技巧
把几个关键屏写成函数
ShowFixation1 | 显示第一个注视屏 | |
ShowCue | 显示线索屏幕 | |
ShowFixation2 | 显示第二个线索屏 需要检测是否提前按键 |
返回'timeout'说明被试没按键 返回'j'说明被试提前按键 |
ShowTarget | 显示目标刺激 | 返回('j', 789)表示被试正常按键 返回('timeout',0)表示被试超时 返回('q', 765)表示退出实验 |
ShowTimePre | 显示反应提前 | |
ShowTimeOut | 显示反应超时 | |
ShowBlank | 显示空屏 |
快速配置试次
from itertools import *
import random
colors = ['red', 'blue', 'green']
clocs = ['left', 'right']
soas = [0.4, 0.7]
tlocs = ['left', 'right'] # + [0]
trails = list(product(colors, clocs, soas, tlocs))* 12 +\
list(product(colors, clocs, soas, [0])) * 6
random.shuffle(trails)
itertools里的product会生成一个迭代器,这个迭代器会依次返回多个循环器集合的笛卡尔积,相当于嵌套循环。在外面套上list把结果转换成列表。
例如list(product(['a', 'b', 'c'],[1, 2])),会生成[('a', 1), ('a', 2), ('b', 1), ('b', 2), ('c', 1), ('c', 2)]
执行完上面的操作后,trails里就有 3*2*2*2*12+3*2*2*1*6=360个元素了。
使用:
color, cloc, soa, tloc = trails[i]
就可以把每个试次的自变量配置取出来。
使用字典保存详细配置
COLOR = {'red': [220, 0, 0],
'green': [0, 165, 0],
'blue': [18, 18, 255],
'white': [255, 255, 255],
'black': [0, 0, 0],
'gray': [127, 127, 127]
}
CLOC = {'left' : (-224, 0),
'right': (224, 0)
}
TLOC = {'left' : (-224, -20),
'right': (224, -20),
0 : 0
}
有时候我们会用嵌套的if和else来区分不同变量下的详细配置,其实这是没有必要的,使用字典可以灵活地解决这个问题。
比如设置线索的颜色和位置,可以这样写:
color, cloc, soa, tloc = trails[i]
circle = Circle(win, radius=16, edges=32, units = 'pix')
circle.setFillColor(colorSpace = 'rgb255', color = COLOR[color])
circle.setLineColor(colorSpace = 'rgb255', color = COLOR[color])
circle.setPos(CLOC[cloc])
保存结果
把每个试次的按键和反应时都保存到results列表里
results.append((rKey, RT))
最后通过StoreResult把结果保存到文件。
def StoreResult(name, N, trails, results):
fp = open(name + '.txt','w')
fp.write("ID\tnum\tcolor\tcloc\tsoa\ttloc\trKey\tRT\n")
def w(x):
fp.write(str(x) + '\t')
def n():
fp.write('\n')
def k(x): #trans 1s to 1000ms
return "%.0f" % (x * 1000)
for i in range(N):
color, cloc, soa, tloc = trails[i]
rKey, RT = results[i]
map(w, [name, i+1, color, cloc, k(soa), tloc, rKey, k(RT)])
n()
fp.close()
在这里,map函数的意思是,分别执行w(name);w(i+1);w(color) ......
程序文件
# -*- coding: utf-8 -*-
"""
Created on Tue Apr 12 10:31:15 2016
@author: zbg
"""
from psychopy.visual import Window, ImageStim, TextStim, BufferImageStim, Rect, Circle
from psychopy import core, event, gui, clock
import random
from itertools import *
#定义一些基本常量
fullscr = False
COLOR = {'red': [220, 0, 0],
'green': [0, 165, 0],
'blue': [18, 18, 255],
'white': [255, 255, 255],
'black': [0, 0, 0],
'gray': [127, 127, 127]
}
CLOC = {'left' : (-224, 0),
'right': (224, 0)
}
TLOC = {'left' : (-224, -20),
'right': (224, -20),
0 : 0
}
#准备实验参数与变量
N = 360
colors = ['red', 'blue', 'green']
clocs = ['left', 'right']
soas = [0.4, 0.7]
tlocs = ['left', 'right'] # + [0]
trails = list(product(colors, clocs, soas, tlocs))* 12 +\
list(product(colors, clocs, soas, [0])) * 6
random.shuffle(trails)
results = []
#程序使用的各种函数
def MakeStimBackGround(win):
t = TextStim(win, text = '+', pos = (0, 0), colorSpace = 'rgb255', color = COLOR['white'], units = 'pix', height = 32)
cb = Rect(win, width=64, height= 64, units = 'pix')
cb.setLineColor(colorSpace = 'rgb255', color = COLOR['black'])
cb.setPos((0, 0))
rb = Rect(win, width=64, height= 64, units = 'pix')
rb.setLineColor(colorSpace = 'rgb255', color = COLOR['black'])
rb.setPos((224, 0))
lb = Rect(win, width=64, height= 64, units = 'pix')
lb.setLineColor(colorSpace = 'rgb255', color = COLOR['black'])
lb.setPos((-224, 0))
stimBackground = BufferImageStim(win, stim = [t, lb, cb, rb])
return stimBackground
def MakeCircle(win):
circle = Circle(win, radius=16, edges=32, units = 'pix')
return circle
def GetSubject():
"""
返回被试的id
"""
myDlg = gui.Dlg(title="Subject Information")
myDlg.addField(u'被试ID:')
myDlg.show()
if myDlg.OK:
thisInfo = myDlg.data
else:
exit(0)
return thisInfo[0]
def WaitTheKey(win, key = 'space'):
event.clearEvents()
while key not in event.getKeys():
pass
def ShowTextAndWaitTheKey(win, text = '', wait = 0, key = 'space', pos=(0,-0.0), height = 55, units = "pix"):
t =TextStim(win, text ,pos = pos, height = height, units = units)
t.draw()
event.clearEvents()
clk = clock.CountdownTimer(wait)
win.flip()
while clk.getTime() > 0:
pass
while key not in event.getKeys():
pass
def ShowTextAndWait(win, text = '', wait = 0, pos=(0,-0.0), height = 55, units = "pix"):
t =TextStim(win, text ,pos = pos, height = height, units = units)
t.draw()
clk = clock.CountdownTimer(wait)
win.flip()
while clk.getTime() > 0:
pass
def ShowIntro(win):
introduce =u"""
指导语
按[空格键]继续
"""
ShowTextAndWaitTheKey(win, introduce)
def ShowFixation1(win, stimBackground):
clk = clock.CountdownTimer(0.8)
stimBackground.draw()
win.flip()
while clk.getTime() > 0:
pass
def ShowCue(win, color, loc, stimBackground, circle):
circle.setFillColor(colorSpace = 'rgb255', color = COLOR[color])
circle.setLineColor(colorSpace = 'rgb255', color = COLOR[color])
circle.setPos(CLOC[loc])
stimBackground.draw()
circle.draw()
clk = clock.CountdownTimer(0.2)
win.flip()
while clk.getTime() > 0:
pass
def ShowFixation2(win, soa, stimBackground):
'''
返回值有
('timeout') 这个是想要的
(按键) 这个表示提前按键
'''
event.clearEvents()
stimBackground.draw()
clk = clock.CountdownTimer(soa)
win.flip()
while clk.getTime() > 0:
pressedKeys = event.getKeys()
if len(pressedKeys) > 0:
return pressedKeys[0]
return 'timeout'
def ShowTarget(win, loc, stimBackground):
"""
返回(按键, 反应时(s))
情况分别有:
('f', 反应时)
('j', 反应时)
...
('q', 反应时) 通常表示退出
('timeout', 0)
"""
event.clearEvents()
if loc == 0:
stimBackground.draw()
else:
t = TextStim(win, text = '*', pos = TLOC[loc], colorSpace = 'rgb255', color = COLOR['black'], units = 'pix', height = 96)
stimBackground.draw()
t.draw()
clk = 0
clk = clock.CountdownTimer(1)
win.flip()
while clk.getTime() > 0:
pressedKeys = event.getKeys()
key = set(pressedKeys) & set(['q', 'j'])
if len(key) > 0:
return (key.pop(), 1 - clk.getTime())
return ('timeout', 0)
def ShowTimePre(win):
ShowTextAndWait(win, text = u'请看到*后再按键', wait = 1)
def ShowTimeOut(win):
ShowTextAndWait(win, text = u'请在*出现1秒内按键', wait = 1)
def ShowBlank(win, wait = 0.7):
ShowTextAndWait(win, wait = wait)
def ShowBreak(win):
ShowTextAndWaitTheKey(win, text = u"休息一下", key = 'space', wait = 10)
def ShowEnd(win):
introduce =u"""
结束了
按[空格键]退出
"""
ShowTextAndWaitTheKey(win, text = introduce)
def StoreResult(name, N, trails, results):
fp = open(name + '.txt','w')
fp.write("ID\tnum\tcolor\tcloc\tsoa\ttloc\trKey\tRT\n")
def w(x):
fp.write(str(x) + '\t')
def n():
fp.write('\n')
def k(x): #trans 1s to 1000ms
return "%.0f" % (x * 1000)
for i in range(N):
color, cloc, soa, tloc = trails[i]
rKey, RT = results[i]
map(w, [name, i+1, color, cloc, k(soa), tloc, rKey, k(RT)])
n()
fp.close()
#实验开始
name = GetSubject()
win = Window(fullscr = fullscr, colorSpace = 'rgb255', color = COLOR['gray'])
stimBackground = MakeStimBackGround(win)
circle = MakeCircle(win)
ShowIntro(win)
for i in range(N):
color, cloc, soa, tloc = trails[i]
RT = 0
rKey = 0
ShowFixation1(win, stimBackground)
ShowCue(win, color, cloc, stimBackground, circle)
key = ShowFixation2(win, soa, stimBackground)
if key != 'timeout':
ShowTimePre(win)
RT = -1
rKey = 0
else:
(rKey, RT) = ShowTarget(win, tloc, stimBackground)
if tloc == 0 and rKey != 'timeout':
ShowTimePre(win)
elif tloc !=0 and rKey == 'timeout':
ShowTimeOut(win)
else:
pass
ShowBlank(win, 0.7)
if rKey == 'q':
StoreResult('exp-' + name, i, trails, results)
win.close()
exit(0)
results.append((rKey, RT))
if i % 60 == 59:
ShowBreak(win)
StoreResult('exp-' + name, N, trails, results)
ShowEnd(win)
win.close()
引用和转载请注明本文链接,谢谢!
psychopy 实验程序购买 https://item.taobao.com/item.htm?spm=a230r.1.14.6.Q6E2OW&id=530690095131&ns=1&abbucket=15#detail
来源:oschina
链接:https://my.oschina.net/u/1387924/blog/683526