psychopy.org | Reference | Downloads | Github

Setup trials with two stimuli conditions


#1

Hello everyone

I need a help with setting up an experiment. Basically, I need to set up a random sequence of 20 trials in two different conditions, stimulus A and stimulus B, established in lines 86 and 97.
The sequence would be: point of fixation, display of one of the the stimulus and the task of reproduction, in line 110. Then, a new attempt begins at the point of fixation.

The problem is that I do not know how to randomize the display of the stimuli.
I thought of generating a sequence of odd and even numbers and put an" if ", for example, if it is an even number choose the sequence for gray clock (lines 87 to 97), if it is an odd number choose the sequence for red clock(lines 100 to 106), but I do not know if that is the best choice, and I also do not know how to insert this "if " with the two for commands.

Thanks from Brazil!!!

import math, numpy, random  # to have handy system and math functions
from psychopy import core, event, visual, gui  # these are the PsychoPy modules

# creates a window
myWin = visual.Window(color='white', 
                      units='pix', 
                      size=[800, 800],
                      allowGUI=False, 
                      fullscr=False
)

#setup participants
gui = gui.Dlg()

gui.addField("Subject ID:")
gui.addField("Condition Num:")

gui.show()

subj_id = gui.data[0]
cond_num = int(gui.data[1])

print (subj_id)
print (cond_num)

#Instructions
text = visual.TextStim(myWin, 
                       text="Test",
                       color=[-1,-1,-1]
)

fixation = visual.TextStim(myWin, 
                       text="+",
                       color=[-1,-1,-1]
)


# gray
disk1 = visual.Circle(myWin, 
                     radius=80,
                     fillColor='gray', 
                     lineColor=None,
                     edges=128
)

# Red
disk2 = visual.Circle(myWin, 
                     radius=80,
                     fillColor='red', 
                     lineColor=None,
                     edges=128
)

# Clockhand
clock_hand = visual.Line(myWin, 
                         start=[0, 0],
                         end=[-80, 0], 
                         lineColor='black',
                         lineWidth=3,
                         ori=0
)

#Text
text.draw()
myWin.flip()
event.waitKeys()

#Start_text
text2 = visual.TextStim(myWin, 
                       text="Start with A",
                       color=[-1,-1,-1]
)

#Stop_text
text3 = visual.TextStim(myWin, 
                       text="Stop with L",
                       color=[-1,-1,-1]
)

#Fixation
fixation.draw()
myWin.flip()
core.wait(5)


#Stimuli_A
current_angle = 0.0

for i in range(10):
    disk1.draw()
    clock_hand.ori = current_angle
    clock_hand.draw()
    myWin.flip()
    core.wait(1) 
    current_angle += 45.0

#Stimuli_B
current_angle = 0.0

for i in range(10):
    disk2.draw()
    clock_hand.ori = current_angle
    clock_hand.draw()
    myWin.flip()
    core.wait(2) 
    current_angle += 45.0



#ReproductionTask
#Start
text2.draw()
myWin.flip()
start = event.waitKeys(keyList=["a"])
#Stop
clock = core.Clock()
text3.draw()
myWin.flip()
stop = event.waitKeys(keyList=["l"],timeStamped=clock)

print(start)
print(stop)
      
myWin.close() #closes the window

#2

Hello, you have the right idea, but what you need is a single for loop to control your trials, iterating over your list of conditions, something like:

from numpy.random import shuffle

trial_conditions = ['red', 'grey'] * 10
shuffle(trial_conditions) # randomise order

for colour in trial_conditions:
    if colour == 'grey':
        wait_period = 1.0
        disk1.draw()
    else:
        wait_period = 2.0
        disk2.draw()

    clock_hand.ori = current_angle
    clock_hand.draw()

    myWin.flip()
    core.wait(wait_period) 
    current_angle += 45.0

But it would really be worth your time to learn how to use the TrialHandler class to control your trials. It will do a lot of the boring stuff for you automatically (like save a row of data to a file for each trial, and so on). I think there’s a demo for it in the demos menu, plus documentation online.

Notice that having just one loop allows us to not have to duplicate any code, except as necessary. i.e. the only code we write twice is the stuff that needs to vary on each trial (drawing the disks and specifying the wait period). Everything else in the trial is just written once. Duplicating code quickly becomes unmanageable and error-prone.

Lastly, as a coding style thing, you’ll appreciate yourself in the future (as will anyone else who reads your code) if you use meaningful, descriptive variable names rather than arbitrary ones like disk1, text_1. eg red_disk, stop_text etc make it really clear what is happening and reduces the need for explanatory comments, as the code itself will describe what it is doing (as currently with clock_hand and fixation).


#3

Thank you very much for the indication of TrialHandler, I’m going to study it, and thank you also for the tip of the variable names, I really need to be careful about that.

But I still have a doubt, I understood that it is not necessary to replicate the repeated code, but I still have problems: in every trial, for example, grey condition, it should shows the fixation point, next the disc and the clock hand from point of origin, wait 1 second, update the angle of the clock hand, wait 1 second, walk 45 degrees, wait another second… this is repeated for 9 times, but it’s still a single trial,
then I get response of the keys, and then a new trial starts (which can be a red).

It’s like two replica clocks, one condition repeats 9 times 45 degrees and another repeats only 7 times, and each has a different wait time.

Looks like a need a for inside the “if”, so that in one condition the updating of the angle occurs 9 times and in the other only 7.

Thank you


#4

OK, this sort of arrangement just requires a pair of nested loops: an outer one cycles through once for every trial, and an inner one controls the stimulus changes within the trial. If you can express it in written form as above, then it can also be expressed in code.

You now have three pieces of information needed to control each trial: stimulus colour, wait time, and number of repetitions. This is getting to the point where it is probably easier to just put this info in an external conditions file table (i.e. a .xlsx or .csv), and link that to a TrialHandler, which would also control the randomisation for you. But below I’ll just stick with the sort of code you already have.

Because there are now 3 pieces of information that vary across trials, we’ll try to eliminate the duplication as much as possible, while still having only one if/else construction. We’ll do this by gathering those three values into a pair of dictionaries, so we can switch between them as a set:

from numpy.random import shuffle

trial_types = ['red', 'grey'] * 10
shuffle(trial_types) # randomise order

# define parameters in a dictionary for each trial type:
grey_trial = {'disk':disk1, 'wait_period':1.0, 'clock_reps':9}
red_trial  = {'disk':disk2, 'wait_period':2.0, 'clock_reps':7}

# trial-level loop:
for trial_type in trial_types:
    if trial_type == 'grey':
        trial_conditions = grey_trial
    else:
         trial_conditions = red_trial

    # fixation:
    fixation.draw()
    myWin.flip()
    core.wait(5)

    # clock drawing:
    current_angle = 0.0

    for rep in range(trial_conditions['clock_reps']):
        trial_conditions['disk'].draw()
        clock_hand.ori = current_angle
        clock_hand.draw()
        myWin.flip()

        core.wait(trial_conditions['wait_period']) 
        current_angle += 45.0

    response = event.waitKeys()
    # do something with the response here

Note that using successive core.wait() commands like this is not the most temporally accurate way of displaying your stimuli. You should look into using core.Clock objects to check your overall timing and consider more accurate methods (there are demos on stimulus timing). This can require a third level of loop, to control stimulus durations more actively.


#5

Hello Michael, it worked perfectly!
I am extremely grateful for your teaching and for your time. I looked for the trialHandler and it contemplates several functions that I really need to configure the experiments of my research that involves perception of time.

Best regards and thank you again