Selecting differents rows of conditions

Hi,
I am new to PsychoPy. In my experiment, I have 3 conditions and each condition has 45 rows. I have an outer loop in my builder file to select a specific condition and after that, I want to select randomly 15 rows of the selected condition three times without repetition. It means that I have 3 blocks in each block I want to choose 15 rows of the specific condition and when the 3 blocks run each participant sees all of the 45 rows of each condition. I use the $choice(45, size=15, replace=False) but when I determine the number of repeats as 3 it repeats the 15 rows 3 times but I want PsychoPy to select another 15 rows that are different from the previously selected rows. for example, if it selected rows 1 to 10 and 15 to 20 for the first block it can select rows of 20 to 25 and 35 to 45 for the second block and the remaining rows for the third block. Could you please help me to solve this? Thank you very much.

Can you clarify the terminology here a little?

Also it’s not clear whether you have one conditions file of 45 rows and want to split it across three blocks of 15 trials, or three conditions files of 45 rows each but only want to present a subset of 15 rows from each.

Lastly, please describe what characterises a “block” exactly. ie what is different between one block and the next?

Hello Michael,
Thank you very much for your response. At all, I have 135 excel rows and I divided it into three different excel files named conditions 1, 2 and 3. So I have an outer loop that randomly selects one on these excel files after that I want to divide each condition to 3 blocks of 15 trials meaning each participant randomly sees one row of condition ‘x’ for 15 times and after that, block 2 comes. Every single participant will see all and the same 135 trials just in a different block and trial order. The difference between blocks will be the actual trials one is presented with. for example participant 1 follows condition1 : rows(13,12,10, 42 and 21to33) and participant 2 follows condition1: rows(1,2,4,6,12 and 35to45). it is also meaning that I have 3 outer loop blocks and 3 inner loop blocks. 3345 = 135. It is important that each participant sees each trial only once and block 1 randomly selects 15 rows of condition ‘x’ and block 2 randomly select another 15 rows and finally block 3 select the remain rows. you can see the design of the experiment.!

OK, just to make sure if have this right, it goes like this?

Block Condition_file
1     1  (15 rows)
1     2  (15 rows)
1     3  (15 rows)
2     1  (another 15 rows)
2     2  (another 15 rows)
2     3  (another 15 rows)
3     1  (remaining 15 rows)
3     2  (remaining 15 rows)
3     3  (remaining 15 rows)

Yes, that’s right. Each condition is divided to 3 blocks of 15 trials and each participant should see all 135 trials but only once. I try to use choice option for selected rows but when the second block runs it shows some trials again. Also every time that I run the experiment the new 15 trials will randomly be showed to participants so I can’t divide the list by myself. PsychoPy should choose 15 trials randomly for block 1 and 15 new one for block 2 and the remaining for block 3 of first condition.

Yes, this is because each time a random loop runs, it will sort the rows in a different random sequence, inevitably meaning some overlap in selections. There are two ways around this:

  • set the “seed” for the random number generator. Software random number generators are in fact deterministic: if you set their starting value, you will always get the same “random” sequence. e.g. put the number 90210 or any other positive integer in the seed field, and you will get the same random sequence across blocks. This means you could just choose rows 0:15 in block 1, 15:30 on block 2, and 30:45 on block 3, and get a random order without repetition. But this soon gets complicated, as you will need to have different seeds across condition files, and across participants.
  • take the opposite approach, using a “sequential” rather than random loop, so the ordering is no longer a problem, but use random selections of rows, that can be managed across blocks to avoid repetitions. This is probably the way to go, but will take a small amount of code to implement.

Insert a code component from the “custom” component panel. In its “begin experiment” tab, put something like:

file_rows = {} # a dictionary to hold all three row orders

for file_number in [0, 1, 2]:
    row_order = list(range(45)) # yields 0:44
    shuffle(row_order)          # randomise the order
    file_rows[file_number] = row_order # store it

So now you have a dictionary called file_rows which can be used to look up the randomised row order for each of the three conditions files, by specifying the index of the conditions file, e.g:

file_rows[0]

would give you the randomised list of 45 row indices for the first (zeroth) conditions file. And this would give you 15 randomly selected entries from that list:

file_rows[0][0:15]

But note that if we then get file_rows[0][15:30], there will be no overlap in the selected row values - the randomisation is within the list itself, so we can use deterministic indices from that list and still get a random, non-repeating, selection.

So now in the trials loop, you need to put an expression in the “Selected rows” field which will look up the list for the appropriate file (0, 1, or 2), which will be based on the iteration number of the conditions loop (also 0, 1, or 2), and then also get entries 0:15 in block 0, 15:30 on block 1, and 30:45 on block 2, based on the iteration number of the blocks loop (also 0, 1, or 2, but changing more slowly). So an expression like this:

$file_rows[conditionsSelectLoop.thisN][(blocks.thisN * 15): ((blocks.thisN + 1) * 15)]

i.e. on the first run, conditionsSelectLoop.thisN is 0, so you get the first list of rows, and based on the blocks loop also being 0, you will get entries 0:15 of that randomised list.

NB as described, your screenshot doesn’t show the correct nesting of the loops. They need to be nested in order from fastest to slowest changing, so the innermost loop should be trials, the second should be conditions, and the outermost should be blocks.

All this is untested, of course, so come back with any problems.

There was a square/round brackets typo in that code that has now been amended as above.

Thank you very much Michael. I corrected it. I put the code on the start routine. At first, I change the place of blocks and conditionSelect loop but I noticed that when the experiment runs it will choose 15 trials from one condition and the subsequent 15 trials is possible to be on another condition but I want the three blocks of 15 trials of condition ‘x’ run and after that, the other condition comes. So again I change their place like the picture now everything is OK but sometimes it does not generate 15 trials on every block and it seems that it can not load the picture and have some delay

. It does not occur every time but randomly happens.15 16 17

No, as above, it needs to be in the “Begin experiment” tab, as this stuff should only happen once. If it is in “begin routine”, you will keep regenerating fresh random lists on each trial, so there will no longer be any experiment-wide control of the selected rows.

That’s the opposite of this:

but you’ll know what you want. This leaves me a little lost though. If the conditions files aren’t being broken up across blocks, then there isn’t anything complicated in this at all.

You should just need two loops, one inner one to run the trials, and one outer one to select the conditions files. The only other thing that would need to change is that you bring the “block_break” routine inside the trials loop, and make it conditional, so that it only runs on every fifteenth trial. i.e. insert a code component on it, and in its “begin routine” tab, put something like:

if trials.thisN not in [14, 29, 44]:
    continueRoutine = False

Then all of the other custom code can be deleted. Just set the trials loop to be “random” and you’ll get what you need automatically.

In this scheme, the “blocks” aren’t implemented explicitly. Instead, there is just a sort of marker to the subject with the occasional occurrence of the block break routine.

Thank you very much Michael. It works correctly. Thanks again.