Condition Randomization: The same condition does not appear more than twice back-to-back

What are you trying to achieve?:
I am presenting words to participant from three different conditions. Each condition has 27 trials. I would like to randomize the condition such that trials of the same condition would be presented more than twice back-to-back (e.g., condition 1, condition 3, condition 2, condition 2, condition 3, condition 1, etc.)

What did you try to make it work?:
I created a loop that draws from a .xsls with two columns:
Stimulus: to be called in text within a routine by $stimulus
Condition: It just says condition 1/2/3
I then added a code component to my builder that resembles an n-back task.

#First, I created two variables in Begin Experiment: 
oneback = NA
twoback = NA
# Then, in Begin Routine:
if oneback == twoback:
    condition != oneback
# Then, in End Routine:
twoback = oneback
oneback = condition

What specifically went wrong when you tried that?:
It just seems like the code block did nothing to condition the randomization. Possibly because it could not call condition from the excel file because I didn’t define that variable? I am not sure how to save condition in the code block to $condition in the .xsls if that is the problem. Any advice would be greatly appreciated.

condition.xlsx (8.8 KB)
test.psyexp (7.0 KB)

I’m not sure whether that is the issue you’re looking for, but the following code has no effect.

if oneback == twoback:
    condition != oneback

The != is a logical operator and returns true or false. It doesn’t change any variable in your case.

Thanks for your help, LukasPsy.

What would be an appropriate line for me to replace the condition != oneback line such that the code says make sure the condition of the current stimulus won’t be oneback’s condition?

What condition do you want condition to be set to if oneback == twoback returns true?

I would like to shuffle the condition such that the current trial won’t repeat the oneback/twoback condition a third time.

For instance, if oneback and twoback are both condition 2, I would want the current trial to be drawn randomly from condition 1 or 3.

Thanks again for following up.

Does this work?

# begin exp
conditionsList = [condition1, condition2, condition3] # substitute the elements of the list with whatever your conditions are

# begin routine
condition = shuffle([i for i in conditionslist if i != oneback])[0] # shuffle a list of the conditions in conditionslist excluding the condition oneback; then, take the first element in this shuffled list

Thank you. I think that’s a great start, but I am getting the error:
PhonemeSet = shuffle([i for i in conditionsList if i != oneback])[0]
TypeError: ‘NoneType’ object has no attribute ‘getitem

My understanding is that this code is manually constructing a list and then reshuffling the conditions based on my rule, but it is not calling anything from the excel sheet in the builder loop or constraining the randomization of the loop.

How can I extract from the condition column of the excel sheet associated with the loop for each stimulus? In the text block of my routine, I can simply call $Stimulus to display the stimulus from the spreadsheet, but I am unsure about how to do the equivalent of $Condition in the custom code block. To put it concretely, what I am aiming to do is for each stimulus, look for the condition column of the same row in the excel sheet and check that it is not the same as the previous stimulus’ condition. If the condition is the same, do not present this item and reshuffle until the stimulus comes from a different condition.

Thanks again.

Oh sorry. Again, this won’t work because shuffle() returns None.
You’ll have to create a copy of that list first. Then shuffle it without assigning the shuffle call to a variable. Then, you can draw the stimulus from the copied list.

oneback = None # I don't know why you put NA and why this doesn't result in an error
twoback = None

# Begin Routine (you have to substitute 'trialHandlerName' with whatever name you gave your loop to which you assigned your conditions file)
if oneback == twoback:
    stimulusList = [i for i in trialHandlerName.trialList if i != oneback]
    shuffle(stimulusList)
    stimulus = stimulusList[0]['stimulus'] 
    # shuffle a list of the conditions in the trialList of trialHandlerName excluding the condition oneback; 
    # then, take the first element in this shuffled list, which is a dict; 
    # in that dict, you call the value for the 'stimulus' key; this key is 'stimulus' because you named the column in your excel file 'stimulus'

# Then, in End Routine:
twoback = oneback
oneback = condition

Thanks again for following up.

This runs without error but I am still getting 3 of the same conditions back to back sometimes. I think there may be two possible reasons for that:

  1. oneback = Condition is not pulling the condition information from the stimulusList dictionary. I tried to change that line to the one below, to no avail:
oneback = stimulusList[0]['Condition']

I am not sure what to save oneback to such that it pulls out the condition parameter for the current trial.

  1. In the text display within the routine, I am currently displaying $stimulus (as it displays in the code), it says it is not defined. Maybe it is still just ignoring the conditioned randomization code and pulling from the excel sheet in a fully randomized manner?

I am also confused about the stimulusList line. You explained that the resulting stimulusList is a dictionary. However, in that line, what is the i indexing? The number of the item, condition, or stimulus? I am confused because I do not understand where it indexes that I want to exclude by condition but not the unique stimulus, and oneback is also supposed to save the condition instead of the stimulus.

Really appreciate all your help.

Could you upload a minimal version (a .py or .psyexp file excluding anything not of relevance to your question) of what you are trying to accomplish and your conditions file?

The i is not an index in this case. The line with i is an example of list comprehension. It takes the trial list of the trialHandler and for every item i in the trial list it checks if the item i is not equal oneback and keeps it in the list if that is the case.

The stimulusList is a list of dictionaries. When we take the index 0, we get the first item in that list (indexing is zero based in python), which is a dictionary containing values drawn from your conditions file.

They are now attached to the original post. The custom code compoenent currently has your previous code, with the variables edited to the names of the excel sheet and the loop I have. Thanks again!

I finally got to look over your files :slight_smile:

First of all: Python is case sensitive. Your column names are capitalised and you tried to access them in lower case letters at some points in your code. condition is not the same as Condition !

You could change the text stimulus depending on what was shown in the last trial; however, you would potentially show the same stimuli several times (although the same stimulus would not follow itself directly).

# Begin Experiment
oneback = None
twoback = None

# Begin Routine
if oneback == twoback == condition: # if three following trials have belonged to the same condition
    stimulusList = [i for i in trials.trialList if i['Condition'] != oneback]
    shuffle(stimulusList)
    Stimulus = stimulusList[0]['Stimulus'] 
    Condition = stimulusList[0]['Condition'] 
    text.setText(Stimulus) # you need that line because the TextStim.text attribute is set to Stimulus before your custom code is inserted
    trials.addData('Stimulus', Stimulus)
    trials.addData('Condition', Condition)

# Then, in End Routine:
twoback = oneback
oneback = Condition

My advice is that instead of trying to change the sequence online, you insert a code component with the following lines in the Begin Experiment tab to determine the randomised sequence of trials at the beginning. Then you set the conditions variable in the trials properties window to this myTrialList and change the loopType in the trials properties to sequential (because you do the randomisation beforehand).

# a list of dicts with your conditions
myTrialList = [
                      {'Condition' : 1, 'Stimulus' : 'cat'},
                      {'Condition' : 2, 'Stimulus' : 'dog'},
                      {'Condition' : 3, 'Stimulus' : 'tree'} # and so on (just as you did in your conditions file)
                      ]
# shuffle the list of dicts
shuffle(myTrialList)
# check whether where in the list three following trial are the same
while True:
    for i in range(lenth(myTrialList)-2): # go over the list
        if myTrialList[i]['Condition'] == myTrialList[i+1]['Condition'] == myTrialList[i+2]['Condition']: # if three trials belong to the same condition
            # do something (for example reshuffle your entire conditions list or change the values for specific trials or subsets of the list) 
            # you have to insert your own code here
where (myTrialList[i]['Condition'] == myTrialList[i+1]['Condition'] == myTrialList[i+2]['Condition']) is True
        continue # go back to the next iteration of the while loop (skip everything that is below) 
   break # exit the while loop

I haven’t tested my code, so there might still be mistakes in it.
If you get error messages, or the experiment doesn’t run the way you planned, try to compile the script (F5) and figure out the sequence in which the code is run.

Thanks for following up. I’ll try the second method.