oTree 随机分组与 Treatment 设计:实验组、对照组和角色分配

oTree 随机分组与 Treatment 设计:实验组、对照组和角色分配

标签: OTree入门随机分组Treatment

经济学实验里经常需要随机:随机分组、随机角色、随机进入实验组或对照组。oTree 可以做这些事,但最好一开始就想清楚:随机发生在 session 层、group 层,还是 player 层。


一、先区分三种随机

常见随机需求有三类:

  • 随机分组:把参与者分成若干组。
  • 随机角色:组内有人是买家,有人是卖家。
  • 随机 treatment:有人看到高信息版本,有人看到低信息版本。

这三件事可以同时存在,但代码位置不一样。


二、随机分组

如果只需要 oTree 默认随机分组,可以在 creating_session 中使用:

def creating_session(subsession: Subsession):
    subsession.group_randomly()

如果是多轮实验,并且希望每轮重新随机:

def creating_session(subsession: Subsession):
    if subsession.round_number > 1:
        subsession.group_randomly()

如果希望第一轮随机,后面保持同组,通常只在第一轮分组,后续轮次复制第一轮结构:

def creating_session(subsession: Subsession):
    if subsession.round_number == 1:
        subsession.group_randomly()
    else:
        subsession.group_like_round(1)

三、角色分配

角色可以用 id_in_group 判断:

class Player(BasePlayer):
    role_name = models.StringField()

def creating_session(subsession: Subsession):
    for group in subsession.get_groups():
        for player in group.get_players():
            if player.id_in_group == 1:
                player.role_name = 'buyer'
            else:
                player.role_name = 'seller'

页面里可以根据角色显示不同内容:

class BuyerDecision(Page):
    @staticmethod
    def is_displayed(player: Player):
        return player.role_name == 'buyer'

角色一定要存成字段,方便导出后分析。不要只在模板里临时判断。


四、Treatment 和 Control

treatment 最好在实验开始时就分配,并保存到 Player 字段或 participant.vars

import random

class Player(BasePlayer):
    treatment = models.StringField()

def creating_session(subsession: Subsession):
    for player in subsession.get_players():
        player.treatment = random.choice(['control', 'treatment'])

如果后续 app 也要使用同一个 treatment,可以同时写入 participant.vars

player.participant.vars['treatment'] = player.treatment

在另一个 app 中读取:

def creating_session(subsession: Subsession):
    for player in subsession.get_players():
        player.treatment = player.participant.vars.get('treatment')

五、按组分配 Treatment

有些实验要求同一组内所有人属于同一个 treatment。这时应当在 group 层分配:

class Group(BaseGroup):
    treatment = models.StringField()

def creating_session(subsession: Subsession):
    for group in subsession.get_groups():
        group.treatment = random.choice(['control', 'treatment'])
        for player in group.get_players():
            player.treatment = group.treatment

这样导出的玩家数据里也有 treatment 标签,后续分析更方便。


六、一个实用检查清单

上线前检查这些问题:

  • treatment 是否写入了导出字段?
  • 随机是在 player 层还是 group 层?
  • 多轮实验是否保持同一 treatment?
  • treatment/control 人数是否需要平衡?
  • 不同 treatment 的页面是否都测试过?

随机不是越复杂越好。实验设计里最重要的是可解释、可复现、可导出。只要这三点做到了,后面的统计分析就会顺很多。

评论