# Conditional Statements

## Contents

# Conditional Statements#

There are some situations where we want to proceed with a task or perform an action dependent on whether a certain condition, or perhaps conditions, have been satisfied. For example, suppose we want to go on a walk outside if it is sunny. The action of going on a walk depends on whether it is sunny. In general, *conditional statements * are statements where an action or event occurs dependent on *if* a condition is true. We will investigate how to express these types of statements in Python.

Conditional statements have the form of an “if-then” statement: *if* statement `P`

, the hypothesis, occurs, *then* statement `Q`

, the conclusion, also occurs.

How do we write this in code? We utilize the `if`

expression in Python.

The statement below will execute the indented block *conclusion*, if the *hypothesis* is true; otherwise, if *hypothesis* is not true, then the indented block is ignored:

```
if hypothesis:
conclusion
```

Perhaps we want to output whether a number is even. There is more than one method to code this! We could:

Extract the last digit and see if it equals 0, 2, 4, 6 or 8.

Check the remainder when divided by 2 to see if it is 0.

Take our input \(n\) and check if \((-1)^n == 1\).

Convert it to a binary number – (a sequence of 0s and 1s that can uniquely represent each number) – and check the last bit.

The approach we will use is to check if the remainder when divided by 2 is 0. If the remainder is 0, then the input is divisible by 2 and thus even!

Recall we use the `%`

operator to compute the remainder. For example 2 % 3 == 2, as 3 goes into 2 exactly 0 times with remainder 2. We also have 3 % 3 == 0 as 3 divides itself with no remainder, and 4 % 3 == 1 as 4 goes into 3 once with a remainder of 1.

And we’ll use the following `if`

expression to implement a function that tests if the input is even.

```
def test_even(input_number):
'''This function does not return anything, but rather
prints a statement about if the input is even'''
if (input_number % 2) == 0:
print("The number", input_number, "is even.")
```

If the input to this function is even, the string will be printed; otherwise, this function does nothing.

```
test_even(4)
```

```
The number 4 is even.
```

We can revise this function to also include information on odd integers. If we divide a number by 2 and get a remainder of 1, then the number is odd.

Our revised function below takes an input integer then evaluates the first condition: *Is the remainder 0?* If so, the corresponding string is printed. The next condition is then evaluated: *Is the remainder 1?* If so, the corresponding string is printed.

Notice that both conditions are always checked in our `test_parity`

function. Note also that the functions defined in this section do not have a `return`

statement, but rather `print`

their output. This is to showcase that `if`

statements are always checked, regardless of the truth value of a previous statement. Recall a function automatically terminates when `return`

is called, thus to illustrate `if`

statements we are using `print`

in our functions.

```
def test_parity(input_number):
'''Prints if the input is even or odd'''
if (input_number % 2) == 0:
print("The number", input_number, "is even.")
if (input_number % 2) == 1:
print("The number", input_number, "is odd.")
```

```
test_parity(5)
```

```
The number 5 is odd.
```

## Elif statement#

In the case above where we have more than one conditional statement, we can utilize an `elif`

statement. Used in combination with an `if`

expression, an `elif`

statement is only checked if all previous statements evaluate to False. This allows us to check if our first condition is true, otherwise we move to evaluate the truth value of the next statement. In words this says “If hypothesis_1 is True then evaluate, else if hypothesis_2 is True then evaluate, …etc”, where the first ‘True’ is evaluated.

The format of an elif statement is:

```
if hypothesis_1:
conclusion_1
elif hypothesis_2:
conclusion_2
...
elif hypothesis_n:
conclusion_n
```

In an `elif`

statement, once a condition is true, the remaining condition or conditions are not evaluated.

Let’s revise the function `test_parity`

to use an `elif`

statement in a new function: `test_parity_revised`

.

```
def test_parity_revised(input_number):
'''Prints if the input is even or odd'''
if (input_number % 2) == 0:
print("The number", input_number, "is even.")
elif (input_number % 2) == 1:
print("The number", input_number, "is odd.")
```

```
test_parity_revised(9)
```

```
The number 9 is odd.
```

```
test_parity_revised(8)
```

```
The number 8 is even.
```

When we input 8 to the test_parity_revised function, the first `if`

statement evaluates to True. At this point in the function, the remaining `elif`

will not execute thus saving computation time.

Now our function can take any integer and output whether or not that value is even or odd.

But what if our user enters the number 5.7? What if they enter a fraction or an irrational number?? We can make `test_parity_revised`

more complete by indicting what to do in these cases.

## Else statement#

Since we have a condition that checks all even numbers and a condition that checks all odd numbers, we want to group all other possibilities into one category – neither even nor odd.

We can do this with the `else`

statement. The `else`

statement takes one of the following forms.

We could have exactly two possible conclusions which is formatted:

```
if hypothesis_1:
conclusion_1
else:
conclusion_2
```

Or we could have \(n+1\) conclusions for a chosen \(n\) in which case we have the format:

```
if hypothesis_1:
conclusion_1
elif hypothesis_2:
conclusion_2
...
elif hypothesis_n:
conclusion_n
else:
conclusion
```

Similar to an `elif`

statement whose condition is true, once the Python interpreter has reached the `else`

statement, no more conditions are evaluated – it is, after all, the last option. At this point, all other conditions have been evaluated and none executed. Thus, there is no condition or hypothesis associated with the `else`

statement. If it is reached, the conclusion of the `else`

statement is executed.

Let’s alter the function `test_partity_revised`

to use an `else`

statement, in a new function: `test_parity_final`

.

```
def test_parity_final(input_number):
'''Returns a number's parity '''
if (input_number % 2) == 0:
print("The number", input_number, "is even.")
elif (input_number % 2) == 1:
print("The number", input_number, "is odd.")
else:
print("The number", input_number, "is neither even nor odd.")
```

Comparing the output of this function to `test_parity_revised`

, we see that the past functions cannot handle a decimal input like 5.7, whereas `test_parity_final`

can!

```
test_parity_final(5)
```

```
The number 5 is odd.
```

```
test_parity_final(5.7)
```

```
The number 5.7 is neither even nor odd.
```

```
test_parity_revised(5.7)
```

A note of caution when using the `else`

statement. Since the `else`

statement is executed without checking a condition, you want to be absolutely certain that all desired possibilities are accounted for in the previous condition(s).

For example, if we change `test_parity_revised`

as below to check if a number is even, but assume that everything else must be odd, we get an error when testing something like 5.7 in this function, as 5.7 is not odd!

```
def test_parity_revised(input_number):
'''Returns a number's parity '''
if (input_number % 2) == 0:
print("The number", input_number, "is even")
else:
print("The number", input_number, "is odd")
test_parity_revised(5.7)
The number 5.7 is odd.
```

Whoops!

## Example: six-sided die#

We can use random selection in combination with conditional statements to investigate abstract phenomena.

For example, suppose we want to consider how often even versus odd numbers are rolled in 100 rolls of an ideal six-sided die. We can illustrate this using the random choice function in addition to our parity function above.

First, we create a six-sided die, by creating an array containing the numbers 1 - 6.

When we randomly select a choice from this list, we are simulating rolling a die. Repeating this 100 times gives us an array containing the outcomes of 100 tosses.

```
import numpy as np
```

```
die = np.arange(1, 7)
die
```

```
array([1, 2, 3, 4, 5, 6])
```

```
results_roll = np.random.choice(die, 100)
results_roll
```

```
array([1, 2, 4, 3, 4, 2, 2, 6, 3, 6, 5, 3, 3, 6, 4, 1, 2, 4, 2, 5, 6, 5,
3, 2, 5, 3, 6, 1, 2, 4, 3, 1, 6, 1, 2, 2, 1, 6, 2, 4, 2, 5, 2, 5,
6, 1, 6, 4, 1, 5, 1, 5, 3, 4, 3, 2, 3, 4, 5, 3, 2, 4, 2, 4, 5, 1,
5, 4, 3, 5, 3, 2, 4, 2, 2, 4, 5, 1, 3, 2, 3, 1, 5, 6, 1, 2, 6, 2,
3, 1, 3, 3, 6, 1, 1, 5, 2, 2, 2, 2])
```

Our random roll generator outputs a number between 1 and 6, but we are only interested in if that number is even or odd. But, the way we’ve written our function, we cannot directly put an entire array through it.

Instead we’ll call `np.vectorize`

, which takes a function as input and returns as output a function that acts on an entire array. That is, it allows for elementwise evaluation of our parity function!

Below we define a new function, `vec_parity`

, which takes an array containing the roll results, that is an array of numbers 1 - 6, and returns an array denoting even or odd rolls.

Note that in the function below we use an *if-else* statement, because we are *assuming* the inputs are integer values 1 - 6.

```
def parity(input_integer):
'''Assumes integer input
Returns even or odd'''
if (input_integer % 2) == 0:
return "even"
else:
return "odd"
vec_parity = np.vectorize(parity)
results_parity = vec_parity(results_roll)
results_parity
```

```
array(['odd', 'even', 'even', 'odd', 'even', 'even', 'even', 'even',
'odd', 'even', 'odd', 'odd', 'odd', 'even', 'even', 'odd', 'even',
'even', 'even', 'odd', 'even', 'odd', 'odd', 'even', 'odd', 'odd',
'even', 'odd', 'even', 'even', 'odd', 'odd', 'even', 'odd', 'even',
'even', 'odd', 'even', 'even', 'even', 'even', 'odd', 'even',
'odd', 'even', 'odd', 'even', 'even', 'odd', 'odd', 'odd', 'odd',
'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd', 'odd', 'even',
'even', 'even', 'even', 'odd', 'odd', 'odd', 'even', 'odd', 'odd',
'odd', 'even', 'even', 'even', 'even', 'even', 'odd', 'odd', 'odd',
'even', 'odd', 'odd', 'odd', 'even', 'odd', 'even', 'even', 'even',
'odd', 'odd', 'odd', 'odd', 'even', 'odd', 'odd', 'odd', 'even',
'even', 'even', 'even'], dtype='<U4')
```

Our goal is to compare the total number of even rolls out of the total 100 rolls of the die.

Recall we can count all the even rolls in the array by elementwise comparing each result to “even” and summing over the instances of `True`

.

```
total_even = sum(results_parity == 'even')
total_even
```

```
51
```

Suppose we want to repeat this experiment a few times and record the results. We create a `results`

array containing the total evens in the first 100 rolls and extend this list with each new experiment.

```
results = np.array([total_even])
results
```

```
array([51])
```

We first generate the 100 random rolls of the die using `np.random.choice(die, 100)`

.

From this array, the `vec_parity`

function will tell us which rolls were even and which rolls were odd. We sum over the even elements and append this to the current array.

(Recall `np.append()`

does not modify the original list, so the assignment is necessary.)

```
results = np.append(results, sum(vec_parity(np.random.choice(die, 100)) == 'even'))
results
```

```
array([51, 57])
```

We can run this experiment again!

```
results = np.append(results, sum(vec_parity(np.random.choice(die, 100)) == 'even'))
results
```

```
array([51, 57, 50])
```

And again!

```
results = np.append(results, sum(vec_parity(np.random.choice(die, 100)) == 'even'))
results
```

```
array([51, 57, 50, 53])
```

And again?

```
results = np.append(results, sum(vec_parity(np.random.choice(die, 100)) == 'even'))
results
```

```
array([51, 57, 50, 53, 52])
```

Is there a more automated way? In the next section we explore how we can streamline this process of running an experiment multiple times.