Presenting random stimuli on a circle

spyder (4.0.1, python 3.6), psychopy (v3.0)

I’d like to present N = 2, 4, 6 (set size randomly varies from trial to trial) stimuli located on an imaginary circle. The idea (possibly not very good) was to use the current trial’s nStim as the number of randomly generated angles (thetas). Fixation screen and possibly fixation point from the encoding display are present. The gratings are not generated. Specifically, it stops at the fixation point and waits there (still running) until it self-terminates.

With focus on the relevant parts:

``````nStim = [2,4,6] # trial list
stim_ecc = 7 # deg
stimN = visual.GratingStim(win, sf=1, size=4, tex='sin', mask='gauss', # create generic stim and only update attributes later
ori=0)

trials = data.TrialHandler(nStim, nReps = 5, # n repeats for all conditions
method='random',
seed=None,
autoLog=True)
totalTrialCnt  = 0
thisTrial = trials.nStim[0] # to initialise stimuli with some values
for thisTrial in trials:
if nStim == 2:
current_N = 2
elif nStim == 4:
current_N = 4
else:
current_N = 6

theta = numpy.linspace(0, 2*numpy.pi, **current_N**, retstep=True) # evenly spaced
rho = stim_ecc

stim_vec = {1:current_N}
for i in stim_vec:
def pol2cart(rho, theta):
x = rho*np.cos(theta)
y = rho*np.sin(theta)
return(x, y)
stimN(i).setPos([x, y])
stimN(i).draw()
win.flip()
core.wait(0.7)

trials.next()
exp.nextEntry()
``````

Error:

``````Traceback (most recent call last):

File "C:\Users\username\Dropbox\trial_handler.py", line 131, in <module>
trials.next() # in which order?

File "C:\Users\username\Anaconda3\envs\psychopy\lib\site-packages\psychopy\data\trial.py", line 350, in __next__
self._terminate()

File "C:\Users\username\Anaconda3\envs\psychopy\lib\site-packages\psychopy\data\base.py", line 115, in _terminate
raise StopIteration

StopIteration
``````

Maybe the calculation time takes longer than the duration of the display (.7 sec)? Or there is an issue in the writing part i don’t see/don’t know.

After uncommenting thisTrial = trials.nStim[0] line there is error:
`AttributeError: 'TrialHandler' object has no attribute 'nStim'`

So possibly the problem is with the way the ‘conditions’ were added.
Thanks!

At the moment, you have not told the TrialHandler anything about a value called `nStim` that should be accessible on each trial. Check the documentation for the `trialList` parameter here:
https://www.psychopy.org/api/data.html#trialhandler

It is defined as

a simple list (or flat array) of dictionaries specifying conditions. This can be imported from an excel/csv file using `importConditions()`

You have just given a list of numbers, not of dictionaries (which would allow the linking of those numbers to a named parameter like `'nStim'`). As suggested, either import the values from a .csv file, which has a column header of `nStim`, or for such a short list, manually define the dictionaries, e.g.

``````nStim = [{'nStim': 2}, {'nStim': 4}, {'nStim': 6}]
``````

although to ease the confusion, you might want to use distinct names, like:

``````stim_list = [{'nStim': 2}, {'nStim': 4}, {'nStim': 6}]
``````

Now each trial will have access to a named attribute called `'nStim'`, e.g.

``````for thisTrial in trials:
print(thisTrial['nStim'])
``````

This doesn’t work, because a TrialHandler object does not have an attribute called `.nStim`

As above, if you want to access this value, you need to get if from an individual trial’s key:value dictionary pairing.

This code is needlessly inefficient (as well as `nStim` being a list, so it would never be equal to a single value anyway):

``````for thisTrial in trials:
if nStim == 2:
current_N = 2
elif nStim == 4:
current_N = 4
else:
current_N = 6
``````

If you do actually need to define a new variable name, simply do it like this:

``````for thisTrial in trials:
current_N = thisTrial['nStim']
``````

The result of this line is constant, so it shouldn’t be inside your loop (causing it to keep getting re-calculated needlessly):

``````theta = numpy.linspace(0, 2*numpy.pi, **current_N**, retstep=True) # evenly spaced
``````

Not clear why you would give this variable a new name instead of using the original:

``````rho = stim_ecc
``````

Again, the `pol2cart()` function doesn’t change, so define it just once, before any of your loops start. At the moment it will be needlessly re-defined for every iteration of both enclosing loops:

``````for i in stim_vec:
def pol2cart(rho, theta):
``````

Also note that code in a function that comes after the `return` statement is not going to be executed:

``````    return(x, y) # all done.
stimN(i).setPos([x, y]) # So I'm doing nothing
stimN(i).draw() # and neither am I
``````

And I’m not sure this is necessary. Just let your `for` loop run:

``````trials.next()
``````

Nearly all of the issues you are encountering here are basic ones of Python programming, rather than anything to do with PsychoPy (except how to construct TrialHandler objects). So I’d strongly recommend that you actually construct your experiment in Builder. It will handle all of the laborious stuff for you, including best-practice timing of stimulus display and response collection, automatic data saving, and getting direct access to variables like `nStim` rather than using the dictionary notation. i.e. you might be treating this as a good way to learn Python, but you also need to learn the even more complicated issues of synchronising with the display refresh rate and so on. Let Builder handle that for you instead, so you can have more confidence in the data that gets generated.

This isn’t intended as patronising advice: even most experienced PsychoPy users (including most of its developers) will tend to let Builder do most of the leg work these days. That way you can focus your Python skills solely on the additional snippets of code you need, in this case, to control the location of your stimuli.

i.e. Build the experiment graphically, and then insert a graphical code component from the “custom” component panel. Just put snippets of code in the relevant tabs there (e.g. define your custom functions in the “Begin experiment” tab, and calculate the stimulus locations in the “begin routine” tab). Builder will automatically insert those code snippets in the right place in the long and comprehensive .py file it generates for you.

2 Likes

Hi Michael,

Thanks a lot for the prompt and thorough response - many points are clearer now and i learned a lot! I suppose, i should first search ‘create lists of dictionaries python’ before any trial to create the nStim.

After discussing it with my supervisor, one solution would be that after randomly defining theta1 and stepsize as 2*pi/N, each subsequent theta would be equal to the previous one + stepsize.

About programming, they say, once you did something once, the next time would be easier and there is always something more to learn.