oTree: Matching, roles, and values

An Introduction to oTree

Matteo Ploner

Design of the experiment

Matching and roles

  • How many repetitions of an interaction are implemented?
    • One shot
    • Repeated
  • When repeated, how are people matched?
    • Partner matching
    • Random partner matching
  • Which roles do they have in the interaction?
    • Symmetric roles
    • Asymmetric roles

A working example

  • Consider 8 subjects that are matched in groups of two

  • One subject in each group is of type BLUE and the other of type GREEN

Repeated interaction

  • When the interaction is repeated we can have 4 possible combinations of type ad matching
    • Constant type and constant matching
      • Individuals are matched together for the entire experiment and keep the same role across repetitions
    • Varying type and constant matching
      • Individuals are matched together for the entire experiment but do not keep the same role across repetitions
    • Constant type and varying matching
      • Individuals are not matched together for the entire experiment but keep the same role across repetitions
    • Varying type and varying matching
      • Individuals are not matched together for the entire experiment and do not keep the same role across repetitions
  • Changes in roles and matching can be explicitly modeled by design or be random
    • In the code provided below we randomize
      • Safe and common approach

Constant type and constant matching

  • Round 1

- Round 2

Varying type and constant matching

  • Round 1

  • Round 2

Constant type and varying matching

  • Round 1

  • Round 2

Varying type and varying matching

  • Round 1

  • Round 2

oTree code for roles and matching

models.py

  • Roles and matching are governed by the file models.py
    • This file manages the “structure” of your experiment
      • The “database”
  • 4 alternative protocols are presented here
    • Constant type and constant matching (CT/CM)
    • Varying type and constant matching (VT/CM)
    • Constant type and varying matching (CT/VM)
    • Varying type and varying matching (VT/VM)

Constant type and constant matching (CT/CM)

CT/CM: commented code

class Constants(BaseConstants):
# define here the constants of the session
    name_in_url = 'groups_roles'
    # label that appears in browser
    players_per_group = 2
    # how many players in each group (important for matching!)
    num_rounds = 2
    # how many repetitions (important for matching!)

class Subsession(BaseSubsession):
#group and types are defined in the Subsession class
    def creating_session(self):
    #to set initial values in the subsession
#***************************************************************************
# START code to generate matching and roles in Round 1
#***************************************************************************
        if self.round_number == 1:
        # the following code is executed only id round is == 1
        # round_number -> is a built-in function that gives the current round number
            self.group_randomly()
            # group_randomly() -> built-in function that  shuffles players randomly
            for g in self.get_groups():
            # get_groups() -> returns a list of all the groups in the subsession.
            # loop through the groups in the subsession
                for p in g.get_players():
                # get_players() -> returns a list of all the players in the subsession.
                # loop through the players in the subsession (p is a player)
                    if p.id_in_group % 2 == 0:
                    # id_in_group -> player's attribute (unique identifier)
                    # if the id is even (via modulo operator)
                        p.participant.vars['type'] = 'BLUE'
                        # the participant is assigned to type "BLUE"
                        # participant.vars is a dictionary that can store any data.
                        # information stored participant.vars persists across apps
                        # this piece of information is not saved in the data exported, do not store here info you need for your analysis
                    else:
                      # if the participant id is odd
                        p.participant.vars['type'] = 'GREEN'
                        # the participant is assigned to type "GREEN"
                    p.type = p.participant.vars['type']
                    # "copy" the value to variable "type" in players space
                    # the variable type must be defined in class Player (see below)
#***************************************************************************
# END code to generate matching and roles in Round 1
#***************************************************************************
#***************************************************************************
# START code to generate matching and types in round >1
#***************************************************************************

        else:
          # if round is not round 1 (see the indenting)
            self.group_like_round(1)
            # perform matching like in round 1 (partner matching)
#***************************************************************************
# END code to generate matching and roles
#***************************************************************************

class Player(BasePlayer):
    type = models.StringField() # this is a string variable that will be filled with player's type (see above)

class Group(BaseGroup):
    pass

Varying type and constant matching (VT/CM)

VT/CM: commented code

class Constants(BaseConstants):
# define here the constants of the session
    name_in_url = 'groups_roles'
    # label that appears in browser
    players_per_group = 2
    # how many players in each group (important for matching!)
    num_rounds = 2
    # how many repetitions (important for matching!)

import random
# import module random
class Subsession(BaseSubsession):
#group and types are defined in the Subsession class
    def creating_session(self):
    #to set initial values in the subsession
#***************************************************************************
# START code to generate matching and types in round 1
#***************************************************************************
        if self.round_number == 1:
        # the following code is executed only id round is == 1
        # round_number -> is a built-in function that gives the current round number
            self.group_randomly()# built-in function
            # group_randomly() -> built-in function that  shuffles players randomly
            rdm=random.randint(0, 1)
            # assign a random value, either 0 or 1, to variable rdm
            for g in self.get_groups():
            #get_groups() -> returns a list of all the groups in the subsession.
             # loop through the groups in the subsession
                for p in g.get_players():
                # get_players() -> returns a list of all the players in the subsession.
                # loop through the players in the subsession (p is a player)
#***************************************************************************
# matching and types when random is 1 - > even = BLUE, odd= GREEN
#***************************************************************************
                    if rdm==1:
                    # the following code is executed if rdm is 1
                        if p.id_in_group % 2 == 0:
                        # id_in_group -> player's attribute (unique identifier)
                        # if the id is even (via modulo operator)
                            p.participant.vars['type'] = 'BLUE'
                            # the participant is assigned to type "BLUE"
                            # participant.vars is a dictionary that can store any data.
                            # information stored participant.vars persists across apps
                            # this piece of information is not saved in the data exported, do not store here info you need for your analysis
                          else:
                          # if the participant id is odd
                            p.participant.vars['type'] = 'GREEN'
                            # the participant is assigned to type "GREEN"
                          p.type = p.participant.vars['type']
                          # "copy" the value to variable "type" in players space
                          # the variable type must be defined in class Player (see below)
#***************************************************************************
# matching and types when random is 1 - > even = GREEN, odd= BLUE
#***************************************************************************
                    else:
                    # the following code is executed if rdm is 0
                          if p.id_in_group % 2 == 0:
                          # see comment above
                              p.participant.vars['type'] = 'GREEN'
                              # see comment above
                          else:
                              p.participant.vars['type'] = 'BLUE'
                              # see comment above
                          p.type = p.participant.vars['type']
                          # see comment above
#***************************************************************************
# END code to generate matching and types in round 1
#***************************************************************************
#***************************************************************************
# START code to generate matching and types in round 1
#***************************************************************************
        else:
        # if round is not round 1 (see the indenting)
            self.group_like_round(1)
            # perform matching like in round 1 (partner matching)
            rdm=random.randint(0, 1)
            # here we run the same code as in round 1 to randomly generate types
            for g in self.get_groups():
                for p in g.get_players():
                    if rdm==1:
                        if p.id_in_group % 2 == 0:
                            p.participant.vars['type'] = 'BLUE'
                        else:
                            p.participant.vars['type'] = 'GREEN'
                        p.type = p.participant.vars['type']
                    else:
                        if p.id_in_group % 2 == 0:
                            p.participant.vars['type'] = 'GREEN'
                        else:
                            p.participant.vars['type'] = 'BLUE'
                    p.type = p.participant.vars['type']
#***************************************************************************
# END code to generate matching and types
#***************************************************************************

class Player(BasePlayer):
    type = models.StringField()# this is a string variable that will be filled with player's type (see above)

class Group(BaseGroup):
    pass

Constant type and varying matching (CT/VM)

CT/VM: commented code

class Constants(BaseConstants):
# define here the constants of the session
    name_in_url = 'groups_roles'
    # label that appears in browser
    players_per_group = 2
    # how many players in each group (important for matching!)
    num_rounds = 2
    # how many repetitions (important for matching!)

class Subsession(BaseSubsession):
#group and types are defined in the Subsession class
    def creating_session(self):
    #to set initial values in the subsession
        self.group_randomly(fixed_id_in_group=True)
        #group_randomly(fixed_id_in_group=True) -> built-in function that  shuffles players randomly but keeps the id constant
        for g in self.get_groups():
        # get_groups() -> returns a list of all the groups in the subsession.
        # loop through the groups in the subsession
            for p in g.get_players():
            # get_players() -> returns a list of all the players in the subsession.
            # loop through the players in the subsession (p is a player)
                if p.id_in_group % 2 == 0:
                  # id_in_group -> player's attribute (unique identifier)
                  # if the id is even (via modulo operator)
                    p.participant.vars['type'] = 'BLUE'
                    # the participant is assigned to type "BLUE"
                    # participant.vars is a dictionary that can store any data.
                    # information stored participant.vars persists across apps
                    # this piece of information is not saved in the data exported, do not store here info you need for your analysis
                else:
                # if the participant id is odd
                    p.participant.vars['type'] = 'GREEN'
                    # the participant is assigned to type "GREEN"
                p.type = p.participant.vars['type']
                # "copy" the value to variable "type" in players space
                # the variable type must be defined in class Player (see below)

class Player(BasePlayer):
    type = models.StringField() # this is a string variable that will be filled with player's type (see above)

class Group(BaseGroup):
    pass

Varying type and varying matching

VT/VM: commented code

class Constants(BaseConstants):
# define here the constants of the session
    name_in_url = 'groups_roles'
    # label that appears in browser
    players_per_group = 2
    # how many players in each group (important for matching!)
    num_rounds = 2
    # how many repetitions (important for matching!)

import random
class Subsession(BaseSubsession):
#group and types are defined in the Subsession class
    def creating_session(self):
    #to set initial values in the subsession
        self.group_randomly()
        #group_randomly() -> built-in function that  shuffles players
        rdm=random.randint(0, 1)
        # assign a random value, either 0 or 1, to variable rdm
        for g in self.get_groups():
        #get_groups() -> returns a list of all the groups in the subsession.
        # loop through the groups in the subsession
            for p in g.get_players():
            # get_players() -> returns a list of all the players in the subsession.
            # loop through the players in the subsession (p is a player)
#***************************************************************************
# matching and types when random is 1 - > even = BLUE, odd= GREEN
#***************************************************************************
                if rdm==1:
                # the following code is executed if rdm is 1
                    if p.id_in_group % 2 == 0:
                    # id_in_group -> player's attribute (unique identifier)
                    # if the id is even (via modulo operator)
                        p.participant.vars['type'] = 'BLUE'
                        # the participant is assigned to type "BLUE"
                        # participant.vars is a dictionary that can store any data.
                        # information stored participant.vars persists across apps
                        # this piece of information is not saved in the data exported, do not store here info you need for your analysis
                    else:
                    # if the participant id is odd
                        p.participant.vars['type'] = 'GREEN'
                        # the participant is assigned to type "GREEN"
                    p.type = p.participant.vars['type']
                    # "copy" the value to variable "type" in players space
                    # the variable type must be defined in class Player (see below)
#***************************************************************************
# matching and types when random is 1 - > even = GREEN, odd= BLUE
#***************************************************************************
                else:
                # the following code is executed if rdm is 0
                    if p.id_in_group % 2 == 0:
                    # see comment above
                        p.participant.vars['type'] = 'GREEN'
                        # see comment above
                    else:
                    # see comment above
                        p.participant.vars['type'] = 'BLUE'
                        # see comment above
                p.type = p.participant.vars['type']
                # see comment above
#***************************************************************************
# END code to generate matching and types
#***************************************************************************
class Player(BasePlayer):
    type = models.StringField()# this is a string variable that will be filled with player's type (see above)

class Group(BaseGroup):
    pass

Assign values

Values

  • Participants are generally endowed with some values (attributes)
    • Unconditionally the same for all participants
      • e.g., endowment
    • Conditional upon some characteristic of the participant
      • e.g., efficiency factors the are related to previous performance in a task

Values: example

  • All players receive and endowment of 10 Euros
  • Green Players are assigned an additional endowment of 1 Euro and Blue players an additional endowment of 2 Euro

Value assignment

  • Take costant type and costant matching

Commented code

class Constants(BaseConstants):
    name_in_url = 'groups_roles'
    players_per_group = 2
    num_rounds = 2
    initial_endowment =c(10)
    # initial_endowment =c(10) -> set here the common endowment that all players receive in currency

class Subsession(BaseSubsession):
    def creating_session(self):
        if self.round_number == 1:
            self.group_randomly()
            print(self.get_group_matrix())
            for g in self.get_groups():
                for p in g.get_players():
                    if p.id_in_group % 2 == 0:
                        p.participant.vars['type'] = 'BLUE'
                    else:
                        p.participant.vars['type'] = 'GREEN'
                    p.type = p.participant.vars['type']
        else:
            self.group_like_round(1)
            print(self.get_group_matrix())
        for p in self.get_players():
            if p.participant.vars['type'] == 'BLUE':
            # the code in this block is for players Blue
                p.participant.vars['additional'] = c(2)
                # each blue player is assigned 2 currencies for the key additional in the participant.vars dictionary
            else:
                p.participant.vars['additional'] = c(1)
                # each blue player is assigned 1 currency for the key additional in the participant.vars dictionary
            p.additional_endow = p.participant.vars['additional']
            # "copy" the value to variable "additional_endow" in players space
            # the variable type must be defined in class Player (see below)
            p.total_endow = p.additional_endow + Constants.initial_endowment
            # "compute the total endowment "copy" the value in players space
            # the variable type must be defined in class Player (see below)

class Player(BasePlayer):
    type = models.StringField()
    additional_endow =  models.CurrencyField()
    #additional_endow -> needed to "store" additional endowment conditional upon types
    total_endow = models.CurrencyField()
    # total_endow -> needed to "store" total endowment conditional upon types
class Group(BaseGroup):
    pass

Thank you

😀

To contact me just write me an email

matteo.ploner@unitn.it

or write me on the forum of the course

Appendix

Assignments

  • Easy

    • Implement a constant type and constant matching with value assignment as follows
      • All types get 10 Euros
        • Green types get additional 2 Euros
        • Blue types are taken away 2 Euros
  • Less easy

    • Implement the following matching protocol
      • 8 periods
      • First 4 periods random matching and varying types (random)
      • Last 4 periods constant matching and fixed types

OTree code

  • The oTree app of this lecture:
Download groups_roles.zip

References