Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MRG: Tracker dealer example #310

Merged
merged 12 commits into from
Jun 7, 2017
81 changes: 81 additions & 0 deletions examples/stimuli/tracker_dealer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# -*- coding: utf-8 -*-
"""
======================
Tracker Dealer Example
======================

This file shows how to interleave multiple Tracker objects using
:class:`expyfun.stimuli.TrackerDealer`.

In this case, a modeled human subject generates two curves (one for each trial
type: 1 & 2)

@author: maddycapp27
"""

import numpy as np
from expyfun.stimuli import TrackerUD, TrackerDealer
from expyfun.analyze import sigmoid
import matplotlib.pyplot as plt

# define parameters of modeled subject (using sigmoid probability)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI if you want you can make pretty sections in the output like this:

https://github.com/mne-tools/mne-python/blob/master/tutorials/plot_artifacts_correction_filtering.py#L49

that render like this:

https://mne-tools.github.io/dev/auto_tutorials/plot_artifacts_correction_filtering.html#removing-power-line-noise-with-notch-filtering

might be overkill in this example but it's something to consider when writing pedagogical examples

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I'll definitely keep that in mind for the future! For this example, I agree that it's not very necessary though.

true_thresh = [30, 40]
slope = 0.1
chance = 0.5

# define parameters for each tracker (assuming each tracker uses same rules)
up = 1
down = 1
step_size_up = [9, 3]
step_size_down = [3, 1]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just so I'm clear... the first element of each of these is "initial step size" and the second element kicks in after change_criteria[1] reversals have occured?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct--do you think there should be a comment to that effect?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for comment

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, a comment would be good (since initially I thought they were different values that were going to be fed into the two different TrackerUDs that were getting created).

stop_criterion = 30
stop_rule = 'reversals'
start_value = 45
change_criteria = [0, 5]
change_rule = 'reversals'
x_min = 0
x_max = 90

# parameters for the tracker dealer
max_lag = 2
rand = None
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think @Eric89GXL usually likes this to be called rng. And I think in this case it is better to seed it, so rng = np.random.RandomState(0). Mess around with the seed value until you get plots that end up with thresholds within 1 or 2 of their true value. Seeding the RNGs will make the plots the same every time, and having the computed thresholds close to the the true ones in the example will be less confusing for people.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahhh, ok, I was confused by that in the other example. Will fix!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, probably shouldn't have been None there, either.



# callback function that prints to console
def callback(event_type, value=None, timestamp=None):
print((str(event_type) + ':').ljust(40) + str(value))


# initialize two tracker objects--one for each trial type
tr_UD = [TrackerUD(callback, up, down, step_size_up, step_size_down,
stop_criterion, stop_rule, start_value,
change_criteria, change_rule, x_min, x_max) for i in [0, 1]]

# initialize TrackerDealer object
tr = TrackerDealer(tr_UD, max_lag, rand)

# Initialize human state
rng = np.random.RandomState(1)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it stylistically okay that I overwrite rng here? I could see it being confusing to someone just skimming the example.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to do rng_human and rng_dealer or something like that.


while not tr.stopped:
# Get information of which trial type is next and what the level is at
# that time from TrackerDealer
ss, level = tr.get_trial()
ss = sum(ss)
tr_UD[ss].respond(rng.rand() < sigmoid(level - true_thresh[ss],
lower=chance, slope=slope))

# Plot the results
axes = [plt.subplot(211), plt.subplot(212)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

axes = plt.subplots(2, 1)[1]

for i in [0, 1]:
fig, ax, lines = tr[i].plot(ax=axes[i])
lines += tr[i].plot_thresh(4, ax=ax)

lines[0].set_label('Trials')
lines[1].set_label('Reversals')
lines[2].set_label('Estimated threshold')

ax.legend(loc='best')
ax.set_title('Adaptive track of model human trial type {} (true threshold '
'is {})'.format(i + 1, true_thresh[i]))
plt.tight_layout()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

avoid global plt commands, prefer fig.tight_layout() here

3 changes: 1 addition & 2 deletions examples/stimuli/tracker_staircase.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
def callback(event_type, value=None, timestamp=None):
print((str(event_type) + ':').ljust(40) + str(value))


# Define parameters for modeled human subject (sigmoid probability)
true_thresh = 35
slope = 0.1
Expand All @@ -37,7 +36,7 @@ def callback(event_type, value=None, timestamp=None):
# Do the task until the tracker stops
while not tr.stopped:
tr.respond(rng.rand() < sigmoid(tr.x_current - true_thresh,
lower=0.5, slope=0.1))
lower=chance, slope=slope))

# Plot the results
fig, ax, lines = tr.plot()
Expand Down
2 changes: 1 addition & 1 deletion expyfun/stimuli/_tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def _check_callback(callback):
class TrackerUD(object):
"""Up-down adaptive tracker

This class implements a standard up-down adative tracker object. Based on
This class implements a standard up-down adaptive tracker object. Based on
how it is configured, it can be used to run a fixed-step m-down n-up
tracker (staircase), or it can implement a weighted up-down procedure.

Expand Down