Help with randomization

Hello,

We are 3 French-speaking students and we both have no experience with using psychopy and python. We would like to build up an experiment which consists in randomly presenting several words (10 words from a list of 15) which are randomly preceded by a red or green fixation cross and to repeat this operation 8 times. If a certain word is preceded by a red cross, then it should be preceded by a red cross all 8 times. Actually, we don’t really know where to start…

Can anyone help with that? Thank you very much in advance

Lola

Is the order of the randomly-selected 10 words constant in each of the 8 blocks, or does it vary randomly across blocks? If it is constant, you can just do the following in a code component (inserted from the “custom” component tab), with this in the “begin experiment” tab:

# define your list of words:
words = ['cat', 'dog', 'etc', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14']

# randomise its order:
np.random.shuffle(words)

# Select a sample of 10:
words = words[0:10]

# balanced, random order of fixation cross colours:
cross_colours = ['red', 'green'] * 5
np.random.shuffle(cross_colours)

Then surround your trial routine(s) with two nested loops. The inner loop (trials) has an nReps value of 10, and the outer blocks loop has an nReps value of 8. Control the stimuli by getting entries from the two lists according to the trial number index:

Put this in the text field of a text stimulus:

$words[trials.thisN]

and this in the colour field of your fixation cross stimulus:

$cross_colours[trials.thisN]

And since these variables have been created in code, they will need to ba manually added to the data file. So put this in the “Begin routine” tab:

thisExp.addData('word', words[trials.thisN])
thisExp.addData('colour', cross_colours[trials.thisN])

Dear Michael,

Thank you very much for your help, we will try this out!

Have a nice day,

Lola

Dear Michael,

Unfortunately, it doesn’t work out but I guess the mistake comes from me. Actually, when I try to run the experiment the runner closes immediately and I don’t get an error message.

Here’s what I did :

Capture d’écran 2020-10-01 à 10.49.17

Capture d’écran 2020-10-01 à 10.49.10

To be more specific, the words must be displayed for 4 seconds and the fixation crosses only for 1.

I hope I don’t take up too much of your time,

Lola

Dear @Michael,
I am one of the students working on that experiment.
Your code works great, thank you! I have adapted it a little bit in order to match our needs.
There is one last thing that we are trying to do but for now without much success.
We were hoping that you could help us out!

Right now we have a final stimulus list of 30 words (inner loop “trials” with nReps = 30), selected randomly (np.random.shuffle(words)) out of a list containing 45 words in total. Each stimulus is preceded by a fixation cross. Half of the trials start with a green fixation cross, the other half start with a red fixation cross. The order of red/green crosses is randomly attributed.
These 30 fixation-cross-word pairs are presented 8 times (outer loop “blocks” with nReps = 8). Within each block, the order of presentation of stimuli is the same. So if the first block starts with

trial 1: “green cross” / “cat”
trial 2: “green cross” / “dog”
trial 3: “red cross” / “fish”
trial 4: “green cross” / “lizard”
trial 5: “red cross” / “horse”
… then all 8 blocks look exactly the same.

We were wondering if there is a way to keep the associations between fixation cross color and stimulus word consistent across blocks (if “cat” was preceded by a green fixation cross the first time, we would like it to be preceded by a green fixation cross throughout all 8 blocks) but randomize the order of stimulus presentations within the blocks?

So if block 1 is as described above, we would like to have block 2 such as
trial 1: “green cross” / “lizard”
trial 2: “green cross” / “dog”
trial 3: “red cross” / “fish”
trial 4: “green cross” / “cat”
trial 5: “red cross” / “horse”
… block 3 keeping the cross color/word association but presenting them in a new random sequence, etc.

Thank you very much for your time and your help!
V

Begin Experiment:

wordsPhono = ['word 1', 'word 2', 'word 3', 'word 4', 'word 5', 'word 6', 'word 7', 'word 8', 'word 9', 'word 10', 'word 11', 'word12', 'word 13', 'word 14', 'word 15']
wordsSemantic = ['word 1', 'word 2', 'word 3', 'word 4', 'word 5', 'word 6', 'word 7', 'word 8', 'word 9', 'word 10', 'word 11', 'word12', 'word 13', 'word 14', 'word 15']
wordsNothing = ['word 1', 'word 2', 'word 3', 'word 4', 'word 5', 'word 6', 'word 7', 'word 8', 'word 9', 'word 10', 'word 11', 'word12', 'word 13', 'word 14', 'word 15']

np.random.shuffle(wordsPhono)
np.random.shuffle(wordsSemantic)
np.random.shuffle(wordsNothing)

#Select a sample of 10:
wordsPhono = wordsPhono[0:10]
wordsSemantic = wordsSemantic[0:10]
wordsNothing = wordsNothing[0:10]
FullStimList = wordsPhono + wordsSemantic + wordsNothing

#balanced, random order of fixation cross colours:
cross_colours = ['red', 'green'] * 15
np.random.shuffle(cross_colours)

Begin Routine:

thisExp.addData('FullStimList', FullStimList[trials.thisN])
thisExp.addData('colour', cross_colours[trials.thisN])

What you can do is zip together the list of colours and words, so that they get grouped into pairs, e.g.

stimuli = zip(cross_colours, FullStimList)

But for convenience, we can go further and put each pair of values into a dictionary so you can access the attributes by name:

stimuli = [{'colour':colour, 'word': word} for colour, word in zip(cross_colours, FullStimList)]

What you will now have is a list of 30 dictionaries, which pair together the two named values needed for each trial. To get values for a given trial, you can access them like this:

trial_colour = stimuli[trials.thisN]['colour']
trial_word = stimuli[trials.thisN]['word']

To get a different order on each iteration of the loop, put this in the “Begin routine” tab so that the list of stimuli gets re-shuffled at the beginning of each loop:

if trials.thisN == 0: # only do this once per block
    shuffle(stimuli)

Dear @Michael,

Thank you for your help, your suggestion works great overall!
However, I am facing a funny problem.

The Stimuli are now being presented in a random order within the 8 different blocks, which is great!
The pairing between fixation cross color and stimulus word is preserved across the 8 blocks for all stimuli EXCEPT for the one that has been presented as Stimulus 1 during Block 1.

So if the very first stimulus presented at the beginning of the experiment was “green fixation cross” + “dog”, during the second block it turns into “red fixation cross” + “dog”. As far as I have tested it, it stays “red”+“dog” until the last block.

When I check the logfile it seems like all pairs are matching (it shows consistently “red” + “dog” for all blocks, even for the first one) but when the experiment is running on the screen that is not the case, the first stimulus I see is “green” + “dog”.

Is it maybe a problem with the instructions I have put in the Color tab of my fixation cross textbox… :thinking:?

Thank you very much in advance!
V

Begin Experiment

wordsPhono = ['word 1', 'word 2', 'word 3', 'word 4', 'word 5', 'word 6', 'word 7', 'word 8', 'word 9', 'word 10', 'word 11', 'word12', 'word 13', 'word 14', 'word 15']
wordsSemantic = ['word 1', 'word 2', 'word 3', 'word 4', 'word 5', 'word 6', 'word 7', 'word 8', 'word 9', 'word 10', 'word 11', 'word12', 'word 13', 'word 14', 'word 15']
wordsNothing = ['word 1', 'word 2', 'word 3', 'word 4', 'word 5', 'word 6', 'word 7', 'word 8', 'word 9', 'word 10', 'word 11', 'word12', 'word 13', 'word 14', 'word 15']

np.random.shuffle(wordsPhono)
np.random.shuffle(wordsSemantic)
np.random.shuffle(wordsNothing)

#Select a sample of 10:
wordsPhono = wordsPhono[0:10]
wordsSemantic = wordsSemantic[0:10]
wordsNothing = wordsNothing[0:10]
FullStimList = wordsPhono + wordsSemantic + wordsNothing

#balanced, random order of fixation cross colours:
cross_colours = ['red', 'green'] * 15
np.random.shuffle(cross_colours)
stimuli = [{'colour':colour, 'word': word} for colour, word in zip(cross_colours, FullStimList)]

Begin Routine

if trials.thisN == (0): # only do this once per block
    shuffle(stimuli)

trial_colour = stimuli[trials.thisN]['colour']
trial_word = stimuli[trials.thisN]['word']

thisExp.addData('StimOrder', stimuli[trials.thisN])

This is the TextBox component for the fixation cross:

This is the TextBox component for the word stimuli:

I haven’t looked closely, but I guess that this is due to the order of your components. They are executed from the top down. So right click on the code component icon to shift it to the top. i.e. the code there needs to be executed before the fixation component attempts to access the results – in this case the fixation component may be accessing item 0 of the list before it has been shuffled. meanwhile, the word shown is likely the correct one, because that is accessed from the list on the next routine, after the shuffling has happened.

Code component ordering is often the cause of “off by one” errors like this. It can also make it possible that the values you record in the data file are off by one trial, so care needs to be taken with testing this stuff carefully (i.e. compare what is displayed on screen during a test with what is recorded in the data).

You can also use the “shortcut” variable names defined in the code above on each trial (e.g. trial_colour) in your components. That makes it less likely that errors will occur with repeating typing more complicated expressions like stimuli[trials.thisN]['colour'] everywhere.

Hi Again,

Our code looks like this now and it works perfectly! However, we would like to add constraints to the randomization so that the colour of the fixation cross does not repeat consecutively more than 3 times! Is there a way to add some code component to the code we already have?

Thank you in advance!

wordsPhono = ["rive", "sûr", "cale", "tir", "nul", "top", "onde", "chasse", "sac", "lime", "chope", "barque", "ride", "passe", "dard"]
wordsSemantic = ["son", "choc", "tort", "quiche", "gîte", "patte", "art", "noce", "dors", "caisse", "laid", "part", "beau", "nappe", "tôt"]
wordsNothing = ["si", "mire", "chaque", "hausse", "hante", "cote", "digue", "aide", "neige", "brève", "niche", "sauf", "hutte", "verre", "marre"]

np.random.shuffle(wordsPhono)
np.random.shuffle(wordsSemantic)
np.random.shuffle(wordsNothing)

# Select a sample of 10:
wordsPhono = wordsPhono[0:10]
wordsSemantic = wordsSemantic[0:10]
wordsNothing = wordsNothing[0:10]
FullStimList = wordsPhono + wordsSemantic + wordsNothing

# balanced, random order of fixation cross colours:
crossPhono = ["red", "green"] * 5
crossSemantic = ["red", "green"] * 5
crossNothing = ["red", "green"] * 5
cross_colours = crossPhono + crossSemantic + crossNothing

stimuli = [{"colour":colour, "word" : word} for colour, word in zip(cross_colours, FullStimList)] 

Unfortunately this seemingly simple constraint usually leads to non-deterministic code. There often isn’t much alternative to brute forcing it. i.e. Create a loop where you randomise the conditions, then check if the sequence meets the constraint. If not, try again. It might take one iteration, or thousands, depending on the constraint. It won’t usually take the computer long to do, but the code will be more messy and involved than you have above. And the end result is that the subject now has guaranteed information in the “random” sequence (that the colour will always change after three consecutive presentations). That may or may not be important. But it will be a good coding exercise for you.

Hi @Michael,

Thank you for clarifying this!

Best regards