用Python的PsychoPy库复刻n-back游戏:一个心理学实验的完整编程实现
·
用Python的PsychoPy库构建n-back游戏:从零实现心理学实验编程
在认知心理学领域,n-back任务被广泛用于研究工作记忆能力。这个看似简单的实验范式——要求被试判断当前刺激是否与前面第n个刺激相同——却蕴含着丰富的认知神经机制。对于技术开发者而言,用代码精确复现这类实验不仅能够加深对心理学原理的理解,更能掌握实验编程的核心方法论。
PsychoPy作为开源的心理学实验构建工具,凭借其精确的刺激呈现和时间控制能力,已成为学术研究中的标准工具之一。本文将完整展示如何用Python和PsychoPy库从零构建一个空间n-back游戏,重点解决实验编程中的关键技术难点。
1. 实验环境搭建与PsychoPy基础
1.1 安装与初始化
首先确保Python环境(建议3.7+)已配置,通过pip安装必要库:
pip install psychopy pandas openpyxl
PsychoPy的核心是 Window 类,它创建实验的主窗口。初始化时应考虑显示器的刷新率和物理尺寸:
from psychopy import visual, event, core
import random
import pandas as pd
# 创建窗口(单位使用像素更易控制UI元素)
win = visual.Window(
size=(1000, 618),
color='white',
units='pix',
fullscr=False # 调试时建议关闭全屏
)
1.2 刺激材料设计
n-back实验通常使用两种视觉刺激:
- 目标刺激 :需要被试关注的元素(如绿色方块)
- 背景刺激 :作为参照的静态元素(如灰色九宫格)
# 九宫格位置参数(以屏幕中心为原点)
positions = [
(-150, 150), (0, 150), (150, 150),
(-150, 0), (0, 0), (150, 0),
(-150, -150),(0, -150),(150, -150)
]
cube_size = 145 # 像素单位
# 创建刺激元素模板
gray_cube = visual.Rect(win, width=cube_size, height=cube_size, fillColor='#afafaf')
green_cube = visual.Rect(win, width=cube_size, height=cube_size, fillColor='green')
2. 实验流程实现
2.1 随机序列生成
n-back实验的关键是生成符合认知负荷要求的刺激序列。需要避免连续出现相同位置,同时控制n-back匹配的概率:
def generate_trial_sequence(length=30, n_back=2, match_prob=0.3):
sequence = []
match_indices = []
# 生成基础序列(保证相邻不重复)
while len(sequence) < length:
pos = random.randint(0, 8)
if not sequence or pos != sequence[-1]:
sequence.append(pos)
# 设置匹配项
for i in range(n_back, length):
if random.random() < match_prob:
sequence[i] = sequence[i - n_back]
match_indices.append(i)
return sequence, match_indices
2.2 实验主循环
实验流程需要精确控制刺激呈现时间、反应收集和数据记录:
def run_experiment(n_levels=[1, 2], trials_per_block=20):
data = {
'block': [],
'trial': [],
'n_level': [],
'position': [],
'response': [],
'correct': [],
'rt': [] # 反应时(毫秒)
}
for block, n in enumerate(n_levels):
sequence, matches = generate_trial_sequence(trials_per_block, n)
for trial, pos in enumerate(sequence):
# 绘制背景
for i in range(9):
gray_cube.pos = positions[i]
gray_cube.draw()
# 绘制目标刺激
green_cube.pos = positions[pos]
green_cube.draw()
win.flip()
# 记录反应
clock = core.Clock()
keys = event.waitKeys(
keyList=[str(i+1) for i in range(9)] + ['escape'],
timeStamped=clock
)
# 数据记录
if keys and keys[0][0] == 'escape':
return pd.DataFrame(data)
response = int(keys[0][0])-1 if keys else None
is_correct = (trial >= n and response == sequence[trial - n]) if response is not None else False
data['block'].append(block)
data['trial'].append(trial)
data['n_level'].append(n)
data['position'].append(pos)
data['response'].append(response)
data['correct'].append(is_correct)
data['rt'].append(keys[0][1]*1000 if keys else None)
return pd.DataFrame(data)
3. 数据管理与分析
3.1 实时数据存储
使用pandas库可以方便地处理实验数据,并支持多种格式导出:
def save_data(df, filename='nback_data.xlsx'):
try:
existing = pd.read_excel(filename)
updated = pd.concat([existing, df], ignore_index=True)
updated.to_excel(filename, index=False)
except FileNotFoundError:
df.to_excel(filename, index=False)
3.2 数据质量检查
实验后应立即进行基本数据验证:
def validate_data(df):
# 检查缺失值
missing = df.isnull().sum()
# 计算准确率
accuracy = df.groupby('n_level')['correct'].mean()
# 反应时分布
rt_stats = df.groupby('n_level')['rt'].agg(['mean', 'std'])
return {
'missing_data': missing,
'accuracy': accuracy,
'reaction_time': rt_stats
}
4. 高级实现技巧与调试
4.1 时间精度优化
PsychoPy提供多种时间控制方式,适用于不同精度要求:
| 方法 | 精度 | 适用场景 | 代码示例 |
|---|---|---|---|
| core.wait() | 中等 | 简单等待 | core.wait(1.0) |
| Clock计时器 | 高 | 反应时测量 | clock = core.Clock() |
| CountdownTimer | 高 | 固定时长 | timer = core.CountdownTimer(5.0) |
4.2 常见问题解决
问题1:刺激呈现不同步
- 原因:未考虑显示器刷新周期
- 解决方案:
win.flip() next_flip = win.getFutureFlipTime(clock='ptb')
问题2:按键响应延迟
- 优化方案:
event.globalKeys.add('escape', func=cleanup) # 全局热键
问题3:随机序列偏差
- 改进方法:
from psychopy.tools import sequenceutils seq = sequenceutils.createBalancedSequence(repeats=5, nConditions=9)
5. 实验扩展与个性化定制
5.1 多模态刺激支持
PsychoPy支持丰富的刺激类型,可以轻松扩展实验范式:
# 声音刺激
from psychopy import sound
tone = sound.Sound('A', octave=4, sampleRate=44100)
# 图像刺激
face_img = visual.ImageStim(win, image='face.jpg', size=(200,200))
5.2 动态难度调整
根据被试表现实时调整n值:
def adaptive_n_back(df, window_size=5):
recent = df.tail(window_size)
accuracy = recent['correct'].mean()
if accuracy > 0.8:
return min(df['n_level'].max() + 1, 5)
elif accuracy < 0.6:
return max(df['n_level'].min() - 1, 1)
else:
return df['n_level'].iloc[-1]
在实际项目中,n-back实验的精确实现需要考虑显示器参数校准、响应设备选择和实验伦理审查等更多因素。PsychoPy的强大之处在于它提供了从实验设计到数据收集的完整解决方案,而Python生态则让后续的数据分析工作变得异常高效。
更多推荐


所有评论(0)