oTree: Individual decision making

An Introduction to oTree

Matteo Ploner

Forms

Submitting information

  • Each page in oTree can contain a form
    • The player fills with some value and then submits it
      • Cardinal values
        • Integer, Float
      • Ordinal values
        • Integer, Categorical
      • Text
        • Strings of numbers and letters
  • Several formats to collect values
    • Open fiels
    • Buttons
    • Radio buttons
    • Dropdown lists

Basic structure of forms

  • First create fields in models.py
  • As an example, if you want to collect name and age
  • Then in pages.py, you will create a class
  • Finally, in the template Anag.html the form will be displayed with
  • To display the forms separately

Input formats

Examples

  • In the following examples the participant receives a random number
  • The participant is asked to report features of the number
    • Several input interfaces are presented
      • Radio
      • Button
      • Checker
      • Dropdown
      • Radio Sequence
      • Text
      • Value
      • Tabular

Button

Setting up an individual decision-making task

Our task

  • First we investigate individuals’ risk attituted
    • Multiple Price List questionnaire (Holt and Laury 2002)
      • Trade-off between lotteries
  • Second we collect pieces of demographic information
    • Age,gender, …

Screens

Instructions

Choices

Outcomes

Final questionnaire

Code

models.py (ii)

  • In Player class we define the “templates” for data collection
    • MPL table
class Player(BasePlayer):

    # This is for main choices, each variable is one row in the choice table MPL
    HL_1 = models.PositiveIntegerField(choices=[[1, 'A'],[2, 'B']],widget=widgets.RadioSelectHorizontal)
    HL_2 = models.PositiveIntegerField(choices=[[1, 'A'],[2, 'B']],widget=widgets.RadioSelectHorizontal)
    HL_3 = models.PositiveIntegerField(choices=[[1, 'A'],[2, 'B']],widget=widgets.RadioSelectHorizontal)
    HL_4 = models.PositiveIntegerField(choices=[[1, 'A'],[2, 'B']],widget=widgets.RadioSelectHorizontal)
    HL_5 = models.PositiveIntegerField(choices=[[1, 'A'],[2, 'B']],widget=widgets.RadioSelectHorizontal)
    HL_6 = models.PositiveIntegerField(choices=[[1, 'A'],[2, 'B']],widget=widgets.RadioSelectHorizontal)
    HL_7 = models.PositiveIntegerField(choices=[[1, 'A'],[2, 'B']],widget=widgets.RadioSelectHorizontal)
    HL_8 = models.PositiveIntegerField(choices=[[1, 'A'],[2, 'B']],widget=widgets.RadioSelectHorizontal)
    HL_9 = models.PositiveIntegerField(choices=[[1, 'A'],[2, 'B']],widget=widgets.RadioSelectHorizontal)
    HL_10 = models.PositiveIntegerField(choices=[[1, 'A'],[2, 'B']],widget=widgets.RadioSelectHorizontal)

    # This is needed for the instructions
    HL = models.PositiveIntegerField(choices=[[1, 'A'],[2, 'B']],widget=widgets.RadioSelectHorizontal)

    # These variables are collected in the final questionnaire
    sex = models.StringField(widget=widgets.RadioSelectHorizontal(),choices=['Male', 'Female'])
    age = models.IntegerField(choices = range(18,60,1))
    comment = models.TextField(label="Your comment here:")
    like = models.IntegerField(choices=[1,2,3,4,5],widget=widgets.RadioSelectHorizontal)

models.py (iii)

  • In Player class we compute the payoffs
    • Typical of individual decision making
"""
... continue
"""
    # Define here the methods associated to Players
    # this method is needed to compute payoffs
    def set_payoff_HL(self):
        #*******************************************
        # select random row and random outcome
        #*******************************************
        self.participant.vars['HL_row'] = random.randint(1,10)
        # select one row randomly for payment (from module random)
        self.participant.vars['HL_random'] = random.randint(1,10)
        # select the number x that defines the outcome of the lottery (if x<=p, outcome is left f1 or f3, otherwise f2 or f4)
        # write it to participant.vars['HL_random']

        #*******************************************
        # select choices in correspondence to random row
        #*******************************************
        choices = [self.HL_1,self.HL_2,self.HL_3,self.HL_4,self.HL_5,self.HL_6,self.HL_7,self.HL_8,self.HL_9,self.HL_10]
        # create a list with all choices of the player (see self)
        self.participant.vars['HL_choice'] = choices[self.participant.vars['HL_row']-1]
        # select from the list the choice in correspondence to the randomly drawn row (notice the offset)
        # write it to participant.vars['HL_choice']

        #*******************************************
        # Compute here the payoffs
        #*******************************************
        if self.participant.vars['HL_random'] <= self.participant.vars['HL_row']:
        # if the random number is smaller equal than the random row
            if self.participant.vars['HL_choice'] == 1: #A
            # if the choice was A
                self.participant.vars['payoff_HL'] = Constants.f1
                # because HL_row is the same as p in the MPL
            else :
            # if the choice was B
                self.participant.vars['payoff_HL'] = Constants.f3
        else:
        # if the random number is slarger than the random row
            if self.participant.vars['HL_choice'] == 1 :#A
                # if the choice was A
                self.participant.vars['payoff_HL'] = Constants.f2
                # because HL_row is the same as p in the MPL
            else :
                self.participant.vars['payoff_HL'] = Constants.f4

        self.payoff = self.participant.vars['payoff_HL']
        # write the payoff to player.payoff

pages.py (ii)

  • Before moving to the next page, we compute payoffs
    • See method set_payoff_HL() from models.py
    • This way we compute payoffs only once and not when browser is refreshed

Templates

Instructions.html (ii)

  • Main body and demo of MPL
    • In a bs container

How to run and test your app

settings.py

  • Add your app to the list SESSION_CONFIGS
    • Description of the app is in a dictionary
      • You can set here other parameters (e.g., treatment) that can be then accessed with session.config

Start a you app

  • Move to your oTree folder with the terminal (usually cd ~/oTree/)
  • You will get the following window

Test your app

  • To test your app and debug just click on the name of the app
    • This creates a number of links (clients) equal to the value in ‘num_demo_participants’ in settings
    • Click on the link to open a client
      • When a number of clients equal to ‘num_demo_participants’ is open you can start your session

Run a session

  • To run a session with N participants
    • Click on Sessions in the top bar menu

  • Click on Create new session

Run a session (ii)

  • Choose you app
    • Configure your settings
      • e.g., number of participants
    • Click on create
      • You will get a number of links equal to number of participants that you can distribute to your clients

Download your data

  • To download your data go tp data in the top bar menu

  • Click on You can download data in Excel or CSV format here. to download data in an open format
    • You can download data for all your apps or separately for each app

Thank you

🦁

To contact me just write me an email

matteo.ploner@unitn.it

or write me on the forum of the course

Appendix

Assignment

  1. Easy
    • Add a field to the final questionnaire
      • Nationality
  2. Less easy
    • Change the protocol of the MPL
      • Instead of a choice for each row, participants report only the row in which they switch from A to B
      • e.g., the sequence AAAAABBBBB become 6

OTree code

  • The oTree app of this lecture:

Download MPL.zipDownload inputs.zip

References

Holt, Charles A, and Susan K Laury. 2002. “Risk Aversion and Incentive Effects.” American Economic Review 92 (5): 1644–55.