Independent Pseudorandomization WITH RESTRICTION

WHAT ARE YOU TRYING TO ACHIEVE?
I’m setting up an experiment in psychopy in which pairs are formed between instruction (‘Visualizar’ or ‘Reavaliar’) and images (60 negative images, 30 neutral images). Note that in total I will have 90 trials, in which each image is shown once and randomly paired with one of the two instructions (‘Visualizar’ or ‘Reavaliar’).
I already have the code for this part and it’s working perfectly.
What i need to do now is to change the code in a way that a neutral image will never be presented with the instruction ‘Reavaliar’. So, everytime a neutral image appears, i want the instruction to be ‘Visualizar’.

Here’s the code i already have:

(Begin experiment tab):

#Instruction Code Part.1:
#Define lists block of instructions and shuffle:
blocklist_inst =
for idx in range(6):
blocklist_inst.append(‘Visualizar’)
for idx in range(3):
blocklist_inst.append(‘Reavaliar’)
shuffle(blocklist_inst)

#Image Code Part.2::
#Define lists of images and shuffle:
useRows =

neutral = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29]
shuffle(neutral)
negative = [30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89]
shuffle(negative)

#Image Code Part.2:
#Pseudorandomize images:
for block in range(10):
blocklist =
for idx in range(3):
blocklist.append(neutral.pop())
for idx in range(6):
blocklist.append(negative.pop())
shuffle(blocklist)
useRows.extend(blocklist)

(Begin routine tab):
#Instruction Code Part.2:
#Pick one instruction from list:
useRows_inst = blocklist_inst.pop()

if len(blocklist_inst) == 0:
for idx in range(6):
blocklist_inst.append(‘Visualizar’)
for idx in range(3):
blocklist_inst.append(‘Reavaliar’)
shuffle(blocklist_inst)

#Save data manually:
thisExp.addData(‘Instruction’, useRows_inst)

WHAT DID YOU TRY TO MAKE IT WORK?
I changed only this part:

#Image Code Part.2:
#Pseudorandomize images:
for block in range(10):
blocklist =
for idx in range(3):
if blocklist_inst[block] == ‘Visualizar’:
blocklist.append(neutral.pop())
else:
blocklist.append(negative.pop())
for idx in range(6):
blocklist.append(negative.pop())
shuffle(blocklist)
useRows.extend(blocklist)

WHAT SPECIFICALLY WENT WRONG WHEN YOU TRIED THAT?:
Index error: pop from empty list.

Try this

#Image Code Part.2:
#Pseudorandomize images:
for block in range(10):
     blocklist = []
     nVis = 0
     for idx in range(9):
     if nVis < 3 and blocklist_inst[block] == ‘Visualizar’:
          blocklist.append(neutral.pop())
          nVis += 1
     else:
          blocklist.append(negative.pop())
     shuffle(blocklist)
     useRows.extend(blocklist)

The runner is showing the same error:

blocklist.append(negative.pop())
IndexError: pop from empty list

Add print('List length', len(negative)) at the start of the loop to see if the length is as you’d expect. It should start at 60. You could also check the length of other lists.

The runner printed this:

List length before loop: 60
Traceback (most recent call last):
List length before loop: 51
List length before loop: 45
List length before loop: 39
List length before loop: 30
List length before loop: 24
List length before loop: 18
List length before loop: 12
List length before loop: 6

Maybe it is happening because the neutral list only has 30 elements, whereas the negative has 60 elements? So when i run more than 30 times, the neutral list gets empty?

But how should i deal with the neutral list? I don’t want to present 60 neutral images in my experiment.

Check the length of this list. I think it has 9 elements, but block takes 10 values.

In 6 blocks you need 3 neutral and 6 negative.

In 3 blocks you need 9 negative.

Total 18 neutral and 63 negative, which is why you are running out of negative.

I think i was not sucessfull in coding what i wanted, then…

I wanted the loop to repeat 10 times (So i would have 10 blocks). Each block would take 9 values: 3 neutral and 6 negative.

3 * 10 = 30
6 * 10 = 60
30 + 60 = 90 images.

But i actually don’t care about the exact numbers. That’s just a way i found to counterbalance the stimuli, so i don’t have a lot of consecutive negative images.

I tried this:

Pseudorandomize images (Blocks of 9 images: 6 negative and 3 neutral. Neutral can’t be presented with ‘Reappraise’).

for block in range(9):
blocklist =
# Determine the current instruction for this block
current_instruction = block_instruction[block]
for idx in range(3):
while current_instruction == ‘Reavaliar’:
print(“Warning: ‘Reavaliar’ presented with neutral image. Retrying…”)
current_instruction = ‘Visualizar’ # Force the instruction to be ‘Visualizar’ when appending neutral images
blocklist.append(neutral.pop())
for idx in range(6):
blocklist.append(negative.pop())
shuffle(blocklist)
useRows.extend(blocklist)

However, even if the warning appears in the out, the error is not corrected, and ‘Reavaliar’ instruction is still shown with neutral images.

Try this:

#Define lists of images and shuffle:
block_instruction = []
conditions = []
useRows = []
trialTypes = [1,1,1,2,2,2,3,3,3]

neutral = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29]
shuffle(neutral)
negative = [30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89]
shuffle(negative)

for Idx in range(10):
     shuffle(trialTypes)
     for Jdx in range(9):
          if trialTypes[Jdx] == 1:
               block_instruction.append('Reavaliar')
               useRows.append(negative.pop())
               conditions.append('Rneg')
          elif trialTypes[Jdx] == 2:
               block_instruction.append('Visualizar')
               useRows.append(negative.pop())
               conditions.append('Vneg')
          else:
               block_instruction.append('Visualizar')
               useRows.append(neutral.pop())
               conditions.append('Vneu')

I used this code, and in the text box of the text component i added “$block_instruction”. However, this makes the text component show all the list of instructions at once. How should i proceed so that the text component takes only 1 element from the block_instruction list?

I tried so many solutions, but none of them worked!

I tried, for example, putting this at the begin routine tab:

#Add this line at the beginning to initialize the index
current_instruction_index = 0

#Inside the loop where you are displaying instructions
block_instruction[current_instruction_index]

#Increment the index for the next iteration or display
current_instruction_index += 1

And i set the text component as “$block_instruction[current_instruction_index]”, but this way only 1 instruction is chosen and this one instruction is the one shown every trial.

There are two ways to do this:

  1. Use thisInstruction = block_instruction.pop() to place the final item into a temporary variable and use that
  2. Use block_instruction[trials.thisN] to select the item based on the index of the loop (if the loop is called trials).

How are you using useRows?

You will also need to save the values in the data file using thisExp.addData(‘Column’,variable)

Where exactly should i insert these lines? Do i need to do anything else, or just add “instruction[trials.thisN]” within each block?

You could set the text component as $instruction[trials.thisN] and then add data via thisExp.addData('instruction',instruction[trials.thisN])

However that will fail if trials isn’t the name of your outer loop with nReps set to 90.

Worked perfectly! Thank you very much!