在oTree

时间:2017-06-24 12:29:20

标签: python django otree

我正在为我将要在硕士论文中运行的实验编写代码。我基本上已经完成了,但是最后一个方面我无法找到解决方法。我有一个有16名参与者的公益游戏,分为8个双人组。我有4次治疗,我平衡了比赛,s.t。每轮治疗由4名球员进行(他们是12名)。我现在缺少的部分是我希望每个玩家每次都能完全玩3次。这种随机化在下面的代码中执行,这在理论上是有效的,但实际上我从未设法到达它的末尾。在20分钟内,我设法在第10轮结束时获得,但无法使程序找到满足上述第11轮和第12轮两个条件的组合。我知道它有点棘手,如果你更容易理解进入它,但是......你有什么建议吗? 非常感谢!

class Subsession(BaseSubsession):

    def before_session_starts(self):
        info_condition = ['Bel', 'Bel', 'Act', 'Act', 'Ctrl', 'Ctrl', 'No', 'No']
        i = 0
        condition = True
        while condition:
            i+= 1
            print('I am in the loop: {} th time. Round{}.'.format(i, self.round_number))
            self.group_randomly()
            for gr_index, g in enumerate(self.get_groups()):
                g.info = info_condition[gr_index]
                for p in g.get_players():
                    p.info_player = g.info

            condition = any(not p.can_go_on(p.info_player) for p in self.get_players())
            if condition == False:
                break
        p.count_treat()
        print('I am out of the loop. Round{}'.format(self.round_number))

class Player(BasePlayer):

    runs = models.CharField()

    def count_treat(self):
        ctrl_count = 0
        no_count = 0
        bel_count = 0
        act_count = 0
        for p in self.in_all_rounds():
            if p.info_player == "Ctrl":
                ctrl_count += 1
            elif p.info_player == "No":
                no_count += 1
            elif p.info_player == "Bel":
                bel_count += 1
            elif p.info_player == "Act":
                act_count += 1
            p.runs = dict()
            p.runs['Ctrl'] = ctrl_count
            p.runs['Bel'] = bel_count
            p.runs['No'] = no_count
            p.runs['Act'] = act_count


    def can_go_on(self, activity):
        self.count_treat()
        print(self.id_in_subsession, self.runs[activity] < 4, activity, self.runs[activity])
        return self.runs[activity] < 4

2 个答案:

答案 0 :(得分:1)

我不是100%确定我遇到了问题,但是如果我建议任务是随机化受试者内部设计的治疗顺序,那么每个玩家将进行三次治疗,但这个顺序应该是跨玩家随机化。然后我认为可以这样做:

models.py中的

import random
class Constants(BaseConstants):
      treatments = [['Bel', 'Bel','Bel'],
             ['Act', 'Act', 'Act'],
             ['Ctrl', 'Ctrl', 'Ctrl'],
             ['No', 'No', 'No']]

class Subsession(BaseSubsession):
def before_session_starts(self):
    if not self.session.vars.get('treatments'):
        treatments = []
        for g in self.get_groups():
            group_treatments = Constants.treatments
            random.shuffle(group_treatments)
            flat_treatments = [item for sublist in group_treatments for item in sublist]
            if g.id % 2 != 0:
                treatments.append(flat_treatments)
            else:
                treatments.append(treatments[-1])
        self.session.vars['treatments'] = treatments

    for g in self.get_groups():
        g.treatment = self.session.vars['treatments'][g.id - 1][g.round_number - 1]

我们在这做什么? 在Constants中,我们创建了一系列治疗列表(不是最优雅的方式,所以如果你需要改变轮次的长度,可以用更多的方式来做,但是为了懒惰它起作用)

然后当每个Subsession被启动时,你获得这个列表列表,对于每个组我们将它们洗牌(每个子列表中的项目都不会被洗牌),然后使它们变平,所以它只是一个列表之后,我们将这个改组的治疗列表添加到会话级别的列表(列表)中。

因此,在treatments会话变量的第一轮之后,您可以获得每组治疗的完整数组。

然后你只需将treatment模型中的Group模型字段设置为等于个性化治疗列表中的项目,对应于当前轮次和组ID。

更新:使两组的治疗顺序相同

答案 1 :(得分:0)

我决定添加另一个答案而不是编辑上一个答案,因为如果您不需要重新组织团体,第一个答案仍然有用。

所以,假设你有四种治疗方法 - [A,B,C,D]

有三个条件:

  1. 条件1 :玩家在此轮次中进行三轮随机治疗,然后进行下一轮(随机)三轮治疗等。

    我们可以将特定玩家的序列描述为一个列表:AAABBBCCCDDD,其中一个字母对应一个处理,它在该字符串中的位置对应一个圆数。

  2. 条件2 :在每轮比赛中,有四名球员进行相同的治疗。

  3. 条件3 :重新洗牌。这意味着没有任何球员在所有轮次中具有完全相同的治疗顺序。因此,如果有人打AAABBBCCCDDD,没有人可以做同样的事情,并且在前三轮比赛中成为A的四名球员之一,则另一名球员需要拥有像AAABBBDDDCCC这样的球员。
  4. 由于人们连续玩三轮(如AAA或BBB),让我们将三联AAA描述为 A 等 - 只是为了简洁起见。所以AAABBBCCCDDD将 ABCD

    下面的代码生成一个矩阵12X16,每个玩家连续三次玩每次治疗,满足所有3个条件。

    代码是最佳的FAR,我相信更有经验的python程序员可以轻松地实现一种更高效的方式。但它的确有效。您需要做的就是将此矩阵分配给任何session.vars变量,然后每个玩家可以根据他的ID和轮数获得他/她的治疗。

    函数by_letters比较两个序列,如果两个序列中的同一位置至少有一个字母,则返回True。因此,Trueabcd会因bdca而返回c,但Falseabcd会返回badc

    我们在开始时产生了四种治疗的所有可能的排列(24)。这是我们可以使用的序列池。

    函数filtered返回可用序列的子集,这些序列没有重合字母(基于by_letters函数)。

    当找到治疗矩阵时,函数triple只重复三次治疗。

    因此,我们通过从我们的一组治疗中产生排列来获得初始数据。

    我们从中选择一个随机序列,然后根据第一次绘制过滤掉剩余的数据。那是我们的第二个序列。

    我们找到两个集合的交集,它们都没有序列1和序列2的重合字母。然后,从剩下的子集中我们绘制了一个随机行。那是我们的第三个序列。

    我们做三个相同的交集以获取第四个序列。 这是我们的第一个4X4矩阵。 我们从初始数据中过滤掉所有序列(使用itertools.filterfalse),然后使用初始数据循环四次,但没有我们刚刚找到的序列。

    由于并非每次都能找到满足所有条件的4个4X4矩阵,我们使用try.. except..绘制另一个随机起始序列。

    import random
    import itertools
    initial_data = list(itertools.permutations('abcd'))
    
    def by_letters(a, b):
        u = zip(a, b)
        for i, j in u:
            if i == j:
                return True
        return False
    
    
    def filtered(what):
        x = [i for i in a if not by_letters(i, what)]
        return x
    
    
    def triple(str):
        return ''.join([c+c+c for c in str])
    
    result = None
    
    while result is None:
        try:
            matrices = []
            a = initial_data
            for i in range(4):
                print('cycle:: ', i)
                line1 = random.choice(a)
                line2 = random.choice(filtered(line1))
                s1 = set(filtered(line1))
                s2 = set(filtered(line2))
                sets = [s1, s2]
                line3 = random.choice(list(set.intersection(*sets)))
                s3 = set(filtered(line3))
                sets = [s1, s2, s3]
                line4 = random.choice(list(set.intersection(*sets)))
                matrix1 = [line1, line2, line3, line4]
                matrices.append(matrix1)
                a = list(itertools.filterfalse(lambda x: x in matrix1, a))
            result = matrices                
    
        except IndexError:
             pass
    
    final = [triple(j) for i in matrices for j in i]
    print(final)
    

    和输出类似于:

    ...
    ['aaabbbdddccc', 'dddcccaaabbb', 'cccdddbbbaaa', 'bbbaaacccddd', 
    'cccdddaaabbb', 'aaabbbcccddd', 'bbbaaadddccc', 'dddcccbbbaaa', 
    'aaacccbbbddd', 'dddaaacccbbb', 'cccbbbdddaaa', 'bbbdddaaaccc', 
    'dddaaabbbccc', 'bbbdddcccaaa', 'aaacccdddbbb', 'cccbbbaaaddd']