Python Tutorial: Level 2

Originally created for the OVGU Cognitive Neuroscience Master's course H.3: Projektseminar Fall 2019

Updated August 2020

Taught by Reshanne Reeder

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:

In [2]:
animal='skunk'

Then we can create our "if" statement:

In [3]:
if animal == 'skunk':
    print("skunk1.png")  
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:

In [4]:
if animal == 'skunk'
    print("skunk1.png") 
  File "<ipython-input-4-b8e0208e6afc>", line 1
    if animal == 'skunk'
                        ^
SyntaxError: invalid syntax

-it will point you to where the colon should go. If you forget to indent:

In [5]:
if animal == 'skunk':
print("skunk1.png")
  File "<ipython-input-5-8f70ea078253>", line 2
    print("skunk1.png")
        ^
IndentationError: expected an indented block

-it will give you a more specific error message. If you assign a variable instead of writing a Boolean:

In [6]:
if animal = 'skunk':
    print("skunk1.png")
  File "<ipython-input-6-18a33f630c3e>", line 1
    if animal = 'skunk':
              ^
SyntaxError: invalid syntax

-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:

In [7]:
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:

In [8]:
animal='skunk'
if animal == 'skunk':
    image='skunk.png'
print(image)
skunk.png

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:

In [9]:
animal='bear'
if animal == 'skunk':
    image='skunk.png'
    
elif animal == 'bear':
    image='bear.png'
    
print(image)
bear.png

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:

In [10]:
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!")
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:

In [11]:
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!")
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.

In [12]:
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!")
I don't know what to do with this animal!

Just like any other Boolean, you can combine statements with "or", "and", "and not":

In [13]:
animal='duck'
if animal == 'skunk' or animal == 'duck':
    print("This is either a skunk or a duck")
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:

In [14]:
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")
This animal is a skunk
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:

In [15]:
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)       
skunk.png
bear.png
duck.png

...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:

In [16]:
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)
skunk.png
bear.png
duck.png

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:

In [17]:
animals = ['skunk','bear','duck']

for animal in animals:
    image = animal + '.png' #use a string operation to combine 2 strings
    print(image)
skunk.png
bear.png
duck.png

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:

In [18]:
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) 
Using the word index to loop through the animals
skunk.png
bear.png
duck.png
Using the letter i to loop through the animals
skunk.png
bear.png
duck.png

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:

In [19]:
trials = range(10)

for trial in trials:
    print(trial)
0
1
2
3
4
5
6
7
8
9

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:

In [20]:
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
gopher.png
rabbit.png
bear.png
tiger.png
owl.png

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:

In [21]:
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
gopher.png
tiger.png
bear.png
rabbit.png
owl.png
gopher.png
tiger.png
rabbit.png
bear.png
owl.png
rabbit.png
owl.png
tiger.png
gopher.png
bear.png

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

In [22]:
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
Subject 1
Shuffling animals for subject 1
Printing owl for subject 1
owl.png
Printing tiger for subject 1
tiger.png
Printing rabbit for subject 1
rabbit.png
Printing bear for subject 1
bear.png
Printing gopher for subject 1
gopher.png
Subject 2
Shuffling animals for subject 2
Printing gopher for subject 2
gopher.png
Printing bear for subject 2
bear.png
Printing owl for subject 2
owl.png
Printing rabbit for subject 2
rabbit.png
Printing tiger for subject 2
tiger.png
Subject 3
Shuffling animals for subject 3
Printing bear for subject 3
bear.png
Printing tiger for subject 3
tiger.png
Printing rabbit for subject 3
rabbit.png
Printing gopher for subject 3
gopher.png
Printing owl for subject 3
owl.png

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:

In [23]:
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)
tiger.png
This animal has an index of 0
rabbit.png
This animal has an index of 1
gopher.png
This animal has an index of 2
bear.png
This animal has an index of 3
owl.png
This animal has an index of 4

If you want to make sure your counter is working correctly, you can also use "count" to print the animal at each index:

In [24]:
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])
tiger.png
This animal is tiger
rabbit.png
This animal is rabbit
gopher.png
This animal is gopher
bear.png
This animal is bear
owl.png
This animal is owl

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:

In [25]:
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
Showing an image
0
Showing an image
1
Showing an image
2
Showing an image
3
Showing an image
4

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:

In [26]:
import random #import the randomization library
print(random.randint(0,10)) #print a random integer between 0 and 10
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:

In [27]:
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
Showing an image for 1 iterations
Showing an image for 2 iterations
Showing an image for 3 iterations
Showing an image for 4 iterations
Showing an image for 5 iterations
Showing an image for 6 iterations
Showing an image for 7 iterations
Showing an image for 8 iterations
Showing an image for 9 iterations
Showing an image for 10 iterations
Showing an image for 11 iterations
Showing an image for 12 iterations
Showing an image for 13 iterations
Showing an image for 14 iterations
Showing an image for 15 iterations
Showing an image for 16 iterations
Showing an image for 17 iterations
Showing an image for 18 iterations
Showing an image for 19 iterations
Showing an image for 20 iterations
Showing an image for 21 iterations
Showing an image for 22 iterations
Showing an image for 23 iterations
Showing an image for 24 iterations
Showing an image for 25 iterations
Showing an image for 26 iterations
Showing an image for 27 iterations
Showing an image for 28 iterations
Showing an image for 29 iterations
Showing an image for 30 iterations
Showing an image for 31 iterations
Showing an image for 32 iterations
Showing an image for 33 iterations
Showing an image for 34 iterations

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:

In [28]:
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
Showing an image for 1 iterations
Showing an image for 2 iterations
Showing an image for 3 iterations
Showing an image for 4 iterations
Showing an image for 5 iterations
Showing an image for 6 iterations
Showing an image for 7 iterations
Showing an image for 8 iterations
Showing an image for 9 iterations
Showing an image for 10 iterations
Showing an image for 11 iterations
Showing an image for 12 iterations
Showing an image for 13 iterations
Showing an image for 14 iterations
Showing an image for 15 iterations
Showing an image for 16 iterations
Showing an image for 17 iterations
Showing an image for 18 iterations
Showing an image for 19 iterations
Showing an image for 20 iterations

This will terminate the loop after 20 iterations if the subject has not responded.