BACK TO Level 0: A New Language
BACK TO Level 1: Manipulating Variables
ONWARD TO Level 3: PsychoPy 101
ONWARD TO Level 4: PsychoPy - Showing windows and stimuli
ONWARD TO Level 5: PsychoPy - Clocks and timing
ONWARD TO Level 6: PsychoPy - Response collection and saving data
ONWARD TO Advanced: Miscellanious tricks for improving your code
A conditional statement can be written with if. In an experiment, you often want to tell your script "If we are on trial 1, present X information", "If the subject number is odd, do Y", or "If the subject pressed this button, record that response". There are many different reasons for using "if" statements. Here is a concrete example: say you have different animal images you want to show in your experiment. You want to tell your experiment if the animal is "skunk", only present images from the skunk directory. First, we have to define our animal variable:
animal='skunk'
Then we can create our "if" statement:
if animal == 'skunk':
print("skunk1.png")
"if" statements need a certain syntax - first the word "if", followed by a Boolean (animal is equal to 'skunk'?), and then a colon : The colon demarcates the end of the "if" statement. If the statement is true (yes, animal is equal to 'skunk'), your script will run whatever code is within the "if" statement (for lines of code to be counted within the statement, they must be indented).
If you write the syntax wrong, you will get various errors. For example, if you forget the colon:
if animal == 'skunk'
print("skunk1.png")
-it will point you to where the colon should go. If you forget to indent:
if animal == 'skunk':
print("skunk1.png")
-it will give you a more specific error message. If you assign a variable instead of writing a Boolean:
if animal = 'skunk':
print("skunk1.png")
-it will point you to where the extra "=" should go.
If the animal is not "skunk", it won't perform the commands within the skunk-specific "if" statement:
animal='bear'
if animal == 'skunk':
print("skunk1.png")
The image name didn't print because the animal is now "bear", and we didn't tell the script to do anything if the animal is "bear". You can assign variables within an "if" statement:
animal='skunk'
if animal == 'skunk':
image='skunk.png'
print(image)
Defining variables within an "if" statement is handy for re-assigning variables on an animal-by-animal basis. Say you want the "image" variable to change depending on which animal you want to show:
animal='bear'
if animal == 'skunk':
image='skunk.png'
elif animal == 'bear':
image='bear.png'
print(image)
Here we use a second "if" statement, "elif" (else if). This is written at the same indentation level as the first "if" statement (we don't want the "elif" statement to be dependent on the animal being a skunk). This group of statements says: If the animal is a skunk, assign the skunk image to the variable "image". Else, if the animal is a bear, assign the bear image to the variable "image". Finally, print the variable "image" to show how it has been reassigned.
We can also use the "else" statement, which is less specific than "elif". For example:
animal='duck'
if animal == 'skunk':
image='skunk.png'
print(image)
elif animal == 'bear':
image='bear.png'
print(image)
else: print("I don't know what to do with this animal!")
You could enter anything into the "animal" variable and get the output from this "else" statement:
animal='lion'
if animal == 'skunk':
image='skunk.png'
print(image)
elif animal == 'bear':
image='bear.png'
print(image)
else: print("I don't know what to do with this animal!")
What "else" does, is it runs some bit of code you tell it to, if none of the conditions in the "if" statements are met. The animal is not skunk, nor is it bear, it is something else. You would typically add an "else" statement with some kind of error message, so you know if you accidentally forgot about a condition or mislabeled something.
animal='beer'
if animal == 'skunk':
image='skunk.png'
print(image)
elif animal == 'bear':
image='bear.png'
print(image)
else: print("I don't know what to do with this animal!")
Just like any other Boolean, you can combine statements with "or", "and", "and not":
animal='duck'
if animal == 'skunk' or animal == 'duck':
print("This is either a skunk or a duck")
You can also "nest" statements so that certain "if" statements are only interpreted if the first "if" statement is true:
animal = 'skunk'
color = 'brown'
if animal == 'duck':
print("This animal is a duck")
if color == 'brown':
print("This is a brown duck")
elif animal == 'skunk':
print("This animal is a skunk")
if color == 'brown':
print("This skunk has been rolling in the mud")
Nested "if" statements require the same syntax as other "if" statements, except they must be indented to indicate that they should be interpreted within the correct statement.
In an experiment, you want to be able to run the same bit of code over and over (for a number of trials, subjects, etc.). In this case, it's important to know how to use the for loop, which allows you to "loop" back around at the end of a bit of code and run it again however many times you specify. Take the animal example -- if you have three animals you want to show in your experiment, you could write:
animal='skunk'
if animal == 'skunk':
image='skunk.png'
print(image)
elif animal == 'bear':
image='bear.png'
print(image)
elif animal == 'duck':
image='duck.png'
print(image)
animal='bear'
if animal == 'skunk':
image='skunk.png'
print(image)
elif animal == 'bear':
image='bear.png'
print(image)
elif animal == 'duck':
image='duck.png'
print(image)
animal='duck'
if animal == 'skunk':
image='skunk.png'
print(image)
elif animal == 'bear':
image='bear.png'
print(image)
elif animal == 'duck':
image='duck.png'
print(image)
...and you get the three image outputs at the end. But that is a LOT of repeated code for just three lines of output! The for loop can allow you to loop through the different animals specified on just 1 line, to run the same bit of code (the "if" statements) three times. Like this:
animals = ['skunk','bear','duck']
for animal in animals:
if animal == 'skunk':
image='skunk.png'
elif animal == 'bear':
image='bear.png'
elif animal == 'duck':
image='duck.png'
print(image)
This is still a lot of repeated code. As you get more comfortable with using for loops, you will find ways to compress the code even more. Like:
animals = ['skunk','bear','duck']
for animal in animals:
image = animal + '.png' #use a string operation to combine 2 strings
print(image)
Here we created a list called "animals" outside of the loop. You can loop through each item in the list by using the syntax above: "for animal in animals". You create a placedholder label for each word indexed in the list within the for loop with "animal". You could also use another indexing label, such as "index" or "i". It works the same way:
print("Using the word index to loop through the animals")
for index in animals:
image = index + '.png'
print(image)
print("Using the letter i to loop through the animals")
for i in animals:
image = i + '.png'
print(image)
The important thing is that you have to refer to the actual name of the list (animals), but the index label can be whatever you want. "animal" is a good index label because it is easy to remember what is contained in the list in this example. Let's try another example that is more relevant for experiments:
trials = range(10)
for trial in trials:
print(trial)
Typically in a trial loop within an experiment, you will want to show text or images on each trial. We will get to actually showing images in a trial loop in level3. Until then, we will simply print the name of the picture we want to show on each trial:
import numpy as np #import numpy library
animals = ['tiger','rabbit','gopher','bear','owl']
np.random.shuffle(animals) #shuffle the order of images shown
for animal in animals: #cycle through the shuffled list
image = animal + '.png'
print(image) #in an actual experiment, we would replace the print function with a "show image" function
Furthermore, we usually want to shuffle the order of images for each subject in the experiment. We can do this with a nested for loop - one looping over subjects and one looping over images:
animals = ['tiger','rabbit','gopher','bear','owl']
subs = [1,2,3]
for sub in subs: #loops through subjects
np.random.shuffle(animals) #shuffle the order of images shown for each subject
for animal in animals: #cycle through the shuffled list
image = animal + '.png'
print(image) #in an actual experiment, we would replace the print function with a "show image" function
You can see that the list of animals is shuffled for each subject. If you are unsure about what your code is doing, you can add some notes to yourself to make it easier to interpret the output. Here, you can practice printing looping integers and strings with %i and %s placeholders
animals = ['tiger','rabbit','gopher','bear','owl']
subs = [1,2,3]
for sub in subs: #loops through subjects
print("Subject %i" %sub) #insert subject number on this loop using %i
np.random.shuffle(animals) #shuffle the order of images shown for each subject
print("Shuffling animals for subject %i" %sub)
for animal in animals: #cycle through the shuffled list
print("Printing %s for subject %i" %(animal, sub)) #print the string on this loop with %s
image = animal + '.png'
print(image) #in an actual experiment, we would replace the print function with a "show image" function
If you would rather know the index value (0,1,2, etc.) instead of the name of the animal, you can add an index counter like this:
animals = ['tiger','rabbit','gopher','bear','owl']
count = -1 #start at -1 to start indexing at 0 within the loop
for animal in animals:
count = count+1 #every time the loop starts over, add 1 to the count
image = animal + '.png'
print(image) #in an actual experiment, we would replace the print function with a "show image" function
print("This animal has an index of %i" %count)
If you want to make sure your counter is working correctly, you can also use "count" to print the animal at each index:
animals = ['tiger','rabbit','gopher','bear','owl']
count = -1 #start at -1 to start indexing at 0 within the loop
for animal in animals:
count = count+1 #every time the loop starts over, add 1 to the count
image = animal + '.png'
print(image) #in an actual experiment, we would replace the print function with a "show image" function
print("This animal is %s" %animals[count])
And that is how you can using indexing in a for loop.
To use a for loop, you have to specify the number of times the loop should iterate. Sometimes you want to loop indefinitely until a particular state or value is met. For example, in an experiment, you might want to tell the experiment to show an image for a certain amount of time. You can specify an amount of time (in seconds) to show the image (e.g., "present this image for 5 seconds"). You have to tell the experiment, "Once 2 seconds have passed, stop showing the image". To do this, you can use a while loop. Here is a simple example to demonstrate how it works:
time_counter = 0 #pretend we are counting up from 0 seconds (the beginning of the experiment)
while time_counter < 5: #do something before the counter reaches 5
print("Showing an image")
print(time_counter) #what is the time on the counter?
time_counter = time_counter +1 #while the loop is still running, add 1 to the counter at the end of the iteration
While loops use conditionals ("while a certain condition is met, do X"), so the statement requires a boolean of some kind. Your while loop can be any kind of truth statement: "While X is less than Y", "While X == Y", "While True", etc. Sometimes you want to show an image in an experiment until the participant makes a response. You don't know exactly when that will be, so you can use a while loop for that. So to create a simulation of response collection (we will learn how to collect actual responses online in a later level), we will use a random number generator to simulate when the subject decides to make a response:
import random #import the randomization library
print(random.randint(0,10)) #print a random integer between 0 and 10
This function outputs a random integer between 0 and 10. Let's say that if the function randomly generates a "5", the subject has made a response (just like we don't know when a subject will make a response, we don't know when the number generator will generate a "5"). So let's add that to a while loop:
import random #import the randomization library
response = False #subject has not made a response yet
iteration = 0 #add an iteration counter
while not response: #while the subject has not responded (also can be written as "while response==False")
iteration = iteration +1
print("Showing an image for %i iterations" %iteration)
if random.randint(0,10) == 5: #if the generator generates a 5
response=True #subject has made a response
If you run this several times, you can see that you get a different number of iterations each time. It is dependent on when the random number generator picks a 5. In the same way you can make the duration of your loop dependent on when a subject makes a response, and it will keep looping indefinitely until the condition is met.
Sometimes when you run this code, the list of iterations is very long. A while loop will loop indefinitely until a condition is met. If you are not careful, you could accidentally create a loop that never ends. This can even crash your computer. You can implement some failsafes so the loop terminates at some point even if the condition is never met. For example:
import random #import the randomization library
response = False #subject has not made a response yet
iteration = 0 #add an iteration counter
failsafe = -1 #add a failsafe counter so if the subject never responds, terminate the loop anyway
while not response: #while the subject has not responded (also can be written as "while response==False")
failsafe = failsafe+1 #every time the loop starts over, add 1 to the counter
if failsafe == 20: #after 20 iterations, if the subject still has not responsed
break #break the loop
iteration = iteration +1
print("Showing an image for %i iterations" %iteration)
if random.randint(0,10) == 11: #if the generator generates 11 (this will never happen)
response=True #subject has made a response
This will terminate the loop after 20 iterations if the subject has not responded.
ONWARD TO Level 3: PsychoPy 101