如何解决Otree中运行两个不同组规模并行应用时的组矩阵分配错误
如何解决Otree中运行两个不同组规模并行应用时的组矩阵分配错误
嘿,我来帮你拆解问题、搞定这个报错!
问题根源
你遇到的The matrix of integers either has duplicate or missing elements错误,核心原因其实很简单:在并行运行的App里,每个App的subsession默认包含整个Session的所有玩家——哪怕你用is_displayed()把非目标玩家的页面隐藏了,subsession依然认定这些玩家属于自己。而set_group_matrix()有个硬要求:传入的矩阵必须覆盖当前subsession的所有玩家,不能只塞你需要的那部分。你之前只把目标玩家放进矩阵,漏掉了其他玩家,自然会触发“缺失元素”的报错。
另外你不用纠结“传玩家对象还是ID”的问题,Otree两种都支持,问题本质是矩阵没覆盖全所有玩家。
解决方案
解决思路就一个:把当前subsession的所有玩家都纳入组矩阵。对于那些不属于当前App的玩家,我们把他们单独分到1人小组里就行——反正他们看不到当前App的页面,完全不影响正常流程。
步骤1:修改并行App的creating_session函数
以你需要6人组的quiz App为例,修改后的代码如下:
def creating_session(subsession): if subsession.round_number == 1: # 只在第一轮分配组,避免重复操作 # 筛选当前App的目标玩家 target_players = [ p for p in subsession.get_players() if p.participant.group_assignment == 'quiz' ] # 筛选不属于当前App的非目标玩家 non_target_players = [ p for p in subsession.get_players() if p.participant.group_assignment != 'quiz' ] # 目标玩家按6人一组分组 target_groups = [target_players[i:i+6] for i in range(0, len(target_players), 6)] # 非目标玩家单独分到1人组(不参与当前App流程) non_target_groups = [[p] for p in non_target_players] # 合并成覆盖所有玩家的完整矩阵 full_group_matrix = target_groups + non_target_groups subsession.set_group_matrix(full_group_matrix)
对应的,需要4人组的quiz_CP App代码改成这样:
def creating_session(subsession): if subsession.round_number == 1: target_players = [ p for p in subsession.get_players() if p.participant.group_assignment == 'quiz_CP' ] non_target_players = [ p for p in subsession.get_players() if p.participant.group_assignment != 'quiz_CP' ] # 目标玩家按4人一组分组 target_groups = [target_players[i:i+4] for i in range(0, len(target_players), 4)] non_target_groups = [[p] for p in non_target_players] full_group_matrix = target_groups + non_target_groups subsession.set_group_matrix(full_group_matrix)
步骤2:(可选)优化初始分组的满员适配
如果你希望每组都是满员的(避免出现5人或3人的不满小组),可以调整初始分配玩家数量的逻辑,让分到quiz的玩家数是6的倍数,分到quiz_CP的是4的倍数:
import random def creating_session(subsession): if subsession.round_number == 1: players = subsession.get_players() total_players = len(players) # 优先匹配60%比例,同时确保两组人数分别是6和4的倍数 target_quiz = int(total_players * 0.6) # 调整到最近的6的倍数 target_quiz = round(target_quiz / 6) * 6 # 微调确保剩余人数能被4整除 while (total_players - target_quiz) % 4 != 0 and target_quiz > 0: target_quiz -=6 # 兜底:如果调整到0,重新找符合条件的最大值 if target_quiz ==0: target_quiz = max([x for x in range(0, total_players,6) if (total_players -x) %4 ==0]) split_indices = { 'quiz': target_quiz, 'quiz_CP': total_players - target_quiz, } group_assignment = ( ['quiz'] * split_indices['quiz'] + ['quiz_CP'] * split_indices['quiz_CP'] ) random.shuffle(group_assignment) for player, group in zip(players, group_assignment): player.participant.group_assignment = group print(f"Assigned {player.participant.code} to {group}")
如果你对满员没有硬性要求,这步可以跳过,直接用你原来的随机分配逻辑就行。
修改后效果验证
调整完之后:
- 目标玩家会按你需要的6/4人组成正常小组,参与App流程;
- 非目标玩家被分到1人小组,他们的
is_displayed()返回False,完全看不到当前App的页面,不会干扰任何流程; - 组矩阵覆盖了当前subsession的所有玩家,没有缺失或重复元素,Otree就不会再抛出报错了。
备注:内容来源于stack exchange,提问作者Zahra Hajbaraty
相关产品推荐
相关产品推荐

