psychopy.org | Reference | Downloads | Github

Select pictures pseudorandomly

Dear all,

I am using PsychoPy3 in Mojave. I am trying to build an experiment, in which participants are asked 4 questions about a picture of a material sample. There are 71 material samples that consist of 2 groups (1 with 56 pictures, 1 with 15 pictures). The 2 groups in turn consist of 8 subsets(each contains 7 pictures) and 2 subsets(1 subset with 8 and 1 with 7 samples) respectively.

I want my experimental loop to select either of the 2 groups first. Then pseudorandomly select a picture from 1 of the 8 or 2 subsets and display the same picture for four questions. So far I have managed to randomly select a group (far outer loop, SelectGroup, in screenshot), then randomly select a picture (middle loop, AllImages, in screenshot) and ask 4 questions (inner loop, Q1toQ4). However, I am struggling to select the images pseudorandomly. That is, select an image from one subset (1, for example) and select the subsequent image from a different subset (2 to 8, for example).

What did you try to make it work?: I have tried to insert a code component (Blank1 in screenshot) following this post (Randomising types of trials so the same stimuli do not repeat one after the other - #2 by jonathan.kominsky). I first tried to input my excel file, but got the error: ‘trialList=data.importConditions(‘SelectImage.xlsx’, selection=Setno),
NameError: name ‘Setno’ is not defined’.
When I tried to manually define a variable equivalent to ‘Setno’, I got the following error: ‘subOrder.append([j,datStructure[j].pop()])
IndexError: pop from empty list’.

I used the following code:
Under ‘Begin Experiment’
‘’'import pandas as pd
‘’'dat = pd.read_excel("./SelectImage.xlsx")

Under ‘Begin Routine’:
‘’‘datStructure = [[1,2,3,4,5,6,7],[1,2,3,4,5,6,7],[1,2,3,4,5,6,7],[1,2,3,4,5,6,7],[1,2,3,4,5,6,7], ‘’’[1,2,3,4,5,6,7],[1,2,3,4,5,6,7],[1,2,3,4,5,6,7],[1,2,3,4,5,6,7,8],[1,2,3,4,5,6,7]]
‘’'blockOrder =
‘’'for i in range(0,10):
‘’'subOrder =
‘’'for j in range(0,len(datStructure)):
‘’'shuffle(datStructure[j])
‘’'subOrder.append([j,datStructure[j].pop()])
‘’'shuffle(subOrder)
‘’'if len(blockOrder) > 0:
‘’'while subOrder[0][0] == blockOrder[-1][0]:
‘’'shuffle(subOrder)
‘’'for k in range(0,len(subOrder)):
‘’'blockOrder.append(subOrder[k])
‘’'trailList = blockOrder

What specifically went wrong when you tried that?: It seems that in both cases the code I tried didn’t work, because it somehow fails to read in the data I need in the first place. I am not sure what goes wrong or whether my approach is correct.

I have been building with PsychoPy for just under 1 week, so I am a newby. Although I can do a little bit of matlab the code components and Python are new to me.

Let me know if you have any suggestions.

Thank you in advance.

Ellen

Screenshot 2020-07-08 at 20.10.33

Dear Michael,

I think the code should be visible properly now.
Sorry, for not putting in the code the right way, it was my first post, so I am still getting used to the system.

Under ‘Begin Experiment’

import pandas as pd
dat = pd.read_excel("./SelectImage.xlsx")

Under ‘Begin Routine’:

datStructure = [[1,2,3,4,5,6,7],[1,2,3,4,5,6,7],[1,2,3,4,5,6,7],[1,2,3,4,5,6,7],[1,2,3,4,5,6,7], [1,2,3,4,5,6,7],[1,2,3,4,5,6,7],[1,2,3,4,5,6,7],[1,2,3,4,5,6,7,8],[1,2,3,4,5,6,7]]
blockOrder = []
for i in range(0,10):
    subOrder = []
    for j in range(0,len(datStructure)):
        shuffle(datStructure[j])
        subOrder.append([j,datStructure[j].pop()])
    shuffle(subOrder)
    if len(blockOrder) > 0:
        while subOrder[0][0] == blockOrder[-1][0]:
            shuffle(subOrder)
    for k in range(0,len(subOrder)):
        blockOrder.append(subOrder[k])
trailList = blockOrder

Thanks in advance.

Kind regards,

Ellen

The basic issue is that there are only 7 items in each of the arrays of datStructure, and you are trying to execute this loop 10 times. When you call “pop”, you are removing one of the items from each of those sub-arrays, so by the time i=7, those arrays are empty.

You can see this for yourself if you modify your code as follows:

datStructure = [[1,2,3,4,5,6,7],[1,2,3,4,5,6,7],[1,2,3,4,5,6,7],[1,2,3,4,5,6,7],[1,2,3,4,5,6,7], [1,2,3,4,5,6,7],[1,2,3,4,5,6,7],[1,2,3,4,5,6,7],[1,2,3,4,5,6,7,8],[1,2,3,4,5,6,7]]
blockOrder = []
for i in range(0,10):
    print(datStructure)
    subOrder = []
    for j in range(0,len(datStructure)):
        shuffle(datStructure[j])
        subOrder.append([j,datStructure[j].pop()])
    shuffle(subOrder)
    if len(blockOrder) > 0:
        while subOrder[0][0] == blockOrder[-1][0]:
            shuffle(subOrder)
    for k in range(0,len(subOrder)):
        blockOrder.append(subOrder[k])
trailList = blockOrder

The added “print” line will show you what’s left in datStructure after every loop, and you will see that right before the error happens, it will print:

[[],[],[],[],[],[],[],[],[n],[]]

(n is whatever value is left in the one list that has 8 items rather than 7)

I’m not entirely sure what you’re trying to achieve, but you either need to refresh datStructure, loop no more times than there are entries in each sub-section of datStructure, or don’t use “pop” and do this as random with replacement rather than without.

Dear Jonathan,

Thank you for your effort and your time. In the meantime, I have solved the problem with the piece of code below. This generates a csv-file, in which the items of each set are shuffled so that an item from set 1 is followed by any item from set 2 to 8. The csv-file is generated at the very beginning of the experiment and used as a conditionsfile in the AllImages loop of my experiment.

Kind regards,

Ellen

# -*- coding: utf-8 -*-
import random
import csv
import codecs

# Preallocate list
Mat = []

# Read in data and assign to list
dat = open("SelectImage.csv","r",encoding = "utf-8")
reader = csv.reader(dat, delimiter=",")
for row in reader:
    Mat.append(row)
dat.close()

# Create 8 set lists
stimList1 = Mat[1:8]
stimList2 = Mat[8:15]
stimList3 = Mat[15:22]
stimList4 = Mat[22:29]
stimList5 = Mat[29:36]
stimList6 = Mat[36:43]
stimList7 = Mat[43:50]
stimList8 = Mat[50:57]

# Create a dictionary that pairs a set with a 'key'
dictLists1 = {'l1':stimList1, 'l2':stimList2,'l3':stimList3,'l4':stimList4,'l5':stimList5,'l6':stimList6,'l7':stimList7,'l8':stimList8}
keyList1 = list(['l1','l2','l3','l4','l5','l6','l7','l8']*len(stimList1))

# Shuffle set lists
random.shuffle(stimList1)
random.shuffle(stimList2)
random.shuffle(stimList3)
random.shuffle(stimList4)
random.shuffle(stimList5)
random.shuffle(stimList6)
random.shuffle(stimList7)
random.shuffle(stimList8)

# Shuffle dictionary keys
random.shuffle(keyList1)

# Loop through keyList and join elements in a new list, such that 2 consecutive keys cannot be the same
orderTest = False
while orderTest == False:
    for i in range(len(keyList1)):
        if ''.join(keyList1).count('l1'*2) > 0 or ''.join(keyList1).count('l2'*2)>0 or ''.join(keyList1).count('l3'*2)>0 or ''.join(keyList1).count('l4'*2)>0 or ''.join(keyList1).count('l5'*2)>0 or ''.join(keyList1).count('l6'*2)>0 or ''.join(keyList1).count('l7'*2)>0 or ''.join(keyList1).count('l8'*2)>0:
            random.shuffle(keyList1)
            break
        else:
            orderTest = True

# Preallocate trialList1
trialList1 = []

# Append a part of the dictionary to its corresponding key 
for thisKey in keyList1:
    trialList1.append(dictLists1[thisKey].pop())

# Preallocate headers
fieldnames = ['Setno', 'Sampleno', 'Select', 'Select1', 'Select2', 'Select3', 'Select4']

# Open a csv-file in write mode
trialFile1 = open("trialList1.csv", "w", newline = '')
#Create writeable object
writer = csv.writer(trialFile1)
#Write header to csv
writer.writerow(fieldnames)
#Write rows trialList1 to csv
for row in trialList1:
    writer.writerow(row)
#Close csv
trialFile1.close()

# Create 2 set lists
stimList9 = Mat[57:64]
stimList10 = Mat[64:72]

# Create a dictionary that pairs a set with a 'key'
dictLists2 = {'l9':stimList9,'l10':stimList10}
keyList2 = list(['l9', 'l10']*len(stimList9))
#keyList2 should only contain elements up until 15 to correspond with number of items in both sets
keyList2 = keyList2[:15]

# Shuffle set lists
random.shuffle(stimList9)
random.shuffle(stimList10)

# Shuffle dictionary keys
random.shuffle(keyList2)

# Loop through keyList and join elements in a new list, such that 2 consecutive keys cannot be the same
orderTest = False
while orderTest == False:
    for j in range(len(keyList2)):
        if ''.join(keyList2).count('l9'*2)>0 or ''.join(keyList2).count('l10'*2)>0:
            random.shuffle(keyList2)
            break
        else:
            orderTest = True

# Preallocate trialList2
trialList2 = []

# Append a part of the dictionary to its corresponding key 
for thisKey in keyList2:
    trialList2.append(dictLists2[thisKey].pop())

# Open a csv-file in write mode
trialFile2 = open("trialList2.csv","w")
#Create writeable object
writer = csv.writer(trialFile2)
#Write rows trialList2 to csv
for row2 in trialList2:
    writer.writerow(row2)
#Close csv
trialFile2.close()

# Open the 2 previously created csv's in read mode
t1 = open("trialList1.csv","r",encoding = "utf-8")
t2 = open("trialList2.csv","r",encoding = "utf-8")
#Create 2 readable objects
reader1 = csv.reader(t1,delimiter=";")
reader2 = csv.reader(t2,delimiter=";")
#Open a new csv in write mode
f = open("trialList.csv","w")
#Create writeable object
writer = csv.writer(f,delimiter = ";")
#Write rows of readable objects to writeable object
for row3 in reader1:
    print(row3)
    writer.writerow(row3)
for row3 in reader2:
    writer.writerow(row3)
#Close csv. It can now be used as conditions file in loop AllImages
f.close()