psychopy.org | Reference | Downloads | Github

Sequential and Random Selection at the same time

Hello everyone,

I am trying to build a psychopy experiment on my Macbook, in which image types will be presented in a fixed order such as:

Intact, Intact, Scrambled…

and for example, within the stimuli type of Intact, there will be 100 images to randomly select from, and in the second Intact stimuli, there will now be 99 images to randomly select from (no image will be repeated), and then in the Scrambled image type, there will be 25 images to select from…

I had completed this on Eprime, in which basically there was a fixed sequential order for type of stimuli, but a random order within those stimuli.

Could anyone help me figure this out in Psychopy please?

Thanks!

Hi, to do this in psychoPy, you generally want to use two loops: an inner loop to control the trials (which will be randomised) and an outer loop to control the blocks (which will be sequential). The technique is described here:

https://www.psychopy.org/builder/blocksCounterbalance.html

In essence, the outer loop needs a conditions file that contains just three rows, each of which will point to the conditions file to be used for the inner loop for each block of trials, e.g.

stimulus_type    stimulus_file
Intact           intact_01.csv
Intact           intact_02.csv
Scrambled        scrambled.csv

i.e. this outer loop cycles through these three conditions sequentially, and the inner loop simply uses the variable $stimulus_file as the name of its conditions file.

That way the first block will have 100 randomised trials, the next 99 randomised trials, and the last, 25 randomised trials.

Hey Michael,

Thank you for the reply.
So would this outer loop just randomly pick an image out of the 100 images within Intact? How would I ensure that the image randomly selected the first time isn’t the same image randomly selected the second time?

The outer loop doesn’t know anything about your images at all. The only variables it knows about are the names of the three conditions files to be used by the inner loop.

Perhaps I don’t quite understand your design?

So basically the order is going to be:

Intact, Intact, Scrambled, Intact, Intact, Scrambled… and so on until it shows about 100 total images.

Everytime an Intact type is shown, it will randomly select out of 80 intact images and everytime a scrambled type is shown, it randomly selects out of 20 scrambled images. However, it can’t pick the same image twice.

Does that make sense?

I get that there needs to be two loops, but how would I structure the excel file for the sequential loop in that it selects images randomly but avoids images that have already been previously selected?

I read your original post as indicating that there were successive blocks of trials of the same type, rather than a single block of trials of interleaved trial types. A double-loop arrangement won’t help with this. What is probably easiest is a single loop, and with the conditions and image names determined in code rather than in a conditions file.

Insert a code component (from the “custom” component panel) and put something like this in the “begin experiment” tab, to create lists of image names (like intact_24.jpg etc):

# create image lists:
intact_images = [f'intact_image_{i}.jpg' for i in range(100)]
scrambled_images = [f'scrambled_image_{i}.jpg' for i in range(25)]

# randomise their order:
shuffle(intact_images)
shuffle(scrambled_images)

Then in the “Begin routine” tab, put something like this, to figure out what condition this trial is, and to sample an image name without replacement from the appropriate list:

# figure out the condition to use, based on every third trial 
# being a "scrambled" one. calculate by checking that the 
# remainder is 0 when diving by 3:
if (the_name_of_your_loop.thisN + 1) % 3 == 0: 
    # trial is a multiple of three, so:
    image_type = 'scrambled'
    image_file = scrambled_images.pop()
else:
    image_type = 'intact'
    image_file = intact_images.pop()

# record these in the data:
thisExp.addData('image_type', image_type)
thisExp.addData('image_file', image_file)

Now just put $image_file in the “image” field of your image stimulus component, set to update every repeat. Make sure that the code component is above the image stimulus component, so that this variable is updated in time for the image stimulus to refer to it.

Note that the loop will only run 75 times before an error occurs, as the scrambled_images list will be emptied at that point, but the numbers you give in your posts above jump around a bit.

Thank you Michael, as always.
I will try this out and let you know.

It’s not that the ‘scrambled’ image will appear every third trial, as I was just using it as an example. The actual test will be more like, for example:
I, I, S, I, I, I, I, I, S, I, I, S, I, I, I, I, S

There is a fixed order to the stimuli type, but it’s not every third trial.

Hey Michael,
Any suggestions on how to do it in this manner rather than every third stimuli type?

Hey Evi, Andrew here!

Does this help? Try replacing the code in the “begin routine” tab with this. You can adjust when the scrambled images appear in the sequence by changing the numbers in the first line of the “if” statement.


# Scrambled images are to appear at fixed positions in a 
# sequence of stimuli.
If (the_name_of_your_loop.thisN + 1) == (3 or 9 or 12 or 17):
	image_type = 'scrambled'
	image_file = scrambled_images.pop()

else:
	image_type = 'intact'
	image_file = intact_images.pop() 

# record these in the data:
thisExp.addData('image_type', image_type)
thisExp.addData('image_file', image_file)

Hi, has been a long weekend here in NZ (for Waitangi Day) and I have not been checking this board…

Andrew is on the right track but that syntax won’t quite work. To get multiple or comparisons to work would have to be something a bit clumsy like this:

if (the_name_of_your_loop.thisN + 1) == 3 or (the_name_of_your_loop.thisN + 1) == 9 or … # etc

The more concise approach would be to use in instead:

if (the_name_of_your_loop.thisN + 1) in [3, 9, 12, 17]:

But perhaps the best alternative would be to control the trial type from a conditions file, which you step through sequentially (to maintain your fixed order). Then you wouldn’t have to muck around with trial numbers at all. In that case, it would simply be:

if your_image_type_column_name == 'S': 
    image_file = scrambled_images.pop()
else:
    image_file = intact_images.pop()

thisExp.addData('image_file', image_file)

Hey Michael,

Thanks for the reply. I hope your weekend was wonderful.

So would the .pop() function ensure that once an image is presented once in the routine, it won’t be presented again?

Thank you.

Yes, those lists are still being created in code, separate from the conditions file (i.e. in the code in the “Begin experiment” tab).

What the pop() method of a list does is pull out the next entry from a list, shrinking that list in size by one entry. So that entry can’t be selected again, because the list no longer contains it.

I am getting an error message in the code; I think I’m messing something up here…

It says name error, name ‘Users’ is not defined.

In Python, the .pop() method only applies to a Python list object - you are trying to pop from a file. Python simply won’t know what to do with that.

In this post, we created the two lists you need (intact_images and scrambled_images):

Your files will likely have a different naming convention of course. Amend that code to match the way your files are actually stored, including if they are in a subfolder. The image file names don’t need to have a full absolute path starting at /Users/. If they are in a subfolder of the same folder as where your experiment is, they can be relative paths that start there. e.g.

intact_images = [f'my_image_subfolder_name/Wow_my_INTACT_pics_{i}.jpg' for i in range(100)]

You need to tweak that code to fit - e.g. reduce the number 100 to match however many images you actually have.

Ok I see. Thank you so much for everything.
I think I’m just about done, but one tiny thing is that my images are named so for example ‘avacado.jpg’ or ‘brush.jpg’ rather than numbered. How do I account for that in the code, because I think it is looking through numbers from 1-100 in the images file name. Or is it better to rename these object files from 1 to 100.

You can use the actual names of your files, but it will be a bit laborious, e.g.

intact_images = ['avocado.jpg', 'brush.jpg', 'blah blah.jpg',] # etc

scrambled_images = ['cat.jpg', 'dog.jpg', 'etc.jpg',] # etc

# or within a sub-folder:

intact_images = ['images/avocado.jpg', 'images/brush.jpg', 'images/blah blah.jpg',] # etc

Michael, thank you so much for your help and patience. I just got it to work!

Great. Good luck with your experiment.