psychopy.org | Reference | Downloads | Github

Randomisation without consecutive presentations

Hi,

I am trying to build a serial reaction time task (SRTT) with psychopy.

In the SRTT paradigm, four rectangles will be arranged horizontally on computer screen. At the beginning of a trial, one of the four rectangles will change from light to dark. Participants will be instructed to respond as quickly and accurately as possible, by pressing the corresponding button of a standard computer keyboard - “v” “b” “n” or “m”.

The SRTT involves “random trials”. Each “random trial/” will consist of a randomised series of 12 stimuli - 3 repetitions of the keys “v” “b” “n” and “m” (e.g., one trial might be: v-m-n-b-v-n-b-m-n-b-m-v).

My problem is that the “random trials” must NOT include consecutive presentations of one letter (e.g., v-m-n-b-v-n-n-m-b-m-b-v). After searching the discourse, my understanding is that I need to generate a custom code to ensure that this does not occur. However, I am very new to psychopy/coding and am not sure of how to do this.

Is anyone able to please help guide me in generating a code that will constrain the randomisation process so that no one square/keys be presented consecutively?

Thanks!

@Mirjam provides a general solution here:

In your specific case, something like this might work:

  • Insert a code component on your trial routine (from the “Custom” component panel).
  • In its “begin experiment” tab, put something like this ( we need to run this at the start of the experiment since the code is non-deterministic: we don’t know how long it will take to run (realistically though, it will be a fraction of a second for only 12 entries):
stimulus_list = ['v', 'b', 'n', 'm'] * 3
shuffle(stimulus_list)

# make sure the same target doesn't appear on consecutive trials
double_stim = False
list_ok = False

while list_ok == False:
    for i in range(len(stimulus_list) - 1):
        # check for equal neighbours:
        if stimulus_list[i] == stimulus_list[i + 1]:
            double_stim = True  # D'oh!
            break  # can stop checking on this run
    if double_stim == True:
        shuffle(stimulus_list)  # try a new order
        double_stim = False
    else:
        list_ok = True  # Yay! The loop won't run again.

print(stimulus_list)

Thank you for your response!

However, I forgot to clarify that both the position of the target stimulus (a rectangle) and the correct response (i.e., “v” “b” “n” or “m”) need to be randomised together so that they correspond.

That is, the correct answer is dependent on the particular position on the screen.

Is it possible to incorporate both the randomisation of the target position and the correct answer in this code?

As the correspondence between the positions and their answers is constant, there isn’t any need to randomise them together. i.e. if you know the correct answer, you also know the position.

You should explain what you need to achieve, and how you are currently attempting it. A screenshot of your stimulus components would probably help so we can refer to the same things.

Note to self for future reference:

stimulus_colours = {'v':'white', 'b':'white', 'n':'white', 'm':'white'}
stimulus_colours[corrAns] = 'black'

Thanks, Michael.

Apologies for not including sufficient information. Below is a screenshot of the stimulus components.

routine_blockselect: functions to select the trial for the participant (TrialR, TrialS, or a 1-minute break (IBI)).
trialR: refers to the random trials (this is what I am having trouble with).
trialS: refers to the trials in which the stimuli are presented in a fixed sequence (this is working as is).
output_R/output_S: functions to organise the data/participant response.

You can only have one keyboard component active at a time, otherwise they conflict with each other. Delete three of them, and just keep one, set:

  • to restrict the allowed keys to 'v', 'b', 'n', 'm',
  • to “Store correct”, and
  • to specify the Correct Answer as $corrAns.

To move your target to the location corresponding to the correct answer, create a dictionary that links letters to positions, in the “Begin Experiment” tab of a code component:

stimulus_x = {'v':-0.6, 'b':-0.2, 'n':0.2, 'm':0.6}

Then in the position field of your target stimulus, put something like this, set to update on every repeat:

(stimulus_x[corrAns], 0)

i.e. on every trial, the target will look up its x position according to the current value of the correct answer.

Thanks, Michael!! You’ve been extremely helpful - I am so very grateful.

I’ve made the changes that you have suggested and this is what it is looking like, now:

However, the code provided in your previous reply does not work in constraining the presentation of consecutive random trials:

stimulus_list = [‘v’, ‘b’, ‘n’, ‘m’] * 3
shuffle(stimulus_list)

make sure the same target doesn’t appear on consecutive trials
double_stim = False
list_ok = False

while list_ok == False:
for i in range(len(stimulus_list) -2):
# check for equal neighbours:
if stimulus_list[i] == stimulus_list[i + 1]:
double_stim = True # D’oh!
break # can stop checking on this run
if double_stim == True:
shuffle(stimulus_list) # try a new order
double_stim = False
else:
list_ok = True # Yay! The loop won’t run again.

print(stimulus_list)

I suspect it is a naming issue, but might be wrong? I’ve tried playing around with the code with no success thus far. Do you have any suggestions as to how I may adapt the code?

Not unless you can tell me precisely what is going wrong.

I followed your previous suggestions and this is what it is looking like:

My issue is that, when I run the task, I am still encountering consecutive presentations.

Sorry, I think there was an off-by-one error in what was this line:

for i in range(len(stimulus_list) - 2):

Try this instead:

for i in range(len(stimulus_list) - 1):

to ensure the entire list is checked for consecutive values.

Thanks, Michael.

Unfortunately, it is still not working. There is no error message, but when I run the task, I am still encountering consecutive presentations.

The code generates a list (see below), however, the presentation of stimuli do not follow this list.

I think there may be an issue with the “TrialR” properties. The presentation of stimuli seems to still follow the specified “looptype”, as when I change this to “sequential” instead of “random”, the loop follows the order of my conditions file (below).

Not sure how to adjust the settings so that the loop follows the generated list order.

The Builder loop can’t generate the sequence of correct responses with the constraint you need, which is why you now have custom code to do that (and to associate responses to stimulus positions). So don’t connect your loop to a conditions file. Instead, use the sequence that was generated in code.

At the beginning of each trial, do this:

# get the keypress corresponding to the current trial number:
corrAns = stimulus_list[your_loop_name.thisN]

# make sure it is recorded in the data:
thisExp.addData['corrAns', corrAns]

Make sure this code component is located above all of your other stimulus components, so that the ones that need it can get access to the latest value of corrAns

Thank you, Michael!!

With your help, I’ve managed to generate a working version of the task!

Thanks again for your help and patience - it is greatly appreciated.

1 Like