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
- Constant type and constant matching
- 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
- In the code provided below we randomize
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”
- This file manages the “structure” of your experiment
- 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)
- This is the code in models.py
class Constants(BaseConstants):
name_in_url = 'groups_roles'
players_per_group = 2
num_rounds = 2
class Subsession(BaseSubsession):
def creating_session(self):
if self.round_number == 1:
self.group_randomly()
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)
class Player(BasePlayer):
type = models.StringField()
class Group(BaseGroup):
pass
- This is the assignment to groups and roles we get
- Round 1
- Matching
- [[<Player 6>, <Player 7>], [<Player 3>, <Player 5>], [<Player 8>, <Player 2>], [<Player 1>, <Player 4>]]
- Type
- [[1, ‘GREEN’], [2, ‘BLUE’], [3, ‘GREEN’], [4, ‘BLUE’], [5, ‘BLUE’], [6, ‘GREEN’], [7, ‘BLUE’], [8, ‘GREEN’]]
- Matching
- Round 2
- Matching
- [[<Player 6>, <Player 7>], [<Player 3>, <Player 5>], [<Player 8>, <Player 2>], [<Player 1>, <Player 4>]]
- Type
- [[1, ‘GREEN’], [2, ‘BLUE’], [3, ‘GREEN’], [4, ‘BLUE’], [5, ‘BLUE’], [6, ‘GREEN’], [7, ‘BLUE’], [8, ‘GREEN’]]
- Matching
- […]
- Round 1
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)
class Constants(BaseConstants):
name_in_url = 'groups_roles'
players_per_group = 2
num_rounds = 2
import random
class Subsession(BaseSubsession):
def creating_session(self):
if self.round_number == 1:
self.group_randomly()
print(self.get_group_matrix())
rdm=random.randint(0, 1)
print(rdm)
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']
else:
self.group_like_round(1)
rdm=random.randint(0, 1)
print(rdm)
for g in self.get_groups():
for p in g.get_players():
if rdm==1: #this way we randomize role accordin to id in group
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']
class Player(BasePlayer):
type = models.StringField()
class Group(BaseGroup):
pass
- Round 1
- Matching
- [[<Player 3>, <Player 1>], [<Player 2>, <Player 8>], [<Player 5>, <Player 6>], [<Player 7>, <Player 4>]]
- Type
- [[1, ‘BLUE’], [2, ‘GREEN’], [3, ‘GREEN’], [4, ‘BLUE’], [5, ‘GREEN’], [6, ‘BLUE’], [7, ‘GREEN’], [8, ‘BLUE’]]
- Matching
- Round 2
- Matching
- [[<Player 3>, <Player 1>], [<Player 2>, <Player 8>], [<Player 5>, <Player 6>], [<Player 7>, <Player 4>]]
- Type
- [[1, ‘GREEN’], [2, ‘BLUE’], [3, ‘BLUE’], [4, ‘GREEN’], [5, ‘BLUE’], [6, ‘GREEN’], [7, ‘BLUE’], [8, ‘GREEN’]]
- Matching
- […]
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)
class Constants(BaseConstants):
name_in_url = 'groups_roles'
players_per_group = 2
num_rounds = 2
class Subsession(BaseSubsession):
def creating_session(self):
self.group_randomly(fixed_id_in_group=True)
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']
for p in self.get_players():
if p.participant.vars['type'] == 'BLUE':
p.participant.vars['value'] = c(10)
else:
p.participant.vars['value'] = c(0)
p.value = p.participant.vars['value']
class Player(BasePlayer):
type = models.StringField()
class Group(BaseGroup):
pass
- Round 1
- Matching
- [[<Player 3>, <Player 2>], [<Player 1>, <Player 8>], [<Player 5>, <Player 4>], [<Player 7>, <Player 6>]]
- Type
- [[1, ‘GREEN’], [2, ‘BLUE’], [3, ‘GREEN’], [4, ‘BLUE’], [5, ‘GREEN’], [6, ‘BLUE’], [7, ‘GREEN’], [8, ‘BLUE’]]
- Matching
- Round 2
- Matching
- [[<Player 1>, <Player 4>], [<Player 7>, <Player 8>], [<Player 5>, <Player 6>], [<Player 3>, <Player 2>]]
- Type
- [[1, ‘GREEN’], [2, ‘BLUE’], [3, ‘GREEN’], [4, ‘BLUE’], [5, ‘GREEN’], [6, ‘BLUE’], [7, ‘GREEN’], [8, ‘BLUE’]]
- Matching
- […]
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
class Constants(BaseConstants):
name_in_url = 'groups_roles'
players_per_group = 2
num_rounds = 2
import random
class Subsession(BaseSubsession):
def creating_session(self):
self.group_randomly()#
rdm=random.randint(0, 1)
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']
print(self.get_group_matrix())
class Player(BasePlayer):
type = models.StringField()
class Group(BaseGroup):
pass
- Round 1
- Matching
- [[<Player 4>, <Player 1>], [<Player 8>, <Player 3>], [<Player 7>, <Player 2>], [<Player 5>, <Player 6>]]
- Type
- [[1, ‘GREEN’], [2, ‘GREEN’], [3, ‘GREEN’], [4, ‘BLUE’], [5, ‘BLUE’], [6, ‘GREEN’], [7, ‘BLUE’], [8, ‘BLUE’]]
- Matching
- Round 2
- Matching
- [[<Player 6>, <Player 8>], [<Player 3>, <Player 1>], [<Player 4>, <Player 7>], [<Player 5>, <Player 2>]]
- Type
- [[1, ‘BLUE’], [2, ‘BLUE’], [3, ‘GREEN’], [4, ‘GREEN’], [5, ‘GREEN’], [6, ‘GREEN’], [7, ‘BLUE’], [8, ‘BLUE’]]
- 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
- Unconditionally the same for all participants
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
class Constants(BaseConstants):
name_in_url = 'groups_roles'
players_per_group = 2
num_rounds = 2
initial_endowment =c(10)
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':
p.participant.vars['additional'] = c(2)
else:
p.participant.vars['additional'] = c(1)
p.additional_endow = p.participant.vars['additional']
p.total_endow = p.additional_endow + Constants.initial_endowment
class Player(BasePlayer):
type = models.StringField()
additional_endow = models.CurrencyField()
total_endow = models.CurrencyField()
class Group(BaseGroup):
pass
- Round 1
- Matching
- [[<Player 8>, <Player 1>], [<Player 6>, <Player 7>], [<Player 3>, <Player 5>], [<Player 2>, <Player 4>]]
- Type and value
- [[1, ‘BLUE’, Currency(12)], [2, ‘GREEN’, Currency(11)], [3, ‘GREEN’, Currency(11)], [4, ‘BLUE’, Currency(12)], [5, ‘BLUE’, Currency(12)], [6, ‘GREEN’, Currency(11)], [7, ‘BLUE’, Currency(12)], [8, ‘GREEN’, Currency(11)]]
- Matching
- Round 2
- Matching
- [[<Player 8>, <Player 1>], [<Player 6>, <Player 7>], [<Player 3>, <Player 5>], [<Player 2>, <Player 4>]]
- Type and value
- [[1, ‘BLUE’, Currency(12)], [2, ‘GREEN’, Currency(11)], [3, ‘GREEN’, Currency(11)], [4, ‘BLUE’, Currency(12)], [5, ‘BLUE’, Currency(12)], [6, ‘GREEN’, Currency(11)], [7, ‘BLUE’, Currency(12)], [8, ‘GREEN’, Currency(11)]]
- 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
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
- All types get 10 Euros
- Implement a constant type and constant matching with value assignment as follows
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
- Implement the following matching protocol