#1 by thaicao (edited )
Dear all, In my experiment, I want to conduct a multi-armed bandit task where participants can sample values of different distributions by clicking on the respective buttons as long as they want. For each click, I want to record the id of the button and the values generated from the options. However, my code only records the id and value from the last click (see code). Because there is no restriction on the number of samples so I can't create a player field for each sample. Is there any way to have a narrow data format where each row of the dataset record the id and value of a sample/button click? I'm very new at Otree and programming, so I'm very grateful for any suggestions. Python code: # CLASSES class C(BaseConstants): NAME_IN_URL = 'Arm5' PLAYERS_PER_GROUP = None NUM_ROUNDS = 1 BANDIT_ARMS = 3 BANDIT_MU = (5.1, 5.2, 5.3) class Subsession(BaseSubsession): pass class Group(BaseGroup): pass class Player(BasePlayer): value = models.FloatField() arm = models.IntegerField() # FUNCTIONS def custom_export(players): yield ['participant_code', 'id_in_group'] for p in players: pp = p.participant yield [pp.code, p.id_in_group] # PAGES class Bandit(Page): form_model = 'player' @staticmethod def live_method(player: Player, data): import random player.arm = data mu = C.BANDIT_MU[data] player.value = round(random.uniform(mu -5, mu +5),2) response = dict(arm = player.arm, value = player.value) return {player.id_in_group: response} HTML: <body> <input id="1" type="button" value ="1" onclick="sendValue(this.id)" > Option 1 </input> <input id="2" type="button" value="2" onclick="sendValue(this.id)" > Option 2 </input> <input id="3" type="button" value="3" onclick="sendValue(this.id)" > Option 3 </input> <div>Value of option </div> <div id="output"></div> </body> <script> // Get a reference to the output element const outputElement = document.getElementById("output"); // send the value of the option being clicked to python code function sendValue(clicked){ liveSend(parseInt(document.getElementById(clicked).value)) } // Sending the random value from python to page function liveRecv(data){ outputElement.innerHTML = data.arm + " is " + data.value } // Hide the new value after 3 seconds setTimeout(function() { outputElement.innerHTML = ""; }, 3000) </script>
#2 by thaicao
I found a solution in case anyone has the same problem: - create a player string field to contain the repeated trials class Player(BasePlayer): ... seq_value = models.LongStringField(initial= "") # record the sequence of values sampled seq_arm = models.LongStringField(initial= "") # record the sequence of arms sampled - append the new data into the blank string field every time a button is pressed using live_method class Bandit(Page): form_model = 'player' @staticmethod def live_method(player: Player, data): ... player.seq_value += ";" + str(player.value) # append the sampled value to the sequence player.seq_arm += ";" + str(player.arm)