Randomizing Columns and Rows

OS (e.g. Win10): Win10
PsychoPy version (e.g. 1.84.x): Latest version
Standard Standalone? (y/n) If not then what?: Yes
What are you trying to achieve?:

I am trying to build an experiment that I can post online. My biggest issue is that I’m having trouble conceptualizing how to minimize the number of routines and maximize the number of loops I use.

I have a list of Mandarin words. Each word in my list has three tones. I attached a spreadsheet here. wordList.xlsx (9.8 KB)

Let’s call each row a “set” and columns 2-4 tones 1, 2, and 3. Each of the three “tones” is an audio file that should play using the audio component in the builder. For the experiment, I need each set (cai, ba, ying, etc.) to play in full (Tone 1, Tone 2, Tone 3) before moving to the next set. But I would like the order in which the tones of each set play to be random (i.e., ba1, ba3, ba2 for participant 1 but ba3, ba1, ba2 for participant 2, etc.).

I’d also like the order of sets to be random (i.e, participant 1 gets piao first but participant 2 gets ba first). I have figured out how to randomize the order of sets OR the order of tones within a set, but not both. The only way I can figure out how to randomize both is to create a new routine for each word set (which is seven routines) and use a loop to randomize the presentation of tones within in set and then use another loop around each of those 7 routines to randomize the order of which set is presented. I know it is frowned upon to use this many loops and is not very economical, and I know there must be a way to do this.

Any help would be greatly appreciated. Unfortunately, my knowledge of Python is quite limited, so I’m forced to use the Builder design.

Thanks!

We advise even experienced Python developers to use Builder, as it handles all the boring stuff for you, so one can just concentrate on adding any small snippets of Python code needed to supplement it. This is one of those cases.

You just need a single routine. Insert another loop around it (i.e. nested inside your current loop that is linked to your conditions file), but don’t link this inner loop to a file. Just give it an nReps value of 3. So this routine will run three times within each trial (i.e. for each row of your conditions file). On each iteration of the inner loop, we will select which column of the file to use.

On your routine, insert a code component (from the “custom” components panel). Put it above your sound component (you can change the vertical order of components by right-clicking on their icons). That way, the sound component gets to refer to the latest-selected sound file. In the code component’s “begin routine” tab, put something like this:

# only on the first iteration per trial:
if your_inner_loop_name.thisN == 0:
    tones = [Tone1, Tone2, Tone3]
    shuffle(tones)
    
    # record the order in the data file:
    thisExp.addData('first_tone', tones[0])
    thisExp.addData('second_tone', tones[1])
    thisExp.addData('third_tone', tones[2])

# pull one out of the hat for this trial:
current_tone = tones.pop(0)

Then in your sound component, put $current_tone in the “sound” field, set to update on every repeat.

So in essence, you have just one routine, nested within two loops. The inner loop selects the tone (columns), and runs three times per trial. The outer loop selects the row from the conditions file, and runs once per trial. The outer loop handles the randomisation of trials (rows). Just set that loop to “random” or “full random” as needed.

Hi Michael,

Thanks for the response—this helps immensely. I tried implementing this (I did have to change $tone to $tones), but I’m getting an error before the first trial’s audio plays. To provide a bit more info, I have it so that when the audio plays, an image component comes up for the participant to choose an option using keys 1, 2, or 4 based on the audio heard. The image is always the same, so I have it set to “constant”. Here are a couple screenshots and the error message:

From cffi callback <function _StreamBase.init..callback_ptr at 0x000002549D19CC80>:

Traceback (most recent call last):

File “D:\PsychoPy\lib\site-packages\sounddevice.py”, line 740, in callback_ptr

return _wrap_callback(callback, data, frames, time, status)

File “D:\PsychoPy\lib\site-packages\sounddevice.py”, line 2516, in _wrap_callback

callback(*args)

File “D:\PsychoPy\lib\site-packages\psychopy\sound\backend_sounddevice.py”, line 201, in callback

dat *= thisSound.volume # Set the volume block by block

numpy.core._exceptions.UFuncTypeError: ufunc ‘multiply’ did not contain a loop with signature matching types (dtype(’<U63’), dtype(’<U63’)) → dtype(’<U63’)

Experiment ended.

I think that should be $current_tone (a single file name) rather than $tones (a list of all three filenames).

1 Like