Skip to content

Commit

Permalink
STYLE: revised coder demos
Browse files Browse the repository at this point in the history
  • Loading branch information
jeremygray committed Nov 24, 2015
1 parent 5b84d27 commit 8e5f16e
Show file tree
Hide file tree
Showing 65 changed files with 1,472 additions and 1,439 deletions.
48 changes: 26 additions & 22 deletions psychopy/demos/coder/experiment control/JND_staircase_analysis.py
Original file line number Diff line number Diff line change
@@ -1,56 +1,59 @@
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
from __future__ import division

# This analysis script takes one or more staircase datafiles as input from a GUI
# It then plots the staircases on top of each other on the left
# and a combined psychometric function from the same data
# on the right.
"""
This analysis script takes one or more staircase datafiles as input from a GUI
It then plots the staircases on top of each other on the left
and a combined psychometric function from the same data
on the right.
# The combined plot uses every unique X value form the staircase, and alters the
# size of the points according to how many trials were run at that level
The combined plot uses every unique X value form the staircase, and alters the
size of the points according to how many trials were run at that level
"""

from __future__ import division
from psychopy import data, gui, core
from psychopy.tools.filetools import fromFile
import pylab, scipy
import os

# set to 0.5 for Yes/No (or PSE). Set to 0.8 for a 2AFC threshold
threshVal = 0.5

# set to zero for Yes/No (or PSE). Set to 0.5 for 2AFC
expectedMin = 0.0

files = gui.fileOpenDlg('.')
if not files:
if not files:
core.quit()

# get the data from all the files
allIntensities, allResponses = [], []
for thisFileName in files:
for thisFileName in files:
thisDat = fromFile(thisFileName)
assert isinstance(thisDat, data.StairHandler)
allIntensities.append( thisDat.intensities )
allResponses.append( thisDat.data )
dataFolder = os.path.split(thisFileName)[0] # just the path, excluding file name
allIntensities.append(thisDat.intensities)
allResponses.append(thisDat.data)
dataFolder = os.path.split(thisFileName)[0] # just the path, not file name

# plot each staircase in left hand panel
pylab.subplot(121)
colors = 'brgkcmbrgkcm'
lines, names = [], []
for fileN, thisStair in enumerate(allIntensities):
# lines.extend(pylab.plot(thisStair)) # uncomment these lines to get a legend for files
for fileN, thisStair in enumerate(allIntensities):
# lines.extend(pylab.plot(thisStair)) # uncomment for a legend for files
# names = files[fileN]
pylab.plot(thisStair, label=files[fileN])
# pylab.legend()

# get combined data
combinedInten, combinedResp, combinedN = \
data.functionFromStaircase(allIntensities, allResponses, bins='unique')
combinedN = pylab.array(combinedN) # convert to array so we can do maths with them
i, r, n = data.functionFromStaircase(allIntensities, allResponses, bins='unique')
combinedInten, combinedResp, combinedN = i, r, n
combinedN = pylab.array(combinedN) # convert to array so we can do maths

# fit curve
fit = data.FitWeibull(combinedInten, combinedResp, expectedMin=expectedMin,
sems = 1.0/combinedN)
fit = data.FitWeibull(combinedInten, combinedResp, expectedMin=expectedMin,
sems=1.0 / combinedN)
smoothInt = pylab.arange(min(combinedInten), max(combinedInten), 0.001)
smoothResp = fit.eval(smoothInt)
thresh = fit.inverse(threshVal)
Expand All @@ -65,8 +68,8 @@

# plot points
pointSizes = pylab.array(combinedN) * 5 # 5 pixels per trial at each point
points = pylab.scatter(combinedInten, combinedResp, s=pointSizes,
edgecolors=(0, 0, 0), facecolor=(1, 1, 1), linewidths=1,
points = pylab.scatter(combinedInten, combinedResp, s=pointSizes,
edgecolors=(0, 0, 0), facecolor=(1, 1, 1), linewidths=1,
zorder=10, # make sure the points plot on top of the line
)

Expand All @@ -77,6 +80,7 @@
pylab.savefig(outputFile)
print('saved figure to: ' + outputFile)
pylab.show()

core.quit()

# The contents of this file are in the public domain.
# The contents of this file are in the public domain.
49 changes: 27 additions & 22 deletions psychopy/demos/coder/experiment control/JND_staircase_exp.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
from __future__ import division

"""
measure your JND in orientation using a staircase method"""
Measure your JND in orientation using a staircase method
"""

from psychopy import core, visual, gui, data, event
from psychopy.tools.filetools import fromFile, toFile
Expand All @@ -17,9 +18,9 @@

# present a dialogue to change params
dlg = gui.DlgFromDict(expInfo, title='simple JND Exp', fixed=['date'])
if dlg.OK:
if dlg.OK:
toFile('lastParams.pickle', expInfo) # save params to file for next time
else:
else:
core.quit() # the user hit cancel so exit

# make a text file to save data
Expand All @@ -34,15 +35,18 @@
foil = visual.GratingStim(win, sf=1, size=4, mask='gauss', ori=expInfo['refOrientation'])
target = visual.GratingStim(win, sf=1, size=4, mask='gauss', ori=expInfo['refOrientation'])
fixation = visual.GratingStim(win, color='black', tex=None, mask='circle', size=0.2)
message1 = visual.TextStim(win, pos=[0, + 3], text='Hit a key when ready.')
message2 = visual.TextStim(win, pos=[0, -3], text="Then press left or right to identify the %.1fdegree probe." %(expInfo['refOrientation']))
message1 = visual.TextStim(win, pos=[0, + 3],
text='Hit a key when ready.')
message2 = visual.TextStim(win, pos=[0, -3],
text="Then press left or right to identify the %.1fdegree probe." % expInfo['refOrientation'])

# create the staircase handler
staircase = data.StairHandler(startVal = 20.0,
stepType = 'lin', stepSizes=[8, 4, 4, 2, 2, 1, 1], # reduce step size every two reversals
minVal=0, maxVal=90,
nUp=1, nDown=3, # will home in on the 80% threshold
nTrials=50)
staircase = data.StairHandler(startVal=20.0,
stepType='lin',
stepSizes=[8, 4, 4, 2, 2, 1, 1], # reduce step size every two reversals
minVal=0, maxVal=90,
nUp=1, nDown=3, # will home in on the 80% threshold
nTrials=50)

# display instructions and wait
message1.draw()
Expand All @@ -54,7 +58,7 @@

for thisIncrement in staircase: # will step through the staircase
# set location of stimuli
targetSide= round(numpy.random.random()) * 2-1 # will be either + 1(right) or -1(left)
targetSide = round(numpy.random.random()) * 2 - 1 # +1 = right, -1 = left
foil.setPos([-5 * targetSide, 0])
target.setPos([5 * targetSide, 0]) # in other location

Expand All @@ -74,33 +78,34 @@
win.flip()

# get response
thisResp=None
while thisResp is None:
allKeys=event.waitKeys()
for thisKey in allKeys:
if (thisKey == 'left' and targetSide == -1) or (thisKey == 'right' and targetSide == 1):
thisResp = None
while thisResp is None:
allKeys = event.waitKeys()
for thisKey in allKeys:
if ((thisKey == 'left' and targetSide == -1) or
(thisKey == 'right' and targetSide == 1)):
thisResp = 1 # correct
elif (thisKey == 'right' and targetSide == -1) or (thisKey == 'left' and targetSide == 1):
elif ((thisKey == 'right' and targetSide == -1) or
(thisKey == 'left' and targetSide == 1)):
thisResp = 0 # incorrect
elif thisKey in ['q', 'escape']:
core.quit() # abort experiment
event.clearEvents('mouse') # only really needed for pygame windows

# add the data to the staircase so it can calculate the next level
staircase.addResponse(thisResp)
dataFile.write('%i %.3f %i\n' %(targetSide, thisIncrement, thisResp))
dataFile.write('%i %.3f %i\n' % (targetSide, thisIncrement, thisResp))

# staircase has ended
dataFile.close()
staircase.saveAsPickle(fileName) # special python binary file to save all the info
staircase.saveAsPickle(fileName) # special python data file to save all the info

# give some output to user
print('reversals:')
print(staircase.reversalIntensities)
print('mean of final 6 reversals = %.3f' %(numpy.average(staircase.reversalIntensities[-6:])))

print('mean of final 6 reversals = %.3f' % numpy.average(staircase.reversalIntensities[-6:]))

win.close()
core.quit()

# The contents of this file are in the public domain.
# The contents of this file are in the public domain.
27 changes: 15 additions & 12 deletions psychopy/demos/coder/experiment control/TrialHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,21 @@
# -*- coding: utf-8 -*-
from __future__ import division

"""
Demo of TrialHandler
"""

from random import random
from psychopy import data

# create your list of stimuli
# NB as of version 1.62 you could simply import an excel spreadsheet with this
# using data.importTrialTypes('someFile.xlsx')
stimList = []
for ori in range(90, 180, 30):
for sf in [0.5, 1.0, 2.0]:
stimList.append(
{'sf':sf, 'ori':ori} # this is a python 'dictionary'
)
for ori in range(90, 180, 30):
for sf in [0.5, 1.0, 2.0]:
# append a python 'dictionary' to the list
stimList.append({'sf':sf, 'ori':ori})

# organise them with the trial handler
trials = data.TrialHandler(stimList, 10, extraInfo= {'participant':"Nobody", 'session':1})
Expand All @@ -24,27 +27,27 @@
nDone = 0
for thisTrial in trials: # handler can act like a for loop
# simulate some data
thisReactionTime = random() + float(thisTrial['sf'])/2.0
thisReactionTime = random() + float(thisTrial['sf']) / 2.0
thisChoice = round(random())
trials.data.add('RT', thisReactionTime) # add the data to our set
trials.data.add('choice', thisChoice)
nDone += 1 # just for a quick reference

print('trial %i had position %s in the list (sf=%.1f)' \
%(nDone, trials.thisIndex, thisTrial['sf']))
msg = 'trial %i had position %s in the list (sf=%.1f)'
print(msg % (nDone, trials.thisIndex, thisTrial['sf']))

# after the experiment
print('\n')
trials.printAsText(stimOut=['sf', 'ori'], # write summary data to screen
dataOut=['RT_mean', 'RT_std', 'choice_raw'])
trials.saveAsText(fileName='testData', # also write summary data to a text file
stimOut=['sf', 'ori'],
stimOut=['sf', 'ori'],
dataOut=['RT_mean', 'RT_std', 'choice_raw'])
trials.saveAsExcel(fileName='testData', # ...or an xlsx file (which supports sheets)
sheetName = 'rawData',
stimOut=['sf', 'ori'],
sheetName = 'rawData',
stimOut=['sf', 'ori'],
dataOut=['RT_mean', 'RT_std', 'choice_raw'])
trials.saveAsPickle(fileName = 'testData') # this saves a copy of the whole object
df = trials.saveAsWideText("testDataWide.txt") # wide is useful for analysis with R or SPSS. Also returns dataframe df

# The contents of this file are in the public domain.
# The contents of this file are in the public domain.
22 changes: 13 additions & 9 deletions psychopy/demos/coder/experiment control/autoDraw_autoLog.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,38 +10,42 @@
stim.setAutoDraw(False) is called. By default a logging message of
level EXP will be created when the setAutoDraw is called.
This can be turned off for each call with stim with stim.setAutoDraw(True, autoLog=False)
This can be turned off for each call with stim.setAutoDraw(True, autoLog=False)
"""

win = visual.Window([800, 800])
stim1 = visual.GratingStim(win, pos=[-0.5, -0.5], name='stim1') # name is used in log entries

# a stim's name is used in log entries
stim1 = visual.GratingStim(win, pos=[-0.5, -0.5], name='stim1')
stim2 = visual.TextStim(win, pos=[0.5, 0.5], text='stim2', name='textStim')
fixation = visual.GratingStim(win, mask='gauss', tex=None, size=0.02,
name='fixation', autoLog=False) # no need to log the fixation piont info

fixation.setAutoDraw(True) # no need to log fixation info
# no need to log the fixation point info, use autoLog=False
fixation = visual.GratingStim(win, mask='gauss', tex=None, size=0.02,
name='fixation', autoLog=False)

fixation.setAutoDraw(True)
stim1.setAutoDraw(True)
stim2.setAutoDraw(True)
# both on
for frameN in range(20): # run 20 frames like this
win.flip()

stim2.setAutoDraw(False)
# only stim1 (and fixation)
# will draw only stim1 (and fixation)
for frameN in range(20): # run 20 frames like this
win.flip()

stim1.setAutoDraw(False)
stim2.setAutoDraw(True)
# only stim2 (and fixation)
# will draw only stim2 (and fixation)
for frameN in range(20): # run 20 frames like this
win.flip()

for stim in [stim1, stim2, fixation]:
for stim in [stim1, stim2, fixation]:
stim.setAutoDraw(False)
win.flip() # will cause the 'off' log messages to be sent

win.close()
core.quit()

# The contents of this file are in the public domain.
# The contents of this file are in the public domain.
47 changes: 26 additions & 21 deletions psychopy/demos/coder/experiment control/experimentHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,52 +2,57 @@
# -*- coding: utf-8 -*-
from __future__ import division

"""
Demo of class data.ExperimentHandler
"""

from psychopy import data, logging
from numpy import random
logging.console.setLevel(logging.DEBUG)

exp = data.ExperimentHandler(name='testExp',
version='0.1',
extraInfo={'participant':'jwp', 'ori':45},
runtimeInfo=None,
originPath=None,
savePickle=True,
saveWideText=True,
exp = data.ExperimentHandler(name='testExp',
version='0.1',
extraInfo={'participant':'jwp', 'ori':45},
runtimeInfo=None,
originPath=None,
savePickle=True,
saveWideText=True,
dataFileName='testExp')

# a first loop (like training?)
conds = data.createFactorialTrialList({'faceExpression':['happy', 'sad'], 'presTime':[0.2, 0.3]})
training = data.TrialHandler(trialList=conds, nReps=3, name='train',
method='random',
seed=100) # this will set the global seed - so fixed for whole exp
conds = data.createFactorialTrialList(
{'faceExpression':['happy', 'sad'], 'presTime':[0.2, 0.3]})
training = data.TrialHandler(trialList=conds, nReps=3, name='train',
method='random',
seed=100) # this will set the global seed - for the whole exp
exp.addLoop(training)
# run those trials
for trial in training:
for trial in training:
training.addData('training.rt', random.random() * 0.5 + 0.5)
if random.random() > 0.5:
if random.random() > 0.5:
training.addData('training.key', 'left')
else:
else:
training.addData('training.key', 'right')
exp.nextEntry()

# then run 3 repeats of a staircase
outerLoop = data.TrialHandler(trialList=[], nReps=3, name='stairBlock',
outerLoop = data.TrialHandler(trialList=[], nReps=3, name='stairBlock',
method='random')
exp.addLoop(outerLoop)
for thisRep in outerLoop: # the outer loop doesn't save any data
staircase = data.StairHandler(startVal=10, name='staircase', nTrials=5)
exp.addLoop(staircase)
for thisTrial in staircase:
for thisTrial in staircase:
id=random.random()
if random.random() > 0.5:
if random.random() > 0.5:
staircase.addData(1)
else:
else:
staircase.addData(0)
exp.addData('id', id)
exp.nextEntry()
for e in exp.entries:
for e in exp.entries:
print(e)
print("Done. 'exp' experimentHandler will now (end of script) save data to testExp.csv")
print(" and also to testExp.psydat, which is 'exp' pickled")
print(" and also to testExp.psydat, which is a pickled version of `exp`")

# The contents of this file are in the public domain.
# The contents of this file are in the public domain.
Loading

0 comments on commit 8e5f16e

Please sign in to comment.