Scikit-oTree Tutorial

Welcome to the Scikit-oTree tutorial. This package aims to integrate any experiment developed on-top of oTree, with the Python Scientific-Stack; alowing the scientists to access a big collection of tools for analyse the experimental data.


In [1]:
import skotree
skotree.VERSION


Out[1]:
'0.5'

Philosophy

1. The data must be processed only by the oTree deployment.

Scikit-oTree don't preprocess any data from the experiment. All the information are preserved exactly as any traditional export from oTree; the project only take this data and present it.

2. The environment for analysis must not be modified.

oTree uses some global configuration to make it run. Scikit-oTree don't store any global configuration alowwing to load data from different experiments without problems. All the oTree related processing always happen in an external process.

3. Only one data type for the data.

The data are always presented as a Pandas DataFrame

Installation

To install Scikit-oTree you must has Python and PIP. You can found a comprensive tutorial to install it here.

After that you only need to run

pip install -U scikit-otree

Local - Loading the experiment

To load your experiment you need to provide the location of the oTree deployment. This is the same location where the setting.py lives.


In [2]:
# this load the library
import skotree

# this load the experiment located
# in the directory tests and
experiment = skotree.oTree("./tests")
experiment


Out[2]:
<oTree@/home/juan/proyectos/skotree/src/tests>

The previous code make a lot of things in background:

  1. First create an extra process deatached from the local one to extract all oTree related settings.
  2. Wait until the process to end.
  3. Check the result of the process and store the settings as atrribute for experiment object.

Let's check the result


In [3]:
experiment.settings


Out[3]:
<django.conf.Settings at 0x7fb702573ac8>

This is the traditional object that you obtain in any oTree experiment if you write

from django.conf import settings

Now let's check some information about the experiment, for example all the exiting oTree apps.


In [4]:
experiment.lsapps()


Out[4]:
['matching_pennies']

or maybe you want to see all the sessiong configured that uses all this apps


In [5]:
experiment.lssessions()


Out[5]:
['matching_pennies']

Yikes! the app and the session has the same name. Let's check the full session configuration.


In [6]:
experiment.session_config("matching_pennies")


Out[6]:
{'app_sequence': ['matching_pennies'],
 'display_name': 'Matching Pennies',
 'doc': '',
 'mturk_hit_settings': {'description': 'Description for your experiment',
  'expiration_hours': 168,
  'frame_height': 500,
  'keywords': ['bonus', 'study'],
  'minutes_allotted_per_assignment': 60,
  'preview_template': 'global/MTurkPreview.html',
  'qualification_requirements': [],
  'title': 'Title for your experiment'},
 'name': 'matching_pennies',
 'num_demo_participants': 2,
 'participation_fee': 0.0,
 'real_world_currency_per_point': 0.0}

Finally you can access any content of the settings object ussing the attribute showed before. For example, maybe you want to see the "currency code"


In [7]:
experiment.settings.REAL_WORLD_CURRENCY_CODE


Out[7]:
'USD'

The Data

Lets check the oTree server data tab

As you can see 4 kind of data can be exported from any experiment.

1. All app

This generates one DataFrame with one row per participant, and all rounds are stacjed horizontally. For Scikit-oTree this functionallity are exposed as all_data() method


In [8]:
all_data = experiment.all_data()
all_data


Out[8]:
participant.id_in_session participant.code participant.label participant._is_bot participant._index_in_pages participant._max_page_index participant._current_app_name participant._round_number participant._current_page_name participant.ip_address ... matching_pennies.3.player.is_winner matching_pennies.3.player.payoff matching_pennies.3.group.id_in_subsession matching_pennies.3.subsession.round_number matching_pennies.4.player.id_in_group matching_pennies.4.player.penny_side matching_pennies.4.player.is_winner matching_pennies.4.player.payoff matching_pennies.4.group.id_in_subsession matching_pennies.4.subsession.round_number
0 1 bqkfd73t NaN 0 0 12 NaN NaN NaN NaN ... NaN 0 1 3 2 NaN NaN 0 1 4
1 2 5cznwlly NaN 0 0 12 NaN NaN NaN NaN ... NaN 0 1 3 1 NaN NaN 0 1 4
2 1 5b8zc5hr NaN 0 12 12 matching_pennies 4.0 ResultsSummary 127.0.0.1 ... 0.0 0 1 3 2 Tails 1.0 100 1 4
3 2 x1f4ua8y NaN 0 12 12 matching_pennies 4.0 ResultsSummary 127.0.0.1 ... 1.0 0 1 3 1 Tails 0.0 0 1 4

4 rows × 49 columns

2. Per-App Data

These data-frame contain a row for each player in the given app. If there are multiple rounds, there will be multiple rows for the same participant. To access this information you need to provide the application name to the method app_data()


In [9]:
data = experiment.app_data("matching_pennies")
data


Out[9]:
participant.id_in_session participant.code participant.label participant._is_bot participant._index_in_pages participant._max_page_index participant._current_app_name participant._round_number participant._current_page_name participant.ip_address ... player.payoff group.id_in_subsession subsession.round_number session.code session.label session.experimenter_name session.mturk_HITId session.mturk_HITGroupId session.comment session.is_demo
0 1 bqkfd73t NaN 0 0 12 NaN NaN NaN NaN ... 0 1 1 nvnpimbf NaN NaN NaN NaN NaN 0
1 2 5cznwlly NaN 0 0 12 NaN NaN NaN NaN ... 0 1 1 nvnpimbf NaN NaN NaN NaN NaN 0
2 1 bqkfd73t NaN 0 0 12 NaN NaN NaN NaN ... 0 1 2 nvnpimbf NaN NaN NaN NaN NaN 0
3 2 5cznwlly NaN 0 0 12 NaN NaN NaN NaN ... 0 1 2 nvnpimbf NaN NaN NaN NaN NaN 0
4 1 bqkfd73t NaN 0 0 12 NaN NaN NaN NaN ... 0 1 3 nvnpimbf NaN NaN NaN NaN NaN 0
5 2 5cznwlly NaN 0 0 12 NaN NaN NaN NaN ... 0 1 3 nvnpimbf NaN NaN NaN NaN NaN 0
6 1 bqkfd73t NaN 0 0 12 NaN NaN NaN NaN ... 0 1 4 nvnpimbf NaN NaN NaN NaN NaN 0
7 2 5cznwlly NaN 0 0 12 NaN NaN NaN NaN ... 0 1 4 nvnpimbf NaN NaN NaN NaN NaN 0
8 1 5b8zc5hr NaN 0 12 12 matching_pennies 4.0 ResultsSummary 127.0.0.1 ... 0 1 1 gh84ltgl NaN NaN NaN NaN NaN 1
9 2 x1f4ua8y NaN 0 12 12 matching_pennies 4.0 ResultsSummary 127.0.0.1 ... 0 1 1 gh84ltgl NaN NaN NaN NaN NaN 1
10 1 5b8zc5hr NaN 0 12 12 matching_pennies 4.0 ResultsSummary 127.0.0.1 ... 0 1 2 gh84ltgl NaN NaN NaN NaN NaN 1
11 2 x1f4ua8y NaN 0 12 12 matching_pennies 4.0 ResultsSummary 127.0.0.1 ... 0 1 2 gh84ltgl NaN NaN NaN NaN NaN 1
12 1 5b8zc5hr NaN 0 12 12 matching_pennies 4.0 ResultsSummary 127.0.0.1 ... 0 1 3 gh84ltgl NaN NaN NaN NaN NaN 1
13 2 x1f4ua8y NaN 0 12 12 matching_pennies 4.0 ResultsSummary 127.0.0.1 ... 0 1 3 gh84ltgl NaN NaN NaN NaN NaN 1
14 1 5b8zc5hr NaN 0 12 12 matching_pennies 4.0 ResultsSummary 127.0.0.1 ... 100 1 4 gh84ltgl NaN NaN NaN NaN NaN 1
15 2 x1f4ua8y NaN 0 12 12 matching_pennies 4.0 ResultsSummary 127.0.0.1 ... 0 1 4 gh84ltgl NaN NaN NaN NaN NaN 1

16 rows × 28 columns

With the power of pandas.DataFrame you can easily filter the data


In [10]:
filtered = data[["participant.code", "player.penny_side", "player.payoff"]]
filtered


Out[10]:
participant.code player.penny_side player.payoff
0 bqkfd73t NaN 0
1 5cznwlly NaN 0
2 bqkfd73t NaN 0
3 5cznwlly NaN 0
4 bqkfd73t NaN 0
5 5cznwlly NaN 0
6 bqkfd73t NaN 0
7 5cznwlly NaN 0
8 5b8zc5hr Heads 0
9 x1f4ua8y Heads 0
10 5b8zc5hr Heads 0
11 x1f4ua8y Tails 0
12 5b8zc5hr Heads 0
13 x1f4ua8y Tails 0
14 5b8zc5hr Tails 100
15 x1f4ua8y Tails 0

Describe the data


In [11]:
filtered.describe()


Out[11]:
player.payoff
count 16.00
mean 6.25
std 25.00
min 0.00
25% 0.00
50% 0.00
75% 0.00
max 100.00

group by participant


In [12]:
group = filtered.groupby("participant.code")
group.describe()


Out[12]:
player.payoff
count mean std min 25% 50% 75% max
participant.code
5b8zc5hr 4.0 25.0 50.0 0.0 0.0 0.0 25.0 100.0
5cznwlly 4.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
bqkfd73t 4.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
x1f4ua8y 4.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0

or check all the columns availables


In [13]:
data.columns


Out[13]:
Index(['participant.id_in_session', 'participant.code', 'participant.label',
       'participant._is_bot', 'participant._index_in_pages',
       'participant._max_page_index', 'participant._current_app_name',
       'participant._round_number', 'participant._current_page_name',
       'participant.ip_address', 'participant.time_started',
       'participant.visited', 'participant.mturk_worker_id',
       'participant.mturk_assignment_id', 'participant.payoff',
       'player.id_in_group', 'player.penny_side', 'player.is_winner',
       'player.payoff', 'group.id_in_subsession', 'subsession.round_number',
       'session.code', 'session.label', 'session.experimenter_name',
       'session.mturk_HITId', 'session.mturk_HITGroupId', 'session.comment',
       'session.is_demo'],
      dtype='object')

3. Per-App Documentation

The code

experiment.app_doc("matching_pennies")

returns the full documentation about the data retrieved by app_data()

4. Time spent on each page

Time spent on each page


In [14]:
tspent = experiment.time_spent()
tspent


Out[14]:
session_id participant__id_in_session participant__code page_index app_name page_name time_stamp seconds_on_page subsession_pk auto_submitted
0 2 1 5b8zc5hr 1 matching_pennies Choice 1509514681 4 5 False
1 2 1 5b8zc5hr 2 matching_pennies ResultsWaitPage 1509514686 5 5 False
2 2 1 5b8zc5hr 4 matching_pennies Choice 1509514692 6 6 False
3 2 1 5b8zc5hr 5 matching_pennies ResultsWaitPage 1509514693 1 6 False
4 2 1 5b8zc5hr 7 matching_pennies Choice 1509514696 3 7 False
5 2 1 5b8zc5hr 8 matching_pennies ResultsWaitPage 1509514701 5 7 False
6 2 1 5b8zc5hr 10 matching_pennies Choice 1509514706 5 8 False
7 2 1 5b8zc5hr 11 matching_pennies ResultsWaitPage 1509514707 1 8 False
8 2 2 x1f4ua8y 1 matching_pennies Choice 1509514685 7 5 False
9 2 2 x1f4ua8y 2 matching_pennies ResultsWaitPage 1509514685 0 5 False
10 2 2 x1f4ua8y 4 matching_pennies Choice 1509514689 4 6 False
11 2 2 x1f4ua8y 5 matching_pennies ResultsWaitPage 1509514694 5 6 False
12 2 2 x1f4ua8y 7 matching_pennies Choice 1509514699 5 7 False
13 2 2 x1f4ua8y 8 matching_pennies ResultsWaitPage 1509514700 1 7 False
14 2 2 x1f4ua8y 10 matching_pennies Choice 1509514702 2 8 False
15 2 2 x1f4ua8y 11 matching_pennies ResultsWaitPage 1509514707 5 8 False

In [15]:
# check the available columns
tspent.columns


Out[15]:
Index(['session_id', 'participant__id_in_session', 'participant__code',
       'page_index', 'app_name', 'page_name', 'time_stamp', 'seconds_on_page',
       'subsession_pk', 'auto_submitted'],
      dtype='object')

In [16]:
# filter only the most important columns
tspent = tspent[["participant__code", "page_index", "seconds_on_page"]]
tspent


Out[16]:
participant__code page_index seconds_on_page
0 5b8zc5hr 1 4
1 5b8zc5hr 2 5
2 5b8zc5hr 4 6
3 5b8zc5hr 5 1
4 5b8zc5hr 7 3
5 5b8zc5hr 8 5
6 5b8zc5hr 10 5
7 5b8zc5hr 11 1
8 x1f4ua8y 1 7
9 x1f4ua8y 2 0
10 x1f4ua8y 4 4
11 x1f4ua8y 5 5
12 x1f4ua8y 7 5
13 x1f4ua8y 8 1
14 x1f4ua8y 10 2
15 x1f4ua8y 11 5

In [17]:
# lets describe the time expent by page
tspent.groupby("page_index").describe()


Out[17]:
seconds_on_page
count mean std min 25% 50% 75% max
page_index
1 2.0 5.5 2.121320 4.0 4.75 5.5 6.25 7.0
2 2.0 2.5 3.535534 0.0 1.25 2.5 3.75 5.0
4 2.0 5.0 1.414214 4.0 4.50 5.0 5.50 6.0
5 2.0 3.0 2.828427 1.0 2.00 3.0 4.00 5.0
7 2.0 4.0 1.414214 3.0 3.50 4.0 4.50 5.0
8 2.0 3.0 2.828427 1.0 2.00 3.0 4.00 5.0
10 2.0 3.5 2.121320 2.0 2.75 3.5 4.25 5.0
11 2.0 3.0 2.828427 1.0 2.00 3.0 4.00 5.0

In [18]:
# and lets make a plot but grouped by participant
%matplotlib inline 
tspent.groupby("participant__code")[["seconds_on_page"]].plot();


Note


This only show a simple example in how to use pandas.DataFrame to understand more, please check
https://pandas.pydata.org/pandas-docs/stable/tutorials.html

Simulate with bots

Scikit-oTree offers out of the box the posibility to run the oTree bot-based-tests and retrieve all the data generated by them.

The method bot_data() consume two arguments:

  1. The name of the session to simulate
  2. The number of participant

And the return a dict like object (called CSVStorage) whith the same attributes as application has the session in the app_sequence key.


In [19]:
storage = experiment.bot_data("matching_pennies", 4)
storage


Running bots, please wait...
[INFO|2017-11-01 05:38:44,513] skotree > Running bots, please wait...
Out[19]:
<CSVStore({matching_pennies})>

as you can see the only available app (as we see before) is the matching_pennies.

Lets extract the data


In [20]:
storage["matching_pennies"]


Out[20]:
participant.id_in_session participant.code participant.label participant._is_bot participant._index_in_pages participant._max_page_index participant._current_app_name participant._round_number participant._current_page_name participant.ip_address ... player.payoff group.id_in_subsession subsession.round_number session.code session.label session.experimenter_name session.mturk_HITId session.mturk_HITGroupId session.comment session.is_demo
0 1 n66opzy5 NaN 1 12 12 matching_pennies 4 ResultsSummary 127.0.0.1 ... 0 1 1 fiezcoi1 NaN NaN NaN NaN NaN 0
1 2 hjvqfo1o NaN 1 12 12 matching_pennies 4 ResultsSummary 127.0.0.1 ... 0 1 1 fiezcoi1 NaN NaN NaN NaN NaN 0
2 3 gq6vxupy NaN 1 12 12 matching_pennies 4 ResultsSummary 127.0.0.1 ... 0 2 1 fiezcoi1 NaN NaN NaN NaN NaN 0
3 4 x80f6pv3 NaN 1 12 12 matching_pennies 4 ResultsSummary 127.0.0.1 ... 0 2 1 fiezcoi1 NaN NaN NaN NaN NaN 0
4 1 n66opzy5 NaN 1 12 12 matching_pennies 4 ResultsSummary 127.0.0.1 ... 0 1 2 fiezcoi1 NaN NaN NaN NaN NaN 0
5 2 hjvqfo1o NaN 1 12 12 matching_pennies 4 ResultsSummary 127.0.0.1 ... 0 1 2 fiezcoi1 NaN NaN NaN NaN NaN 0
6 3 gq6vxupy NaN 1 12 12 matching_pennies 4 ResultsSummary 127.0.0.1 ... 0 2 2 fiezcoi1 NaN NaN NaN NaN NaN 0
7 4 x80f6pv3 NaN 1 12 12 matching_pennies 4 ResultsSummary 127.0.0.1 ... 0 2 2 fiezcoi1 NaN NaN NaN NaN NaN 0
8 1 n66opzy5 NaN 1 12 12 matching_pennies 4 ResultsSummary 127.0.0.1 ... 100 1 3 fiezcoi1 NaN NaN NaN NaN NaN 0
9 2 hjvqfo1o NaN 1 12 12 matching_pennies 4 ResultsSummary 127.0.0.1 ... 0 1 3 fiezcoi1 NaN NaN NaN NaN NaN 0
10 3 gq6vxupy NaN 1 12 12 matching_pennies 4 ResultsSummary 127.0.0.1 ... 100 2 3 fiezcoi1 NaN NaN NaN NaN NaN 0
11 4 x80f6pv3 NaN 1 12 12 matching_pennies 4 ResultsSummary 127.0.0.1 ... 0 2 3 fiezcoi1 NaN NaN NaN NaN NaN 0
12 1 n66opzy5 NaN 1 12 12 matching_pennies 4 ResultsSummary 127.0.0.1 ... 0 1 4 fiezcoi1 NaN NaN NaN NaN NaN 0
13 2 hjvqfo1o NaN 1 12 12 matching_pennies 4 ResultsSummary 127.0.0.1 ... 0 1 4 fiezcoi1 NaN NaN NaN NaN NaN 0
14 3 gq6vxupy NaN 1 12 12 matching_pennies 4 ResultsSummary 127.0.0.1 ... 0 2 4 fiezcoi1 NaN NaN NaN NaN NaN 0
15 4 x80f6pv3 NaN 1 12 12 matching_pennies 4 ResultsSummary 127.0.0.1 ... 0 2 4 fiezcoi1 NaN NaN NaN NaN NaN 0

16 rows × 28 columns

also for convenience the sintax storage.matching_pennied are available


In [21]:
storage.matching_pennies


Out[21]:
participant.id_in_session participant.code participant.label participant._is_bot participant._index_in_pages participant._max_page_index participant._current_app_name participant._round_number participant._current_page_name participant.ip_address ... player.payoff group.id_in_subsession subsession.round_number session.code session.label session.experimenter_name session.mturk_HITId session.mturk_HITGroupId session.comment session.is_demo
0 1 n66opzy5 NaN 1 12 12 matching_pennies 4 ResultsSummary 127.0.0.1 ... 0 1 1 fiezcoi1 NaN NaN NaN NaN NaN 0
1 2 hjvqfo1o NaN 1 12 12 matching_pennies 4 ResultsSummary 127.0.0.1 ... 0 1 1 fiezcoi1 NaN NaN NaN NaN NaN 0
2 3 gq6vxupy NaN 1 12 12 matching_pennies 4 ResultsSummary 127.0.0.1 ... 0 2 1 fiezcoi1 NaN NaN NaN NaN NaN 0
3 4 x80f6pv3 NaN 1 12 12 matching_pennies 4 ResultsSummary 127.0.0.1 ... 0 2 1 fiezcoi1 NaN NaN NaN NaN NaN 0
4 1 n66opzy5 NaN 1 12 12 matching_pennies 4 ResultsSummary 127.0.0.1 ... 0 1 2 fiezcoi1 NaN NaN NaN NaN NaN 0
5 2 hjvqfo1o NaN 1 12 12 matching_pennies 4 ResultsSummary 127.0.0.1 ... 0 1 2 fiezcoi1 NaN NaN NaN NaN NaN 0
6 3 gq6vxupy NaN 1 12 12 matching_pennies 4 ResultsSummary 127.0.0.1 ... 0 2 2 fiezcoi1 NaN NaN NaN NaN NaN 0
7 4 x80f6pv3 NaN 1 12 12 matching_pennies 4 ResultsSummary 127.0.0.1 ... 0 2 2 fiezcoi1 NaN NaN NaN NaN NaN 0
8 1 n66opzy5 NaN 1 12 12 matching_pennies 4 ResultsSummary 127.0.0.1 ... 100 1 3 fiezcoi1 NaN NaN NaN NaN NaN 0
9 2 hjvqfo1o NaN 1 12 12 matching_pennies 4 ResultsSummary 127.0.0.1 ... 0 1 3 fiezcoi1 NaN NaN NaN NaN NaN 0
10 3 gq6vxupy NaN 1 12 12 matching_pennies 4 ResultsSummary 127.0.0.1 ... 100 2 3 fiezcoi1 NaN NaN NaN NaN NaN 0
11 4 x80f6pv3 NaN 1 12 12 matching_pennies 4 ResultsSummary 127.0.0.1 ... 0 2 3 fiezcoi1 NaN NaN NaN NaN NaN 0
12 1 n66opzy5 NaN 1 12 12 matching_pennies 4 ResultsSummary 127.0.0.1 ... 0 1 4 fiezcoi1 NaN NaN NaN NaN NaN 0
13 2 hjvqfo1o NaN 1 12 12 matching_pennies 4 ResultsSummary 127.0.0.1 ... 0 1 4 fiezcoi1 NaN NaN NaN NaN NaN 0
14 3 gq6vxupy NaN 1 12 12 matching_pennies 4 ResultsSummary 127.0.0.1 ... 0 2 4 fiezcoi1 NaN NaN NaN NaN NaN 0
15 4 x80f6pv3 NaN 1 12 12 matching_pennies 4 ResultsSummary 127.0.0.1 ... 0 2 4 fiezcoi1 NaN NaN NaN NaN NaN 0

16 rows × 28 columns

If for some reason the experiment fails, this method returns an exception. for example if we provide a invalid number of participants


In [22]:
experiment.bot_data("matching_pennies", 1)


Running bots, please wait...
[INFO|2017-11-01 05:38:46,546] skotree > Running bots, please wait...
----------------------------------------------------------------
RuntimeError                   Traceback (most recent call last)
<ipython-input-22-7eb7fed99fee> in <module>()
----> 1 experiment.bot_data("matching_pennies", 1)

~/proyectos/skotree/src/skotree.py in bot_data(self, session_name, num_participants)
    689 
    690         """
--> 691         return self._middleware.bot_data(session_name, num_participants)
    692 
    693     @property

~/proyectos/skotree/src/skotree.py in bot_data(self, session_name, num_participants)
    399             return fps
    400 
--> 401         fps = self.execute(_bot_data)
    402         store = CSVStore(fps)
    403         return store

~/proyectos/skotree/src/skotree.py in execute(self, func)
    262         if isinstance(result, BaseException):
    263             logger.debug("Remote procees raises an exception!")
--> 264             raise result
    265         return result
    266 

RuntimeError: 

Connect to a remote experiment

New in version 0.4

To connect to a remote oTree location instead of given the settings.py path, you need to provide the URL where the experiment is running.


In [23]:
remote = skotree.oTree("http://localhost:8000")
remote


Out[23]:
<oTree@http://localhost:8000>

In [24]:
remote.lsapps()


Out[24]:
['matching_pennies']

In [25]:
remote.lssessions()


Out[25]:
['matching_pennies']

In [26]:
remote.app_data("matching_pennies")


Out[26]:
participant.id_in_session participant.code participant.label participant._is_bot participant._index_in_pages participant._max_page_index participant._current_app_name participant._round_number participant._current_page_name participant.ip_address ... player.payoff group.id_in_subsession subsession.round_number session.code session.label session.experimenter_name session.mturk_HITId session.mturk_HITGroupId session.comment session.is_demo
0 1 bqkfd73t NaN 0 0 12 NaN NaN NaN NaN ... 0 1 1 nvnpimbf NaN NaN NaN NaN NaN 0
1 2 5cznwlly NaN 0 0 12 NaN NaN NaN NaN ... 0 1 1 nvnpimbf NaN NaN NaN NaN NaN 0
2 1 bqkfd73t NaN 0 0 12 NaN NaN NaN NaN ... 0 1 2 nvnpimbf NaN NaN NaN NaN NaN 0
3 2 5cznwlly NaN 0 0 12 NaN NaN NaN NaN ... 0 1 2 nvnpimbf NaN NaN NaN NaN NaN 0
4 1 bqkfd73t NaN 0 0 12 NaN NaN NaN NaN ... 0 1 3 nvnpimbf NaN NaN NaN NaN NaN 0
5 2 5cznwlly NaN 0 0 12 NaN NaN NaN NaN ... 0 1 3 nvnpimbf NaN NaN NaN NaN NaN 0
6 1 bqkfd73t NaN 0 0 12 NaN NaN NaN NaN ... 0 1 4 nvnpimbf NaN NaN NaN NaN NaN 0
7 2 5cznwlly NaN 0 0 12 NaN NaN NaN NaN ... 0 1 4 nvnpimbf NaN NaN NaN NaN NaN 0
8 1 5b8zc5hr NaN 0 12 12 matching_pennies 4.0 ResultsSummary 127.0.0.1 ... 0 1 1 gh84ltgl NaN NaN NaN NaN NaN 1
9 2 x1f4ua8y NaN 0 12 12 matching_pennies 4.0 ResultsSummary 127.0.0.1 ... 0 1 1 gh84ltgl NaN NaN NaN NaN NaN 1
10 1 5b8zc5hr NaN 0 12 12 matching_pennies 4.0 ResultsSummary 127.0.0.1 ... 0 1 2 gh84ltgl NaN NaN NaN NaN NaN 1
11 2 x1f4ua8y NaN 0 12 12 matching_pennies 4.0 ResultsSummary 127.0.0.1 ... 0 1 2 gh84ltgl NaN NaN NaN NaN NaN 1
12 1 5b8zc5hr NaN 0 12 12 matching_pennies 4.0 ResultsSummary 127.0.0.1 ... 0 1 3 gh84ltgl NaN NaN NaN NaN NaN 1
13 2 x1f4ua8y NaN 0 12 12 matching_pennies 4.0 ResultsSummary 127.0.0.1 ... 0 1 3 gh84ltgl NaN NaN NaN NaN NaN 1
14 1 5b8zc5hr NaN 0 12 12 matching_pennies 4.0 ResultsSummary 127.0.0.1 ... 100 1 4 gh84ltgl NaN NaN NaN NaN NaN 1
15 2 x1f4ua8y NaN 0 12 12 matching_pennies 4.0 ResultsSummary 127.0.0.1 ... 0 1 4 gh84ltgl NaN NaN NaN NaN NaN 1

16 rows × 28 columns

Connect to a remote experiment With Authentication

New in version 0.5

If you are trying to connect to server in auth level mode DEMO or STUDY (More Information about modes) without credentials an error will be shown:


In [3]:
skotree.oTree("http://localhost:9000")


----------------------------------------------------------------
NoLoggedin                     Traceback (most recent call last)
<ipython-input-3-6b6d5c6c3e94> in <module>()
----> 1 skotree.oTree("http://localhost:9000")

~/proyectos/skotree/src/skotree.py in __init__(self, path, middleware, **kwargs)
    596             middleware = "remote" if is_url(path) else "local"
    597         self._path = os.path.abspath(path) if middleware == "local" else path
--> 598         self._middleware = MIDDLEWARES[middleware](self._path, **kwargs)
    599 
    600     def __repr__(self):

~/proyectos/skotree/src/skotree.py in __init__(self, url, **kwargs)
    426         self._client = requests.session()
    427         self.login(**kwargs)
--> 428         self.check_loggedin()
    429 
    430     def absurl(self, subpath):

~/proyectos/skotree/src/skotree.py in check_loggedin(self)
    458         if "/accounts/login/?next=" in resp.url:
    459             msg = "You are not authenticated in experiment located at {}"
--> 460             raise NoLoggedin(msg.format(self._url))
    461 
    462     def lsapps(self):

NoLoggedin: You are not authenticated in experiment located at http://localhost:9000

In this cases you need to provide the parameters username and password


In [5]:
# the credential are not stored internally
exp = skotree.oTree("http://localhost:9000", username="admin", password="skotree")
exp


Out[5]:
<oTree@http://localhost:9000>

and now all works as before


In [7]:
exp.all_data()


Out[7]:
participant.id_in_session participant.code participant.label participant._is_bot participant._index_in_pages participant._max_page_index participant._current_app_name participant._round_number participant._current_page_name participant.ip_address ... matching_pennies.3.player.is_winner matching_pennies.3.player.payoff matching_pennies.3.group.id_in_subsession matching_pennies.3.subsession.round_number matching_pennies.4.player.id_in_group matching_pennies.4.player.penny_side matching_pennies.4.player.is_winner matching_pennies.4.player.payoff matching_pennies.4.group.id_in_subsession matching_pennies.4.subsession.round_number
0 1 wxcd26g1 NaN 0 0 12 NaN NaN NaN NaN ... NaN 0 1 3 2 NaN NaN 0 1 4
1 2 akfa88o8 NaN 0 0 12 NaN NaN NaN NaN ... NaN 0 1 3 1 NaN NaN 0 1 4

2 rows × 49 columns

Some methods not work in a remote experiment

  • session_config()
  • bot_data()

Raises an NotImplementedError when are called.


In [27]:
remote.bot_data("matching_pennies", 1)


----------------------------------------------------------------
NotImplementedError            Traceback (most recent call last)
<ipython-input-27-1dbb6d4fbd98> in <module>()
----> 1 remote.bot_data("matching_pennies", 1)

~/proyectos/skotree/src/skotree.py in bot_data(self, session_name, num_participants)
    689 
    690         """
--> 691         return self._middleware.bot_data(session_name, num_participants)
    692 
    693     @property

~/proyectos/skotree/src/skotree.py in bot_data(self, session_name, num_participants)
    501 
    502     def bot_data(self, session_name, num_participants=None):
--> 503         raise NotImplementedError("Remote oTree can't run bots")
    504 
    505     @property

NotImplementedError: Remote oTree can't run bots

Also the settings attribute always returns None

Advanced Analysis

The pandas.DataFrame itself is a great tool for data analysis, but also can be easily adapted to feed a lot of libraries like:

  • Scipy: general scientific Computation.
  • Matplotlib or Bokeh for plotting.
  • Scikit-Learn: Simple and efficient tools for data mining and data analysis
  • StatsModels statsmodels is a Python module that provides classes and functions for the estimation of many different statistical models, as well as for conducting statistical tests, and statistical data exploration. An extensive list of result statistics are available for each estimator.
  • PyMC is a python module that implements Bayesian statistical models and fitting algorithms, including Markov chain Monte Carlo. Its flexibility and extensibility make it applicable to a large suite of problems. Along with core sampling functionality, PyMC includes methods for summarizing output, plotting, goodness-of-fit and convergence diagnostics.
  • Deep-Learning with TensorFlow or Keras

And much more...