Randomize trial order without repeating stimuli twice in a row

If this template helps then use it. If not then just delete and start from scratch.

OS : Win10
PsychoPy version : 3.0.5
Standard Standalone? (y/n) : yes

I am running a study where 4 stimuli are presented a number of times per block. Specifically, I have a .csv file with 24 rows, and there are 4 stimuli in one of the columns of the .csv file with each stimulus repeating 6 times. For each block, I need the rows to be randomly selected. However, I also need to make sure each stimulus is never repeated. As an example, the 4 stimuli are: lavender, lilac, lemon, pear. Each should be presented 6 times per block, and in a random order without any stimuli presented twice. For instance, if they see a lemon, the next stimulus must be random, but it cannot be a lemon again. I cannot figure out a code component I could put at the beginning of each block that would achieve this. Any help would be greatly appreciated. Thank you!

This kind of custom randomisation constraint can’t be implemented within Builder’s available options using a conditions file. What you need to do is insert a code component and use custom code to generate a new randomised conditions file (or list of conditions in memory) on each run.

A possible algorithm would be:

  • create a reference list ['lavender', 'lilac', 'lemon', 'pear']
  • copy it.
  • shuffle that copy. This is now the first 4 conditions, in order.
  • copy the reference list again.
  • extract the item from it that is the same as the last item of the first list.
  • randomly sample one of the three remaining items and append it to the first list.
  • put the extracted item back in the second list.
  • shuffle the second list.
  • extend the first list with the second list (which now contains 3 items). The result is now the first 8 conditions in order.
  • repeat the previous 6 steps 4 times.
1 Like

I don’t use Builder, but I have a piece of code (using numpy) at the start of an experiment that loops through my randomised list of stimuli to check for identical items in subsequent rows, and reshuffles the list (and checks again, and reshuffles if needed…) until there are none. This does introduce a short waiting time at the start, but never too long in my experience.

# make sure the same target doesn't appear on consecutive trials
doubletarget = False
listok = False

while listok == False:
    for i in range(len(practiselist)-1):
        if practiselist[i,stimTextCol] == practiselist[i+1,stimTextCol]:
            doubletarget = True
    if doubletarget == True:
        practiselist = np.random.permutation(practiselist)
        print("reshuffle practiselist...")
        doubletarget = False
    else:
        listok = True
2 Likes

Yes, that’s the sort of non-deterministic, brute force approach I’ve usually used in the past, and is a good general solution for all sorts of constraints. For a long list, with different constraints, it might take hundreds or thousands of attempts to find a winner, but that won’t usually take more than a second or two.

In this case I realized that there is a deterministic algorithm that could solve the constraint in one go, but the structure of your code does provide a more general solution for other sorts of constraints too. And in this case, it is certainly simpler to implement as well.

Your proposed solution will (should?) take the exact same time to come up with a suitable stimulus list on every run, which I might need at some point in the future, so I’m saving it for future reference for myself. But I was too proud of my little code snippet not to show it off, and I hope it’s useful to someone with the same general problem.

1 Like

Hi Mirjam and Michael,

I tried to use Mirjam’s code to randomize my stimuli according to 1 condition but it did not work. The script keeps running without ending (even after 1 or 2 minutes) and I can’t figure out why. The text component of my first routine never shows up so I guess psychopy is running the while loop again and again, but the message "reshuffle ..." is never printed.

I have a csv file (MMN_stim.csv) containing my stim and trigger’s info (it will be an EEG study).
So I have 1030 lines of standard stim, 100 for deviant_1, 100 for deviant_2, 100 for deviant_3, and 100 for deviant_4.
I would like to randomise my list by making sure that a deviant stim will never be immediately followed by another deviant. In other words, each deviant stim must always be immediately followed by a standard stim.
Bellow is the code I inserted in my first routine. Would you have any advice? Do you know where is my mistake?

filename_to_read = _thisDir + os.sep + 'MMN_stim.csv'
filename_to_write = _thisDir + os.sep + 'MMN_stim_' + expInfo['participant'] + '.csv'

doubledev = False
listok = False

my_file = open(filename_to_read)
randstim = my_file.readlines()
header, stim_list = randstim[0], randstim[1:]
shuffle(stim_list)

while listok == False:
    for i in range(len(stim_list)-1):
        if stim_list[i] != 'standard.wav' and stim_list[i+1] != 'standard.wav':
            doubledev = True
            break
    if doubledev == True:
        shuffle(stim_list)
        print("reshuffle ...")
        doubledev = False
    else:
        listok = True

with open(filename_to_write,'w') as out:
    out.write(''.join([header]+stim_list))

Many thanks in advance!