from otree.api import ( models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer, Currency as c, currency_range ) import random import itertools import math author = 'Thomas Graeber' #Revised and debugged by Tsahi Halyo doc = """ Subjective uncertainty in risk """ def divisors(n, setting): if setting == 1: divs = [1,n] else: divs = [] for i in range(2,int(math.sqrt(n))+1): if n%i == 0: divs.extend([i,n/i]) return list(divs) class Constants(BaseConstants): name_in_url = 'within_subjec_risk' players_per_group = None decisions = { 'probabilities': [1,5, 10, 25, 35, 50, 65, 75, 90, 95, 99], 'amounts': [15,16,17,18,19,20,21,22,23,24,25], } rocl_probability = 0 combinations = len(decisions['probabilities']) * len(decisions['amounts']) num_rounds_gains = 6 num_rounds_losses = 0 #Needs to be num_rounds_gains+1+num_rounds_losses +1 if we have repetitions for inconsistency analysis, otherwise, just num_rounds_gains+numrounds num_rounds = 6 class Subsession(BaseSubsession): def creating_session(self): global num_players treatment_set = ['c_prob', 'c_prob', 'c_prob', 'c_amt', 'c_amt', 'c_amt'] if self.round_number == 1: num_players = len(self.get_players()) for p in self.get_players(): p.participant.vars['failed_comprehension'] = False p.participant.vars['lists_paying_round'] = random.randint(1, Constants.num_rounds) p.participant.vars['gain_list_sequence'] = random.sample(range(0, Constants.combinations), Constants.num_rounds_gains) p.participant.vars['loss_list_sequence'] = random.sample(range(0, Constants.combinations), Constants.num_rounds_losses) p.participant.vars['losses_first'] = random.choice([True, False]) #In case each subject sees both Complex Amt. & Complex Prob. #p.participant.vars['prob_first'] = random.choice([True, False]) if p.participant.vars['losses_first']: p.participant.vars['list_sequence'] = p.participant.vars['gain_list_sequence'] + p.participant.vars['loss_list_sequence'] else: p.participant.vars['list_sequence'] = p.participant.vars['loss_list_sequence'] + p.participant.vars['gain_list_sequence'] #Treatment Assignment #Base non-random assignment (will be overridden) p.participant.vars['treatment'] = ['base' for x in range(6)] #For balanced random assignment p.participant.vars['task_type'] = 'Failed' for p in self.get_players(): p.is_rocl = False p.losses_first = p.participant.vars['losses_first'] #p.prob_first = p.participant.vars['prob_first'] r_n = p.round_number - 1 if p.participant.vars['treatment'][r_n] == 'c_prob': p.is_complex_amt = False p.is_complex_prob = True elif p.participant.vars['treatment'][r_n] == 'c_amt': p.is_complex_amt = True p.is_complex_prob = False elif p.participant.vars['treatment'][r_n] == 'base': p.is_complex_amt = False p.is_complex_prob = False p.set_current_task_number() if p.round_number == p.participant.vars['lists_paying_round']: p.on_paying_round = True class Group(BaseGroup): pass class Player(BasePlayer): task_type_beta = models.IntegerField(choices=[[1, 'Load'],[2, 'Complex Probability'], [3, 'Baseline']]) def set_treatment_beta(self): self.is_complex_amt = False self.is_complex_prob = False self.is_load = False if self.task_type_beta == 1: self.is_complex_prob = False self.is_load = True elif self.task_type_beta == 2: self.is_complex_prob = True self.is_load = False def set_treatment(self): self.is_rocl = False self.is_complex_amt = False self.is_complex_prob = False self.is_load = False if self.participant.vars['treat'] == 1: if self.round_number == 1: r = random.random() if r <= 1 or True: self.participant.vars['task_type'] = 'cprob' else: self.participant.vars['task_type'] = 'load' if self.participant.vars['task_type'] == 'cprob': self.is_complex_prob = True self.type = 'complex_prob' if self.participant.vars['task_type'] == 'load': self.is_load = True self.type = 'load' self.task_identifier = 'lottery_prob{}_am{}_rocl{}_cp{}_ca{}'.format( self.lottery_probability, self.lottery_amount, self.is_rocl, self.is_complex_prob, self.is_complex_amt ) treat_group = models.BooleanField() task_number = models.IntegerField() task_identifier = models.StringField() #switching_point_1 = models.FloatField() # switching_point_2 = models.FloatField() on_paying_round = models.BooleanField(initial=False) lottery_amount = models.IntegerField() lottery_probability = models.IntegerField() lottery_prob_loss = models.IntegerField() rocl_probability = models.IntegerField() complex_prob = models.StringField() complex_amt = models.StringField() load_guess = models.IntegerField() load_sum = models.IntegerField(initial = 0) load_correct = models.IntegerField(initial = 0) load_num_correct = models.IntegerField(initial=0) is_rocl = models.BooleanField() is_complex_prob = models.BooleanField() is_complex_amt = models.BooleanField() is_load = models.BooleanField() prob_first = models.BooleanField() losses_first = models.BooleanField() type = models.StringField() endowment = models.FloatField() # Field which is 0 if always Option B, 1 if switching point, 2 if never option B indicator_never_always_switcher = models.IntegerField() switching_point = models.FloatField() lottery_identifier = models.StringField() confidence = models.FloatField() #confidence_upper_bound = models.FloatField() #confidence_lower_bound = models.FloatField() random_payoff = models.CurrencyField() random_right_side_amount = models.CurrencyField() timed_out = models.BooleanField(initial=False, blank=True) attention_check = models.StringField() failed_comprehension = models.BooleanField(initial=False) qn_lottery_got_wrong = models.BooleanField(initial=False) qn_list_got_wrong = models.BooleanField(initial=False) qn_confidence_got_wrong = models.BooleanField(initial=False) qn_lottery = models.IntegerField( choices=[ [0, 'It is possible that I get paid both $15 and $5, i.e., I may receive a total amount of $20 from this lottery.'], [1, 'I receive EITHER $15 OR $5 from this lottery.'], [0, 'It is possible that I receive no money from this lottery.'], ], widget=widgets.RadioSelect, blank=False, label="" ) qn_list = models.IntegerField( choices=[ [0, 'This person indicated that the lottery is worth more to them than $9.'], [0, 'This person indicated that the lottery is worth between $3 and $7 to them. '], [1, 'This person indicated that the lottery is worth between $8 and $9 to them.'], ], widget=widgets.RadioSelect, blank=False, label="" ) qn_confidence = models.FloatField( blank=False, label="" ) def set_complex_prob(self): b = random.choice([2*x for x in range(5,10)]) a = random.randint(4,9) c = int(random.choice([x for x in divisors(a*b, 0) if x < 10])) d = int((a*b)/c - self.lottery_probability) if d>= 0: self.complex_prob = '(' + str(a) + ' x ' + str(b) + ') / ' + str(c) + ' - ' + str(d) else: self.complex_prob = '(' + str(a) + ' x ' + str(b) + ') / ' + str(c) + ' + ' + str(abs(d)) # a = random.choice([6*x for x in range(1,self.lottery_probability)]) # b = self.lottery_probability - a # a_1 = int(random.choice(divisors(a,0))) # a_2 = int(a/a_1) # if len(divisors(abs(b),1)) > 2: # b_1 = int(random.choice(divisors(abs(b),0))) # else: # b_1 = int(random.choice(divisors(abs(b),1))) # b_2 = abs(int(b/b_1)) # if b > 0: # self.complex_prob = '(' + str(a_1) + ' x ' + str(a_2) + ')' + ' + ' + '(' + str(b_1) + ' x ' + str(b_2) + ')' # else: # self.complex_prob = '(' + str(a_1) + ' x ' + str(a_2) + ')' + ' - ' + '(' + str(b_1) + ' x ' + str(b_2) + ')' def set_complex_amount(self): # a = random.choice([4, 6, 8, 9]) # b = random.choice([x for x in list(range(4, 10)) if x != a]) # primfac1 = [] # if (a % 2) == 0: # primfac1 = [2, int(a/2)] # elif (a % 3) == 0: # primfac1 = [3, int(a/3)] # c1 = primfac1[0] # c2 = primfac1[1] # d = random.choice([4, 6, 8, 9]) # e = random.choice([x for x in list(range(4, 10)) if x != d]) # primfac2 = [] # if (d % 2) == 0: # primfac2 = [2, int(d / 2)] # elif (d % 3) == 0: # primfac2 = [3, int(d / 3)] # f1 = primfac2[0] # f2 = primfac2[1] # g = self.lottery_amount - b * c2 - e * f2 # if g >= 0: # camount = '(' + str(a) + ' x ' + str(b) + '/' + str(c1) + ')+' + '(' + str(d) + ' x ' + str(e) + '/' + str(f1) + ')+' + str(g) # elif g < 0: # camount = '(' + str(a) + ' x ' + str(b) + '/' + str(c1) + ')+' + '(' + str(d) + ' x ' + str(e) + '/' + str(f1) + ')-' + str(abs(g)) # return camount b = random.choice([2*x for x in range(5,10)]) a = random.randint(4,9) c = int(random.choice([x for x in divisors(a*b, 0) if x < 10])) d = int((a*b)/c - self.lottery_amount) if d>= 0: self.complex_amt = '(' + str(a) + ' x ' + str(b) + ') / ' + str(c) + ' - ' + str(d) else: self.complex_amt = '(' + str(a) + ' x ' + str(b) + ') / ' + str(c) + ' + ' + str(abs(d)) def set_current_task_number(self): self.task_number = self.participant.vars['list_sequence'][self.round_number-1] task_list = [] #Cartesian product l = itertools.product( Constants.decisions['probabilities'], Constants.decisions['amounts'], ) task_list.extend(l) tup = task_list[self.task_number] self.lottery_probability = tup[0] self.lottery_prob_loss = 100 - self.lottery_probability if self.losses_first and self.round_number <= Constants.num_rounds_losses or not self.losses_first and self.round_number > Constants.num_rounds_gains: self.lottery_amount= -tup[1] else: self.lottery_amount = tup[1] if self.is_rocl: self.type = 'rocl' self.lottery_probability = random.choice([x for x in Constants.decisions['probabilities'] if x not in [1,99]]) elif self.is_complex_prob: self.type = 'complex_prob' elif self.is_complex_amt: self.type = 'complex_amt' else: self.type = 'list' self.task_identifier = 'lottery_prob{}_am{}_rocl{}_cp{}_ca{}'.format( self.lottery_probability, self.lottery_amount, self.is_rocl, self.is_complex_prob, self.is_complex_amt ) # setattr(self, 'lottery_amount', random.choice(Constants.decisions[self.task_number]['y_list'])) # setattr(self, 'task_identifier', Constants.decisions[self.task_number]['name']) if self.lottery_amount > 0: self.endowment = 0 else: self.endowment = abs(self.lottery_amount) #Why the generators? def frange(self, start, stop, step): i = start while i < stop: if isinstance(i, int): yield i else: yield round(i, 2) i += step def right_side_amounts1(self): if self.lottery_amount > 0: #start should be 0 instead of 1, speak to Ben and Toto lst = self.frange(1, self.lottery_amount + 1, 1) else: #end should be 1 instead of 0, speak to Ben and Toto lst = self.frange(self.lottery_amount, 0, 1) l = enumerate(lst, 1) count, amount = zip(*l) absamount = (abs(x) for x in amount) lst = zip(count, amount, absamount) return list(lst) def current_lottery(self): if self.is_rocl: lott = [('X', self.lottery_amount, abs(self.lottery_amount)), ('100% - X', 0, 0)] elif self.is_complex_prob: self.set_complex_prob() lott = [(self.complex_prob, self.lottery_amount, abs(self.lottery_amount)), ('100 - (' + self.complex_prob + ')', 0, 0)] elif self.is_complex_amt: self.set_complex_amount() lott = [(self.lottery_probability, self.complex_amt, abs(self.lottery_amount)), (100 - self.lottery_probability, 0, 0)] else: lott = [(self.lottery_probability, self.lottery_amount, abs(self.lottery_amount)), (100 - self.lottery_probability, 0, 0)] return lott def rocl_range(self): rr = [self.lottery_probability - 5, self.lottery_probability + 5] return rr def range_bounds(self): if self.lottery_amount > 0: bounds = [0, self.lottery_amount] elif self.lottery_amount < 0: bounds = [self.lottery_amount, 0] return bounds def table_length1(self): return abs(self.lottery_amount) + 1 def set_switching_point_and_indicator(self): if self.lottery_amount > 0: if self.switching_point == 9999: self.switching_point = self.lottery_amount + 1 self.indicator_never_always_switcher = 2 elif self.switching_point == 0: self.indicator_never_always_switcher = 0 else: self.indicator_never_always_switcher = 1 elif self.lottery_amount < 0: if self.switching_point == 9999: self.switching_point = 1 self.indicator_never_always_switcher = 2 elif self.switching_point == self.lottery_amount: self.indicator_never_always_switcher = 0 else: self.indicator_never_always_switcher = 1 def set_payoffs(self): random_right_side_amount = random.choice(self.right_side_amounts1())[1] possible_payoff = 0 if random_right_side_amount >= self.switching_point: possible_payoff = c(random_right_side_amount + self.endowment) else: if not self.is_rocl: prob = self.lottery_probability else: self.rocl_probability = random.randint(self.rocl_range()[0], self.rocl_range()[1]) prob = self.rocl_probability outcome = random.random() < prob / 100 possible_payoff = c(outcome * self.lottery_amount) + self.endowment self.random_payoff = possible_payoff if self.is_load and self.load_correct == 1: possible_payoff += c(2) if self.on_paying_round: if 'possible_payoffs' in self.participant.vars: if False: self.participant.vars['possible_payoffs'].append(0) else: self.participant.vars['possible_payoffs'].append(round(possible_payoff, 2)) elif False: self.participant.vars['possible_payoffs'] = [0] else: self.participant.vars['possible_payoffs'] = [round(possible_payoff, 2)]