Randomizing with constraints

Making custom randomized orders

We have already seen that by default builder allows you to present trials in one of three orders, including:

  • Random

  • Sequential

  • Full Random

From this we know that if we want a pre-specified order, the best way to do this is to use the ‘sequential’ option and specify that order in our conditions file. But what if we want more “custom”, “on-the-fly” randomization.

Using loops for loading stimuli/conditions

Loops don’t just have to be used to present trials/repeat stimuli, they can also be used to load in your conditions file, and manipulate it how you want:

  • Make a conditions file with the header ‘Word’ and a list of words to present

  • Add a routine to your experiment and call it ‘loading’.

  • Add a code component to this routine and at under Begin Routine use print(Word)

Once we know how to load our stimuli, we can use our python skills to manipulate the order of words from within our experiment itself.

Some cases where this can be useful:

  • You want some stimuli to be presented at fixed trials (e.g. 5th, 10th, 15th) trial is fixed, but with the interleaving trials selected randomly.

  • You want to randomly pick a subset of the to-be-presented words in some practice trials.

and many more! We will practice with the first case example.

Setting up our conditions file

In this example, we are going to make sure every 5th word is presented in a fixed order, with the other words presented randomly. First, set up your conditions file to be something like this (but with more words).

Word

Fixed

N

Cat

0

1

Dog

0

2

Fish

0

3

Owl

0

4

Tree

1

5

Here our column “Word” is the word that will be presented and the column “Fixed” indicates if we want that word to be in a fixed location.

Word

Fixed

N

Cat

0

1

Dog

0

2

Fish

0

3

Owl

0

4

Tree

1

5

Accessing the “fed in” trial info

We could load these variables into our experiment in several ways. Remember that the Trial Handler loads in a conditions file as a list of dictionaries. In fact, we can access that list directly using trials.trialList where trials is the name of our loop. Or we could access a single trial dictionary using trials.trialList[n] where n is the index.

Note

There are several inbuilt functions that can be handy for use with trialHandler:

trials.getCurrentTrial() - fetches the current trial info trials.getFutureTrial(n) - fetches the info of the trial n ahead trials.getEarlierTrial(n) - fetches the info of the trial n back

Making a custom trial list

Once we have access to the list of trials we can pick out our random trials:

oldTrials = myLoop.trialList

randomTrials = []
for myTrial in oldTrials:
        if not myTrial['Fixed']:
                randomTrials.append(myTrial)

Then we can piece our trialList back together by randomly sampling without replacement from the randomTrials list:

newTrials = []

for myTrial in oldTrials:
        if myTrial['Fixed']:
                # Use this trial
                newTrials.append(myTrial)
        else:
                # re sample without replacement
                shuffle(randomTrials)
                newTrials.append(randomTrials[-1])
                randomTrials.pop()
print(newTrials)

Working through a custom trial list in builder

Once we have our custom randomized trial list we need to use that in Builder. Usually we wrap a loop around a routine and feed in a conditions file. Then nReps corresponds to the number of times we repeat that file. This time we need to do it a little differently…..

Add a routine called ‘showWords’ and inside it add a simple textBox component that lasts for 0.5 seconds. Wrap a loop around the routine and call it ‘trials’. This time we want nReps to correspond to the length of our custom trialList (‘newTrials’). So you can type len(newTrials) in the nReps field.

Note

Online len() might not smoothly translate in the nReps field. If that happens make a custom variable after the creation of ‘newTrials’ and use that instead in teh nReps field, e.g. :code:` myNReps = len(newTrials) will smoothly translate in a code component.

OK finally, we need to use the trial info from each run in our textBox component. In the Text field of the component type newTrials[trials.thisN] and make sure to set every repeat.

Note

Online trials.thisN might not smoothly translate (but it should do very soon). In place, you can use a code component to count the iterations, in the ‘Begin Experiment’ tab type trialCount = 0 then in the ‘End Routine’ tab type trialCount +=1 then use ‘trialCount’ in place of ‘trials.thisN’.

If you ran that now you might be surprised to see a full dictionary printed out on each trial. Remember each trial is a dictionary we need to access the value corresponding to the key ‘Word’. So for the final touch update newTrials[trials.thisN] to newTrials[trials.thisN]['Word']

Exercise (15-20 mins)

This exercise will make a list of trials entirely through code, without the need to import an external conditions file. You will need a routine with a code component to create your trialList then a routine with a loop to iterate through that trial list:

  1. Make a two lists - one of odd numbers and one of even numbers.

  2. Use a for loop to interleave the numbers from your list and make a larger list that alternates between odd and even.

  3. Use the numbers in your list to present a series of trials, with one number presented per trial.

Tip: remember we can sample without replacement using :code:`shuffle()` and :code:`pop()` so that the selection of numbers is random each time - this method works locally and online


Back to top