前言

容我想想要从哪开始写啊……

前一阵子因为各种机缘巧合,接触到了 PsychoPy 这个工具,而且真用它完整做完了一整套从设计到分析数据出结论的实验流程。用完之后的感受是:比 EPrime 高到不知道哪里去了.jpg

在这之后,又因为一些其他的机缘巧合,就有了写这篇指南的想法,希望能帮助一些后来的朋友了解、使用 PsychoPy。

所以,这篇文章意在使 0 基础的朋友快速了解、上手使用 PsychoPy,并掌握使用 PsychoPy 完整进行一次实验的能力。

开始吧!

0. 什么是 PsychoPy?

能吃吗?怎么吃?好不好吃?

当然在具体进入教程之前,首先要介绍一下这个工具本身。

PsychoPy 是一款基于 Python 开发的心理学工具包,旨在提供一套足够强大、现代化的工具,帮助各界心理学者设计、开展实验。

略去开发者啊,开发历程之类的东西,直接跳到“PsychoPy 的特殊之处”,也就是为什么我们会选择 PsychoPy 的问题上:

  1. 它的能力足够强大:从简单的文本、图像到视频、量表;从静止刺激到动态刺激,从普通的按键反应到滑动条、输入框等交互式程序,甚至还支持脑电眼动仪等复杂实验仪器;

    PsychoPy 所有的刺激与输入

  2. 它上手足够简单:对普通用户,提供现代化的图形设计界面,实验流程一目了然、简单明了;对高级开发者,也提供了代码编辑界面,可轻松修改程序;

    PsychoPy 图形化界面一览

如果只有这两个优势的话,PsychoPy 只是一个大号的、现代化的 E-Prime (其实本来也是),真正让它不同于 E-Prime 的是下面三点:

  1. 它具有良好的跨平台兼容性与可迁移性:底层架构基于 Python,可以在任何搭载了 Python 的平台上使用、运行,不会遇到奇奇怪怪的兼容性问题 (反观 E-Prime 程序,随便换台电脑就运行不起来了)

    PsychoPy 程序代码节选,可以在任何安装了 Python 3 与 PsychoPy 依赖库的环境下运行

  2. 它拥有运行线上实验的能力:PsychoPy 可以将程序转译为 Javascript 脚本,从而可以在浏览器中在线运行实验程序;

    在浏览器中运行 PsychoPy 程序

  3. 它是开源的自由程序:无需任何门槛与费用,任何人均可使用;如有需要,任何人均可直接修改其底层代码;

    PsychoPy 活跃的开发社区

与其他目前常用且流行的心理学实验开发工具进行比较的话,大概是这样:

工具名上手难度可迁移性自由度在线实验
MATLAB高(纯代码环境)中(需配置环境)难以实现
E-Prime中 (可视化编辑器+代码编辑器)低(换个机器就炸了)几乎无法实现
Inquisit高(纯代码环境)高(仅需浏览器插件)完全支持
jsPsych高(纯代码环境)高(基于网页开发)完全支持
PsychoPy中(可视化编辑器+代码编辑器)高(需配置环境)完全支持

当然,上面这张表取决于我的主观经验,可能有失偏颇,大致做个参考。

总而言之,如果你满足以下的条件的话:

  • 想要使用相对简单的办法制作实验程序
  • 想要使用较为轻量的实验环境 (MATLAB 实在是太大了)
  • 想要实现跨平台的线上实验
  • 不希望与底层代码结构打太多交道

我会建议你尝试 PsychoPy。

1. 安装并配置 PsychoPy

那么,这么好的东西,在哪里才能买得到呢?

既然到了这里,可能得先说:

本文无意成为一篇全面详尽的 PsychoPy 指南,本文作者也并不具有这样的实力与能力。

本文希望做到的是能让阅读者快速了解这一工具,可以在较短时间内掌握其使用方法。

如在开发、使用过程中遇到问题,官方文档与搜索引擎是更好的资料来源。

在最开始,先介绍几个 PsychoPy 开发过程中最重要的网站:

首先前往 PsychoPy 项目的 GitHub 发布页(https://github.com/psychopy/psychopy/releases),根据版本与系统下载对应的安装包(通常 400-700 MB 不等)。

下载页面导航

如果希望对接国内脑岛平台(https://naodao.com/)进行在线实验,推荐下载 2021.2.3 版本,该版本为脑岛支持的最高版本。

如果无此类需求,仅在本地或自建网站开展实验,则推荐下载带有 latest标签的最新版(图上为 2022.2.2)。

我反正两个版本都装了, 如果同时安装多个版本的 PsychoPy,请注意区分安装文件夹。

Windows 与 MacOS 环境下,PsychoPy 安装时会自动安装独立的 Python 环境,因此无需额外配置,安装完成后开箱即用。如果是 Linux 用户,请参考官方文档(https://www.psychopy.org/download.html)进行安装与配置。

安装完毕,打开 PsychoPy,你会看到如下的界面:

PsychoPy Builder 启动界面

2. 认识 PsychoPy——界面篇

这是啥?那是啥?我是啥?

随着这个窗口出现的应该还有两个窗口。这三个窗口分别是:

  • PsychoPy Builder:图形化编辑界面,之后要讲的大部分的操作都会在这里完成;
  • PsychoPy Coder:代码编辑界面,有需要时,可以在这里加载代码,进行更加直接的修改;
  • PsychoPy Runner:程序运行器,在本地运行程序时,会通过它记录程序中发生的所有事件,并输出日志。

PsychoPy 三兄弟

我们绝大多数操作都会在 Builder 里完成,所以我们先把目光看向 Builder。

PsychoPy Builder 窗口界面

  • ① 为操作栏,和其他软件里顶上的按钮类似,这些都是针对文件本身的操作,包括新建、保存、调整设置、运行等功能;
  • ② 为 Routines 面板,在这里会显示当前 Routine 的所有组件,在后面的章节中会详细介绍;
  • ③ 为 Components 面板,这里列出了 PsychoPy 里可用的所有组件,点击组件即可将其添加到左边的 Routines 面板;
  • ④ 为 Flow 面板,这里会显示当前实验的整个流程。

整个界面非常简洁,相信很多人看到这里就已经知道 PsychoPy 大致的编程流程了,因为界面很直观嘛。

从下面开始,就会涉及到 PsychoPy 编程的核心概念了。

3. 认识 PsychoPy——Routine 篇

道理我都懂,但是我还是不会啊。

从界面上可以看出,PsychoPy 程序是一个“组块式”进行的结构。不同的组块按照时间顺序安排,并在某些条件下循环出现,以此构成整个实验的流程。

一个 PsychoPy 程序的局部流程图

其中,图上的每一个彩色小方块就是一个 Routine,而右边灰色的practice则代表了一处循环(Loop)。

Routine 和 Loop 是构建 PsychoPy 程序的核心概念,所有的实验程序设计都围绕着安排 Routine 和 Loop 展开。

先来认识一下 Routine。来做个小实验吧!有条件的话可以打开 PsychoPy,跟着一起做。

实验一:简单认识 Routine

制作一个简单的测试程序。

运行程序后,在第一个页面显示“Hello, world!”。

按空格进入第二个页面,在第二个页面显示“Welcome to psychology!”。

程序已经默认提供了一个 Routine,叫做trial,我们可以就把它作为第一个页面。

首先,向trial中添加一个文本组件。在右边的 Stimuli 菜单中找到 Text,点击它,会弹出一个设置面板:

文本组件的各项属性

  • Name(变量名):组件的名字,和其他编程工具一样,只能以字母开头,只能包含字母、数字、下划线,并且整个程序中不能重复;
  • Start(开始时间):该组件从什么时候开始加载,默认为 0(Routine 开始时加载);
  • Stop(结束时间):该组件持续多少时间,默认为 1(刺激持续 1 秒),留空表示不结束;
  • Text(文本内容):文本组件的内容。

另外,Expected start 和 Expected duration 表示你预估的开始时间和持续时间,填写后对程序运行没有影响,可以作为笔记使用。

将 Name 设置为 hello,Stop 一栏留空(不填写任何内容),将 Text 中的内容改为Hello, world!,点击 OK,一个名叫 hello 的组件就会出现在你的界面上:

hello!

接下来,我们添加第二个 routine。点击 Insert Routine 按钮,选择(new),输入welcome作为名称,就新建了一个名为 welcome 的 routine。

welcome!

但是,现在它并没有出现在我们的 Flow 面板中。你还需要手动指定它的位置(也就是图上的灰点,会跟随你的鼠标移动)。

welcome?

trial的后面单击它,它就被插入到了trial后面。和前面一样,我们设置一个文本组件,就不重复展示了。

做好的 welcome 应该长这样

接下来,找个地方保存一下你的实验文件(控制栏里的 ? Save current experiment file 按钮),然后点击 ▶ Run experiment,程序跑起来了!

填写被试信息

Hello, world!

……然后你发现不管怎么按,你都没法进入下一页。恭喜你,被我坑了(

很简单,因为我们压根还没设置键盘反馈,程序不知道我们按了空格键。

按 ESC 强制退出实验程序,回到 Builder,往trial里面新增一个 Keyboard 组件(在 Responses 分类里)。

键盘组件属性

  • Name、Start、Stop:同理,略;
  • Force end of Routine(强制 Routine 结束):默认勾选,当接收到符合条件的反应时结束 routine,进入下一步;
  • Allowed keys(允许的按键):仅接收在此处定义的按键,其他按键会被忽略。

在 Allowed keys 一栏,前面带有一个固定的$符号。

在 PsychoPy 里,$符号代表其后续的内容为 Python 语法,程序将使用 Python 进行解析。可以看到,这里的按键定义是字符串的形式。

后续还会用到它,此处仅作讲解。

同样,将 Stop 一栏清空,将 Allowed keys 改成只剩下'space'一项,然后单击 OK。

加上键盘组件之后的 trial

你肯定猜到了,为了正常结束程序,在welcome里面也需要一个键盘组件,如法炮制即可。

那么,再次运行程序,这次就应该一切正常了——

Welcome to psychology!

按两下空格,程序就应该正常退出了。

恭喜你,写完了第一个 PsychoPy 程序!

4. 认识 PsychoPy——文件篇

菜做好了,盘子呢?

刚搞了这么多,先休息一下。不妨趁现在打开你保存实验程序的文件夹,看看里面都有什么:

实验程序文件夹

当然我忘了给实验文件取名字了,所以这里是 untitled(未命名)。目前,这个文件夹里有三个东西:

  • untitled.psyexp 文件:PsychoPy 的程序文件,也就是你刚刚编辑的东西;
  • untitled.lastrun.py 文件:刚刚运行实验时,PsychoPy 编译的 Python 脚本代码,每次运行都会更新一次;
  • data 文件夹:储存了所有运行时的数据,包括我们刚刚试运行的那几次;

点开 data 文件夹,会出现三类文件:

默认状态下,PsychoPy 生成的程序文件

  • .csv 文件:大家喜闻乐见的表格文件(或者更严谨地说,“逗号分隔符文件”),保存了实验过程中产生的各种变量和对应的值;
  • .log 文件:日志文件,记录了程序运行期间发生的所有事件;
  • .psydat 文件:PsychoPy 独有的数据文件,将实验程序和其中的内容直接作为 Python 对象保存下来,最大限度地保留了原始数据。

当然,我这里调整过设置,各位可能会有其他文件出现,具体的设置可以在 Builder 窗口里点击 ⚙️ Edit experiment settings 按钮,选择 Data 查看:

储存数据设置

一般情况下,这三个文件就足够我们使用了。

以上是在本地使用 Python 环境运行程序时会产生的数据文件。

如果要在线上进行实验,情况会变得完全不同,线上实验环境下程序只会生成 .csv 文件,后续将会详细说明。

同时,.psydat 是一个需要使用 Python 才能解析的文件。根据官方文档,一般在“常规数据文件没有保存所有数据(通常是忘了设置)”的情况下才会用到它。读取这个文件的方法请参考:https://www.psychopy.org/general/dataOutputs.html#psychopy-data-file-psydat

打开最新的那个 .csv 文件,然后……

这都是些啥啊.jpg

在数据文件中,每一行对应了实验中的一个 Routine;这一行里的数据则表明了“在这个 Routine 里发生了什么”。

这里的每一个变量都单独成列,比如hello.started的意思是名为hello的组件在什么时候加载成功,而key_resp.rt则是名为key_resp的组件记录下的反应时。大致知道就行了,在这里还不需要详细了解。

而文件后面的participant session date expName psychoPyVersion frameRate这些变量就是实验的元数据,记录了被试、被试编号、实验日期、实验名、程序版本、显示器帧率等数据。

还是挺直观的,不是吗?

5. 认识 PsychoPy——Loop 篇

门前大桥下,游过一群鸭,快来快来数一数,二四六七八?

来做一点比较像真正的心理学实验的东西吧!

实验二:简单认识 Loop

创建一个程序,包含一个指导语页面、十个试次、一个结束语页面。

在每个试次里,屏幕上首先呈现 200 毫秒注视点,随后空屏 300 毫秒,再出现字母 A,被试需要在出现 A 之后按键盘上的 A 进入下一个试次。

相信大家也都知道我想介绍什么了,这里就讲得简略一点了。我们首先创建三个 Routine,分别是IntrotrialEnding

随后,我们在 IntroEnding中加入一些指导语,并设置好按键反馈。

相信你也注意到了,因为变量名不能重复的关系,如果不加以控制,整个程序的变量名就会非常混乱。

又因为变量名直接关系到我们进行数据分析时的质量,因此我建议起名时起一些易于分辨的名称。

以我自己的方法举例,假如我的 routine 叫Intro,那么我就会把里面的文本称作Intro_text,把键盘组件称作Intro_key_resp,这样既可以知道组件是什么,也知道组件对应的 routine 是什么。

接下来我们看向trial,在里面设置注视点、空屏和刺激:

trial 的示例设置,注意 Routines 面板中的时间安排

注视点trial_fixationtrial开始时呈现,持续 0.2 秒;刺激条件trial_stimuli与按键反馈trial_key_resp都从 0.5 秒时开始,不限持续时间。

那么接下来,点击Insert Loop插入循环:

Insert Loop

循环的各项属性

  • Name(变量名):略;
  • loopType(循环类型):在循环中,刺激条件如何呈现,此处暂时不设置;
  • Is trials(是试次):这个循环表示的是正式的试次,而不是练习或其他用途,会在数据文件中输出对应的标签;
  • nReps(重复次数):循环内的内容重复的次数;
  • Selected rows(选择行数):从条件文件中选择一部分行载入,而不是全部载入,此处暂时不设置;
  • random seed(随机种子):输入种子,可以使每次程序运行时输出相同的顺序,即“伪随机”——看似随机,但每一次运行都是相同的;
  • conditions(条件文件):载入一个文件,将文件内容作为循环运行的条件,此处暂时不设置。

在这里,我们只需要勾上 Is trials,然后将 nReps 设置为10即可。这样程序应该就能运行了。

循环创建完成

在这里,我们创建了一个名为trials的循环。大功告成!

……是不是少了什么?

6. 真正的心理学实验——条件文件

……我好像不是来学 Excel 和 MATLAB 的?

到这里,你已经明白了 PsychoPy 编程的大致流程,唯一没有解决的问题是:它不是个实验。

真正的心理学实验,包括各种各样的条件、各种各样的随机化,不是简简单单的一个 A 就能代表的。

实验三:注入灵魂——条件文件的载入

在实验二的基础上,修改实验。

试次包含五个小写字母 a,五个大写字母 A,被试看到 a 时按下 F 键,看到 A 时按下 J 键。如果按错,则记录为错误反应,也进入下一个试次。

我想了很久要怎么不突兀地引入这个“条件文件”的存在,但是想来想去都觉得很突兀,所以直接还是直接说了(

条件文件是 PsychoPy 主程序外的一个文件,可以被 PsychoPy 读取,用以创建实验内需要用到的各种条件。

而条件文件支持的格式是——.csv 或 .xlsx。

打开 Excel 吧。

在 Excel 里编辑条件文件

条件文件的第一行是变量名,后面的每行都代表一个条件。

这里的条件文件相对简单,所以我们可以手写。我在这里写了三列:stimuli即刺激,correct即正确的答案,type即类型。除开第一行,一共有 10 行,也就是 10 个条件。

添加额外的分类变量与标记变量对分析数据非常有帮助。

PsychoPy 会将条件文件的每一列都作为一个变量读入,因此添加分类变量可以帮助你快速从数据文件中筛选数据。

把它保存为conditions.xlsx,和 PsychoPy 程序文件放在同一个文件夹底下。

随后再次进入trials的编辑面板,单击它,在刚刚被我们忽略的 Conditions 一栏选中我们做好的刺激文件:

载入条件文件

可以看到,PsychoPy 读取到了 10 个条件,3 个变量。

下一步是把试次里的刺激改成我们在条件文件里定义好的东西,点击trial,进入trial_stimuli的编辑界面:

首先更改文本

如图所示,先把文本修改成$stimuli

这里是前面提到的$代表变量的知识。这里告诉程序,每次把变量stimuli里的内容展示到屏幕上——也就是 a 或者 A。

第二步——这次不坑人了,把右边方框里的 constant 改为 set every repeat,告诉程序“在每次重复时更新”。

如果不这样设置,那么你输入的内容会被视作常量,trial 仅接收第一次的输入,后面的循环和第一次循环显示的内容完全一致。

接下来,修改键盘输入的相关内容,点击trial_key_resp

修改接受的输入

启用“正确答案”设置

首先修改 Allowed Keys,改为我们需要的:'f','j'

第二步,在 Data 中,勾选 Store correct 选项,告诉程序“保存正确结果”。同时在下面的 Correct Answer 里输入$correct,我们刚刚在条件文件中设置的正确答案。

这样做之后,数据文件中会新增一列,以 0 或 1 的形式保存每次按键的正确与否,可以省去手动对比答案的时间。

在一次模拟实验中收集到的数据,其中 Parity_key_resp.corr 即为正确标记

点击 OK,大功告成,运行实验试试吧?

……好像还有哪不对?怎么做了十一次实验?后面怎么还有?

相信你已经猜到了。

没错,就是这里

nReps 实际表示的是,条件文件中每一个条件的重复次数。刚刚的意思是,一共载入了 10 个条件,每个条件重复 10 次,也就是 100 个试次。

因此,把这里改成 1 就行了。

不过都到这里了,你肯定也想到了……

减小条件文件的一个好办法——增加重复次数

两个条件,每个条件重复五次,效果也是一样的。完全可以。

其实不一样。取决于顶上 loopType 的设定。

loopType 分为五种,假设我们现在有 [a, b, c] 三种条件,每种条件重复三次(nReps = 3),则有:

  • random:随机,但在条件文件内不会重复

    • 会出现类似 [a b c, b c a, c a b] 的序列,在每组 3 个条件都呈现过一次之前不会重复
  • sequential:序列化,会按照条件文件的顺序依次读入

    • 即 [a, b, c, a, b, c, a, b, c]
  • full random:全随机序列,会打乱组间不重复的规则

    • 会出现类似 [a, a, b, c, c, b, b, a, c] 的序列,即完全将所有试次全部打乱
  • staircase:阶梯化,会在每次循环中产出一个level变量,帮助有自适应需求的用户开发程序,使得程序可以动态调节
  • interleaved staircases:交叉阶梯化,存在多个变量同时进行变化时使用

对于一般的实验来说,从前两个随机化方法中选择即可。如果实验对试次的呈现顺序、呈现方式、同一刺激连续次数等方面有要求的话,推荐还是用 MATLAB 写一个完整的、随机化的、固定的条件文件,随后用 sequintial 方法读取。

到这里,你已经成功使用 PsychoPy 编写了一个看起来很心理学的实验程序 (虽然不知道能拿来干嘛)。可以为自己鼓鼓掌,花了半个小时一个小时两个小时看到这里也辛苦了(

最后做点总结吧。

7. 小结

下课了!

PsychoPy 编程建立于三个基础:Routine、Loop、条件文件。

通过串联 Routine 和 Loop,可以把实验的流程勾画出来,安排各个试次、各个刺激的呈现顺序;而屏幕上具体呈现什么,从设备上接收什么,则可以通过外置的条件文件进行定义与管理。

后记

我先睡会.jpg

怎么小结就两句话啊,总感觉得多写点,但是不知道要写什么了。

作为一个入门篇的内容,这篇文章主要就是介绍最基本的“怎么用”的问题,如果说读到这里让你感觉“啊,我的脑子会了”,那其实这篇文章就成功了。

当然,脑子会了不代表手会了,更多的东西需要在实践中逐渐摸索出来,所以如果想真正掌握这个工具——还得真用起来才知道。

写到这里我的字数统计器告诉我我已经写了一万三千个字了,太长了,所以我们把其他的一些东西——随机时间啊、线上实验啊、数据分析啊,都安排到后面吧。

最后修改:2022 年 07 月 22 日
虽然点赞什么的确实没什么意义但是也可以点一个再走呗?(