\(\newcommand{L}[1]{\| #1 \|}\newcommand{VL}[1]{\L{ \vec{#1} }}\newcommand{R}[1]{\operatorname{Re}\,(#1)}\newcommand{I}[1]{\operatorname{Im}\, (#1)}\)

Conditionals, functions and loops

In this Notebook, we work out how to simulate a coin toss in Python. Then we wrap up the coin toss logic into our own function. Lastly, we work out how to do many coin tosses and collect the result.

Conditionals

We want our Python code to do the equivalent of a coin toss. We want to run the code, and get a random answer, that is 1 50% of the time (representing a Head), and 0 50% of the time (representing a Tail).

Let’s start by getting a random number that can be any value, with equal likelihood, all the way between 0 and 1. We think first of the random module:

>>> import random

We can explore the functions inside the random module by tab completion (see Exercise). We can also look at the documentation for the random module. We discover that the random function in the random module does what we want:

>>> # Get a random number between 0 and 1
>>> random.random()
0.889900600711181
>>> # Get another random number between 0 and 1
>>> random.random()
0.9602318110750124

To do our coin toss, we can look at the random number. If it is < 0.5, we can decide this is a 0 (for Tails), and if it is >= 0.5, we will say it is a 1 (for Heads). This is a “if, then” type statement, called a conditional. It’s called a conditional because we do something only if a condition is met. The condition here is that the random number is < 0.5. A conditional in Python looks like this:

>>> random_no = random.random()
>>> # Next we see the condition that is being tested
>>> if random_no < 0.5:
...     # Tails
...     our_result = 0
... else:
...     # Heads
...     our_result = 1

There are a couple of important things to notice here. The conditional starts with if, followed by the condition, here random_no < 0.5. There must be a colon (:) at the end of the line, and this signals the start of the statements that should run if the condition is True.

The statement to execute if the condition is True, is our_result = 0. Notice that this statement is indented by four spaces. It is this indentation that tells Python that the statement should run only if the condition is True. You can put many statements to run if the condition is True, but they must all be indented in the same way. Luckily the Notebook and other code editors usually guess what we want and do the indentation for us.

Next we see a line else:. Notice that this line is not indented. That is because we want this line to run if the condition is False. Remember, the indented lines above run only when the condition is True.

Notice again that the else: has a colon at the end, to say that more indented lines will follow. These lines only run if the condition is False.

To recap, the indented lines after the if and before the else run only if the condition is True. The indented lines after the else: only run if the condition is False.

You don’t have to have an else: part of a conditional block - you can just have the if part. For example, this code would do the same as the code above:

>>> random_no = random.random()
>>> # Default to Heads
>>> our_result = 1
>>> # We have the "if" part, but no "else:" part.
>>> if random_no < 0.5:
...     # Tails
...     our_result = 0

Functions

In Introduction to the Jupyter Notebook, we saw functions in action, such as len and max.

These are functions that Python provides for us.

We can also write our own functions.

We’re going to wrap up our logic for the coin toss into a function, and call the function coin_toss. Like random.random our function coin_toss will accept no arguments. It will return a 0 50% of the time and a 1 50% of the time.

We make a function like this:

>>> def coin_toss():
...     random_no = random.random()
...     if random_no < 0.5:
...         our_result = 0
...     else:
...         our_result = 1
...     return our_result

The function starts with the word def which tells Python we are defining a function. Next follows the name of the function, followed by, in our case, parentheses. You specify what arguments the function should have, in the parentheses. In our case, our function as no arguments, so there is nothing between the parentheses.

Next there follows a series of statements that are indented. These statements are the body of the function. They are the code that gets run when the function gets called.

We return the result with the return statement. Here we are returning a 0 if a random number was < 0.5, and a 1 otherwise.

Let’s call the function a few times to see if the results are plausible:

>>> # Notice, we pass no arguments
>>> coin_toss()
0
>>> coin_toss()
1
>>> coin_toss()
0

Exercise

Up until now, we’ve assumed that the chance that a child is a boy is 0.5. Now assume the proportion of boys born in the UK is 0.513 [1].

Here is a new copy of the coin_toss function, but renamed. Like the coin_toss function, it returns 0 50% of the time, and 1 the rest of the time. Modify the function to return 0 (for a boy) 51.3% of the time, and 1 (for a girl) 48.7% of the time:

>>> def girl_or_boy():
...     # Return 1 for a girl, 0 for a boy
...     random_no = random.random()
...     if random_no < 0.5:
...         our_result = 0
...     else:
...         our_result = 1
...     return our_result

You can try your function out a few times with the cell below:

>>> girl_or_boy()
0

Loops

We often want to repeat some code many times. We could type the same code over and over again, but this would be messy, error-prone and boring. No, what we want, is a loop.

A loop is a set of statements that we can repeat several times.

One type of Python loop uses while.

While loops

For our trials, we need to do four coin tosses, and collect the results. We can do this with a while loop, like this:

>>> # We use a counter to keep track of how many times we've run
>>> counter = 0
>>> coin_tosses = []
>>> while counter < 4:
...     result = coin_toss()
...     coin_tosses.append(result)
...     counter = counter + 1
...
>>> coin_tosses
[0, 1, 1, 1]

The while loop, like the if statement, tests a condition, here counter < 4. Like the if statement, there are indented statements after the condition test, that only run if the condition test is True. The difference is that a while loop keeps running the indented statements until the condition is True. So, the first time through, when counter is set to 0, the condition will be True (0 is less than 4), and the body of the while loop executes. But, when the while loop finishes running counter = counter + 1 at the end of the block, it goes back and checks the conditional again. Now counter is 1, but this is still less than 4, and so we proceed to run result = coin_toss() and the rest of the statements in the body. We do this until the condition is False, and then the while loop stops and the program continues after the end of the while loop.

Sum and count

sum is a function, like len or max:

>>> my_list = [3, 5, 1]
>>> sum(my_list)
9

count is a method of a list. As y’all remember, a method is a function attached to something, in this case, a list.

>>> my_list = [3, 5, 1, 3]
>>> my_list.count(3)
2
>>> my_list.count(1)
1

Exercise

Using the while loop above as a template, make a list of 10000 samples from the girl_or_boy function you wrote above. Calculate how many of these samples are girls (have value of 1) (hint: sum might be useful). What proportion of the samples are girls? How close is this to the number you were expecting? Here’s a copy of the while loop above, for you to use as a template:

>>> # A copy of the while loop above for you to edit
>>> counter = 0
>>> coin_tosses = []
>>> while counter < 4:
...     result = coin_toss()
...     coin_tosses.append(result)
...     counter = counter + 1
...
>>> coin_tosses
[0, 1, 0, 0]
[1]Official UK government statistics give the birth ratio as 105.3. This the number of boys born for every 100 girls.