Counterbalance - Across participants

Hello again PsychoPY community!

As I’m struggling to find an elegant solution to my problem, I wanted to once again reach out and ask for your opinions.

I am creating some routines at the beginning of the experiment and these routines will serve as “Training” trials where participants learn associations between certain shapes (3 shapes, let’s call them A B and C) & 3 sounds (let’s call them X Y and Z)

As can be seen in the below picture, one training trial looks like this. A shape (cue_hex_1; let it be shape A) appears simultaneously with a sound (story_1, let it be X) and then the routine ends when the sound stimuli ends. As you can see at the bottom there are 3 training routines in which three different shapes are being matched with three different sounds – so in other routines, shape B will appear with sound Y and so on…

Later there is a “Reminder” phase (see the picture below) where I show the shapes and also show a text (rem_1) that says something like “when you saw this shape you heard that story, remember?”. So for instance, I present shape A and the text says that it corresponds to sound X.

So the thing is, the shape & sound matches must be counter-balanced across participants (so there will be 6 different versions at the end). The non-elegant way to do this for me would be to just manually upload different sounds to different training routines & change the texts in the reminder routines also manually, at the beginning of each experiment. However, I believe there must be more efficient ways to achieve this – which I struggle to come up with at this moment.

I hope the description was clear. Any ideas would be appreciated!


an easy way to counterbalance is specify all intended combinations (shapes, sounds, reminder routines aso.) in an Excel-sheet and read the needed lines based on, for instance, the participant number.

BTW, it is better style and the code is easier to maintain if you reuse a routine when only the presented stimuli differ. That is training_1, training_2 and training_3 are “combined” in one routine “training” which is presented three times.

Best wishes Jens

Hello @JensBoelte Thanks a lot for your quick reply! :blush:

I followed your advice and put all the training / reminder routines in just one routine. Now, however, I have some other issues.

So to first show you, here is how one Training routine looks like:

I made this Excel sheet which includes the duration values (you can for now just focus on coloumn A to F, the rest is for Reminder routine and the logic is basically the same). What I did here is, I put the duration of each sound clip manually and I put those values in a way that it matches with the duration of a certain shape (e.g when dur_story_1 is 90, dur_cue_circle is also 90 and the rest of the durations in that row is zero, so nothing else will appear. Only Sound 1 and the Shape Circle will appear. When the loop iterates, then it moves on to other pairs)

I indexed the duration as, e.g. $dur_story_1 in the properties of “story_1” variable above. And did the same for all other stimuli in the loop.

So, my first question is… I would actually like it better, if participant press a button after a sound clip ends, to move on to the next one. It can be easily done when the routines are separate but when everything is in one routine, I couldn’t come up with a solution. Is there a way to set the button press as something that moves the loop forward? (If I’m making sense). If so, how could I adjust my excel table for this – right now, it is all based on the duration of the sound clips…

And to mention another thing, to counterbalance, I create separate condition files for each participant (6 different files in total, the one above is the 1st version) and I found this code below, which reads a certain condition file based on the participant number: (This code is from frank.papenmeier, see this discussion here: Selecting condition files depending on participant number - #2 by frank.papenmeier so, thank you!)

And my condition files are located in a folder like this:

So, the second question: my understanding is if, let’s say I will have 15 participants, I have to create 15 of these to make the indexing work with that code? Or, would there be a, again, “more elegant” way to do so?

Sorry for this long message! And thank you again!


when you have six different condition-files which you would like to distribute evenly across participants, your number of participants needs to be a multiple of six :wink:

The following code selects a condition-file on the basis of the participant number:

conditionFile = "dummy.xlsx"

if int(expInfo['participant'])% 6 == 0:
    conditionFile = "conditionFile6.xlsx"
elif int(expInfo['participant'])% 5 == 0:
    conditionFile = "conditionFile5.xlsx"
elif int(expInfo['participant'])% 4 == 0:
    conditionFile = "conditionFile4.xlsx"
elif int(expInfo['participant'])% 3 == 0:
    conditionFile = "conditionFile3.xlsx"
elif int(expInfo['participant'])% 2 == 0:
    conditionFile = "conditionFile2.xlsx"
    conditionFile = "conditionFile1.xlsx"

then enter


in the loop conditions file field.

I do not yet understand your trial structure. I understood that you have three stories which you like to combine with three shapes. In your example you show three stories and three shape. I would rather have a trial in which I’d show only one shape and one story.

Best wishes Jens

Hi again @JensBoelte !

thanks a lot for sharing this code, I really appreciate it.

I think I’ll also go back to having one shape & one story within a single trial. And I can still use my conditions file for counterbalancing. I am planning to put all the stimuli (say, all the shapes and sounds) in each of the training routines (3 routines) and loop through this 3 routine one time. With the help of the condition file, only certain 1 shape and 1 sound will appear in each routine. So to sum up, tI realized the counterbalancing is still achievable with seperate routines in this case and, seperate routines also allows me to put some breaks (with or without response…) in between these routines.

Sorry for being not super clear, as everything is still a work in progress and sometimes even not 100% clear to me either…

Anyhow, thanks again!


give this a try, it presents three shapes (triangle, rectangle, circle) with three visual stimuli (1, 2, 3)

ConditionFile.psyexp (17.4 KB)
conditionfile.xlsx (8.4 KB)

Best wishes Jens

Hello @JensBoelte ! Sorry for the late response but, thank you so much! This actually helped a lot :slight_smile:

Please note that the code will result in a particular UNeven distribution of participants to groups. Please see my previous post about this code.


wakecarter is correct (thanks for the hint). This code results in an uneven distribution of participants across conditions. I repeated an error I did earlier (see link above)

if int(expInfo['participant'])% 6 == 0:
    conditionFile = "conditionFile6.xlsx"
elif int(expInfo['participant'])% 6 == 5:
    conditionFile = "conditionFile5.xlsx"
elif int(expInfo['participant'])% 6 == 4:
    conditionFile = "conditionFile4.xlsx"
elif int(expInfo['participant'])% 6 == 3:
    conditionFile = "conditionFile3.xlsx"
elif int(expInfo['participant'])% 6 == 2:
    conditionFile = "conditionFile2.xlsx"
elif int(expInfo['participant'])% 6 == 1:
    conditionFile = "conditionFile1.xlsx"

results in a even distribution.

Best wishes Jens

1 Like

Thank you @wakecarter for the hint and thank you a ton for updating the code @JensBoelte I really appreciate it!

Hi! Just to check, the code should be added to the ‘before experiment’ part, right?

Thank you (have a similar problem as OP!)

Also I think there’s a typo in the last ‘participant’ :slight_smile:

(@grimfandangosh if you haven’t noticed)

Put it in Begin Experiment

Typo corrected.

Hello everyone,

once again thanks for the code, it works perfectly.

I would like to ask one more question though. How would I access to the participant number with a Phyton code, if I am using a naming convention like “MM_1_XXX” (where XXX would be the participant number, 001, 002, 0011, etc). Is there a way to index to last, or last two/three digit of the XXX?

I’m new to Phyton so, apologies!

Thanks! :slight_smile:


participant_number = int(expInfo['participant'][-3:])