psychopy.org | Reference | Downloads | Github

MMN task: Randomize trial order without repeating a deviant sound twice in a row

I work on Win 10, PsychoPy v2021.2.3 standard standalone version

I am trying to randomize trials.
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 for the sound standard.wav
  • 100 lines for the sound deviant_1.wav
  • 100 lines for the sound deviant_2.wav
  • 100 lines for the sound deviant_3.wav
  • 100 lines for the sound deviant_4.wav

The csv file looks like

stim              trigger_1     trigger_2     time_trigger_1     time_trigger_2
sound_file.wav    1             2             0.1                0.6

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.

Below is the code I inserted in my first routine:

filename_to_read = _thisDir + os.sep + 'MMN_stim.csv'
filename_to_write = _thisDir + os.sep + 'MMN_stim_B1_random.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,0] != 'standard.wav') and (stim_list[i+1,0] != '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))

However, 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.

When I remove the while loop, everything is working fine but there is systematically consecutive deviant sounds.
Any advice would be greatly appreciated!!

Thanks in advance,
Ambre

The way I do this with prospective memory targets is have a jittered gap. You can see my PM code on Pavlovia which is similar my independent randomisation demo. I preload the stimuli into lists and then present them in a loop with no spreadsheet.

1 Like

Many thanks for your reply. Just to be sure, is that the correct link of the experiment you mentioned: VESPR / PM Time · GitLab?

Indeed I don’t really need to write the randomized list, and as I have only 4 different stim I could easily write a code to determine triggers and triggers_times. However I already tried to do it without any spreadsheet (see code bellow) but it still does not work. I’m stucked at the very beginning of the experiment. That is I can launch the experiment, enter the participant number, and then nothing happens, I don’t stop seeing the blank grey screen.
This is due to the while loop and does not come from problems about playing stimuli. But I’m fully unable to figure out what’s wrong as I do not have any error message.

I did not see any conditional randomization in your code, could you tell me if you have one and if so in which routine it is?

Bellow is the code creating the stimuli list without spreadsheet:

stim_list = ['standard.wav']*1030 + ['deviant_1.wav', 'deviant_2.wav', 'deviant_3.wav', 'deviant_4.wav']*100
shuffle(stim_list)

doubledev = False
list_ok = False

while list_ok == 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:
        list_ok = True  

That’s the correct demo.

The spreadsheet has Words and Targets (equivalent to your standard and deviant trials).

The words are loaded into wordlist (in the word_loop loop)
The targets are loaded into targetlist (in the targets loop)

targetgap is set to 25 in the code component in the start routine.

The type of trial is decided in the code_pretrial component in the pretrial routine.

In this demo the targets are at fixed intervals. However, I usually add a jitter, e.g.

putTarget+=targetGap+randint(0,targetJitter)

If you wanted to adopt this principle in your code then you code have something like

conditions = []
minGap = 10
maxGap = 15
for i in range(len(wordlist)+len(targetlist):
     if i == putTarget and len(targetList):
          conditions.append(targetlist.pop())
          putTarget+=randint(minGap,(maxGap+1))
     else:
          conditions.append(wordlist..pop())

Many thanks @wakecarter !
I did follow your solution but I got errors like could not convert the string to float. Can’t understand why since I only used strings as strings. Anyway I saved the generated list as a csv file, then called this file with the trial loop and now everything works fine!

I write my entire solution below if anyone need it:

In my instruction routine (the first one) I inserted a code component :code:.

  • In the Before Experiment tab:
    import csv

  • In the Begin Experiment tab:

headers = [['stim', 'trigger_1']]
standardList = [['standard.wav', '1']]*1030
deviantList = [['deviant_1.wav', '3'], ['deviant_2.wav', '5'], ['deviant_3.wav', '7'], ['deviant_4.wav', '9']]*100
shuffle(deviantList)

stimList = []
minGap = 1
maxGap = 4
stim_num = 0
  • In the Begin Routine tab:
# Make sure putDeviant will not be < 7
putDeviant = 6 + randint(minGap, (maxGap+1))  

for i in range(len(standardList)+len(deviantList)):
# Make sure the 1st deviant appears after at least 7 standards
    if stim_num < 7: 
        stimList .append(standardList.pop())
        stim_num += 1
# Insert a deviant when the value of putDeeviant = i
    elif i == putDeviant and len(deviantList): 
        stimList .append(deviantList.pop())
        stim_num += 1
        putDeviant = stim_num + randint(minGap, (maxGap+1))
# In other cases, insert a standard
    else:
        stimList .append(standardList.pop())
        stim_num += 1

# Add headers to the stimuli list
all_stim = headers + stimList 

# Write the list with the headeers into *.csv
filename_to_write = _thisDir + os.sep + 'MMN_stim_B1_random.csv'
np.savetxt(filename_to_write, all_stim, delimiter =";", fmt ='% s')

Then, I have my “normal routine” surrounded by a loop calling MMN_stim_B1_random.csv to play the spoken stimuli.
Works perfectly :ok_hand:

Many thanks again!