Computerized experiments
Advantages of computerized experiments
- Computerized experiments present several advantages over paper&pencil experiments
- Live interaction
- Dynamic interfaces
- Codified data
- Wide audience
- Web-based experiments
- Specialized softwares are available
- z-Tree (Fischbacher 2007) is the de-facto standard for lab experiments
- Runs locally on Win OS
- See my online lecture notes if interested
- oTree (Chen, Schonger, and Wickens 2016) is an emerging software
- Platform-independent, web-based
- z-Tree (Fischbacher 2007) is the de-facto standard for lab experiments
- Here, we focus on oTree
About oTree
- oTree is a framework based on Python to run controlled experiments
- Games
- e.g., Public Goods Games (PGG)
- Individual decision making
- e.g., Risk elicitation tasks
- Surveys and tests
- e.g., Raven test
- Games
- Support by the community
- Forum
- Code developed by others
- e.g. Holzmeister (2017)
- oTree is open-source,
- Licensed under an adaptation of the MIT license.
- Cite it in the paper when you use it
- Licensed under an adaptation of the MIT license.
Code
- Programming language of oTree is Python
- Popular object-oriented programming language
- Developed in early 90’s by Guido Van Rossum
- OTree’s user interface is based on HTML5
- Supported by modern browser and rich in functionalities
- OTree is based on Django web application framework
- OTree applications are web applications
- All components of oTree are free and open-source
Functioning
- The basic setup consists in
- An app (experiment) written within oTree
- A server computer
- Cloud server, local PC …
- Subjects’ devices with a browser
- PC, Laptop, Tablet, Mobile Phone …
- oTree creates a session on the server and generates links for all participants
- Participants click on the links and are sent to a personal page
- They submit their answers, which are collected by the server
- The experimenter can check the progress on the server
oTree app
Conceptual overview
Session | |||||
---|---|---|---|---|---|
Subsession | Subsession | ||||
Page | Page | Page | Page | Page | Page |
- Sessions
- Participants take part in a series of tasks
- Subsessions
- Sections of a session
- EXAMPLE: a PGG is subsession 1 and a questionnaire is subsession 2
- Repetitions of the same task are performed over distinct subsessions (periods)
- Sections of a session
- Page
- Sections of a subsession
- EXAMPLE: the PGG is made of 4 pages (instructions, …) and the questionnaire of 2 pages
- Sections of a subsession
- Groups
- Each subsession can be divided into groups of players
- Groups can be shuffled between subsessions
- Each subsession can be divided into groups of players
Object hierarchy
- oTree’s entities are organized with the following hyerarchy
- Session
- Subsession
- Group
- Player
- Page
- Player
- Group
- Subsession
- A session is a series of subsessions
- A subsession contains multiple groups
- A group contains multiple players
- Each player proceeds through multiple pages
- A group contains multiple players
- A subsession contains multiple groups
- ⚠ player and particcfipant have different meanings
- A Player is a section of a Participant
- Similar to Subsession and Session
- A Player is a section of a Participant
Your first app
- To create an app named “my_first_app” move to the oTree folder
- and create the app
- Move to the folder my_first_app
- You will find the following files
- models.py
- pages.py
- tests.py
- And a subfolder
- templates/my_first_app
- MyPage.html, Results.html
- templates/my_first_app
- You will find the following files
models.py
- Here you define the structure of your data
- 3 data models
- Subsession
- Group
- Player
- These are Python classes (see below)
- A model is basically a database
- Specify columns and their nature
- Integers, strings, …
- Specify columns and their nature
- 3 data models
from otree.api import (
models,
widgets,
BaseConstants,
BaseSubsession,
BaseGroup,
BasePlayer,
Currency as c,
currency_range,
)
author = "Me"
doc = """
This is my first app
"""
class Constants(BaseConstants):
name_in_url = 'my_first_app'
players_per_group = None
num_rounds = 1
class Subsession(BaseSubsession):
pass
class Group(BaseGroup):
pass
class Player(BasePlayer):
pass
templates
- These are the pages that are displayed to participants
- html files that contain informations and forms
- forms are used to collect data
- A default MyPage.html is created
- {% formfields %} will display the forms of the page
- see pages.py
- {% next_button %} will display a button to continue
- {% formfields %} will display the forms of the page
- html files that contain informations and forms
- The HTML can contain “fancy” stuff
- Javascript
- Bootstrap framework
- …
pages.py
- Pages that the participants see are defined in pages.py
- Logic for how to display the HTML templates
- when, how, and what to display
- Logic for how to display the HTML templates
- page_sequence gives the order of pages
- if there are multiple rounds the sequence is repeated
Python
Basics
- A very short introduction to Python
- Based on Learn X in Y minutes, where X=Python Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
- and on Ascher and Lutz (1999)
#####################################################
# Numbers and logical operators
####################################################
# Math is what you would expect
1 + 1 # => 2
8 - 1 # => 7
10 * 2 # => 20
35 / 5 # => 7.0
# Enforce precedence with parentheses
1 + 3 * 2 # => 7
(1 + 3) * 2 # => 8
# Boolean Operators
## Note "and" and "or" are case-sensitive
True and False # => False
False or True # => True
## True and False are actually 1 and 0 but with different keywords
True + True # => 2
True * 8 # => 8
False - 5 # => -5
# Comparisons
## Equality is ==
1 == 1 # => True
2 == 1 # => False
## Inequality is !=
1 != 1 # => False
2 != 1 # => True
## More comparisons
1 < 10 # => True
1 > 10 # => False
2 <= 2 # => True
2 >= 2 # => True
Basics: Strings and variables
- Strings are an ordered collection of characters
# Strings are created with " or '
"This is a string."
'This is also a string.'
# Strings can be added too! But try not to do this.
"Hello " + "world!" # => "Hello world!"
# You can also format using f-strings or formatted string literals (in Python 3.6+)
name = "Reiko"
f"{name} is {len(name)} characters long." # => "Reiko is 5 characters long."
# Strings can be sliced and indexed
name[0] => "R"
name[-2] => "k"
# There are no declarations, only assignments.
# Convention is to use lower_case_with_underscores
some_var = 5
some_var # => 5
Basics: Lists and tuples
- Lists are ordered collections of arbitrary items
- Can contain numbers, strings, and other lists
- Accessed by offset
- As an example, all players in a group
# empty list
li = []
# Prefilled list
other_li = [4, 5, 6]
# Add stuff to the end of a list with append
other_li.append(7) # => li is now [4, 5, 6, 7]
# Remove from the end with pop
other_li.pop() # => 7 li is now [4, 5, 6]
# Access a list like you would any array
other_li [0] # => 4
# Look at the last element
other_li [-1] # => 6
# You can look at ranges with slice syntax.
# The start index is included, the end index is not
# (It's a closed/open range for you mathy types.)
other_li [1:3] # Return list from index 1 to 3 => [5, 6]
# Remove arbitrary elements from a list with "del"
del other_li[2] # li is now [4, 5]
# Insert an element at a specific index
other_li.insert(1, 2) # li is now [4, 2, 5] again
# Get the index of the first item found matching the argument
other_li.index(2) # => 1
# Check for existence in a list with "in"
1 in other_li # => False
# Examine the length with "len()"
len(other_li) # => 3
# Tuples are like lists but are immutable.
tup = (1, 2, 3)
tup[0] # => 1
Basics: Dictionaries
- Useful way to collect and organize information
- As an example, payoffs of players
# Dictionaries store mappings from keys to values
empty_dict = {}
# Here is a prefilled dictionary
filled_dict = {"one": 1, "two": 2, "three": 3}
# Look up values with []
filled_dict["one"] # => 1
# Get all keys as an iterable with "keys()". We need to wrap the call in list()
# to turn it into a list. Order of insertion is preserved (in Python >=3.7)
list(filled_dict.keys()) # => ["one", "two", "three"]
# Get all values as an iterable with "values()". Once again we need to wrap it
# in list() to get it out of the iterable.
list(filled_dict.values()) # => [1, 2, 3]
# Check for existence of keys in a dictionary with "in"
"one" in filled_dict # => True
1 in filled_dict # => False
# Adding to a dictionary
filled_dict.update({"four":4}) # => {"one": 1, "two": 2, "three": 3, "four": 4}
filled_dict["four"] = 4 # another way to add to dict
# Remove keys from a dictionary with del
del filled_dict["one"] # Removes the key "one" from filled dict
Basics: Control Flow and Iteration
- Many times you need to iterate through variables
- As an example, collect all choice sof participants in a group
# Let's just make a variable
some_var = 5
# Here is an if statement. Indentation is significant in Python!
# Convention is to use four spaces, not tabs.
# This prints "some_var is smaller than 10"
# if conditions
if some_var > 10:
print("some_var is totally bigger than 10.")
elif some_var < 10: # This elif clause is optional.
print("some_var is smaller than 10.")
else: # This is optional too.
print("some_var is indeed 10.")
# loops
for i in range(4):
print(i)
# =>0
# =>1
# =>2
# =>3
for animal in ["dog", "cat", "mouse"]:
# You can use format() to interpolate formatted strings
print("{} is a mammal".format(animal))
# =>dog is a mammal
# =>cat is a mammal
# =>mouse is a mammal
# We can use list comprehensions to loop or filter
numbers = [3, 4, 5, 6, 7]
[x for x in numbers if x > 5] # => [6, 7]
- Indenting is important!
Basics: Functions
- Functions are a device that groups a bunch of statements
Basics: Classes
- Classes are main object-oriented-programming (OOP) in Python
- Class objects provide default behavior
- The class statement creates a class object and assigns it a name
- Assignments inside class statements make class attributes
- Class attributes export object state and behavior
- def statements inside class generate a method
- Instance objects are generated from classes
- Calling a class object makes a new instance object
- Each instance object inherits class attributes and gets its own namespace
- Assignment to self in methods make per-instance attributes
- self refers to the instance being processed
- Class objects provide default behavior
Classes: An Example
- Pokemon is a class with some properties
- Height
- Weight
- Category
- Two instances of the class
Charmander:
Height: 2’ 00";
Weight 18.7 lbs;
Category: Lizard
Bulbasaur:
Height: 2’ 04";
Weight: 15.2 lbs;
Category: Seed
Classes: An Example (ii)
class Pokemon:
nature = "Pokemon"# this property is shared by all instances
def __init__(self, name, height, weight, category):
# Assign the argument to the instance's name attribute
self.name = name
self.height = height
self.weight = weight
self.category = category
self.comment=[]
# to add a comment
def add_comment(self, comment):
self.comment.append(comment)
# convert height and weight into metric
def convert_metric(self):
# conversion rates
feet_conv_cm=30.5
inch_conv_cm=2.54
lbs_conv_kg=0.453592
self.height_cm = int(self.height.split("'")[0])*feet_conv_cm+int(self.height.split("'")[1])*inch_conv_cm
self.weight_kg = self.weight*lbs_conv_kg
# print the result
print("Height (cm): %f, Weight (Kg): %f" %(self.height_cm, self.weight_kg))
Classes: An Example (iii)
# Create two instances of the class
p_1 = Pokemon("Charmander","2' 5''", 18.7, "Lizard")
p_2 = Pokemon("Bulbasaur","2' 04''", 15.2, "Seed")
# Add a comment
p_1.add_comment("Its ability is Blaze")
p_2.add_comment("Its ability is Overgrow")
# Convert their measures in metric system
p_1.convert_metric()
p_2.convert_metric()
# Who is taller?
if p_1.height>p_2.height:
print(p_1.name + " is taller")
elif p_1.height<p_2.height:
print(p_2.name + " is taller")
else:
print(p_2.name + " and" + p_1.name + "are equally taller")
Thank you
😎
To contact me just write me an email
or write me on the forum of the course
Assignment
Software required during the course
- Install the following software on a machine you have access to
- In Lab 1 the software is installed on each machine
- All the software is open source and freely downloadable
- You find below the links to OS WIN installations, installations for other OS are available online
oTree
Python
Without this software you will not be able to follow next lectures
Create an instance of class Pokemon
- Consider the class Pokemon we illustrated above
- Create a new instance of the class
- Charizard: Height: 5’ 07"; Weight: 199.5 lbs; Category: Flame
- Check whether Charizard is taller than Bulbasaur
References
Ascher, David, and Mark Lutz. 1999. Learning Python. O’Reilly.
Chen, Daniel L, Martin Schonger, and Chris Wickens. 2016. “OTree—an Open-Source Platform for Laboratory, Online, and Field Experiments.” Journal of Behavioral and Experimental Finance 9. Elsevier: 88–97.
Fischbacher, Urs. 2007. “Z-Tree: Zurich Toolbox for Ready-Made Economic Experiments.” Experimental Economics 10 (2). Springer: 171–78.
Holzmeister, Felix. 2017. “OTree: Ready-Made Apps for Risk Preference Elicitation Methods.” Journal of Behavioral and Experimental Finance 16. Elsevier: 33–38.