# Python loops

## Contents

# Python loops¶

Loops are used when you need to run the same expression of similar data.
There are two kinds of loops in Python: `for`

loops and `while`

loops.

`for`

loops¶

The syntax of `for`

loops is as follows:

```
for <variable> in <collection>:
# run an expression potentially using <variable>
# run another expression ...
```

The idea is that `<collection>`

is an iterable object like a `tuple`

or a `list`

and that `<variable>`

takes sequentially each one of the values in that iterable object.
Here are a few examples:

```
for x in (1, 5, 6, 10, 20):
print('x = {0:d}'.format(x))
```

```
x = 1
x = 5
x = 6
x = 10
x = 20
```

```
for x in [1, 5, 6, 10, 20]:
print('x = {0:d}'.format(x))
```

```
x = 1
x = 5
x = 6
x = 10
x = 20
```

```
for c in 'This is a string':
print('c = \'{0:s}\''.format(c))
```

```
c = 'T'
c = 'h'
c = 'i'
c = 's'
c = ' '
c = 'i'
c = 's'
c = ' '
c = 'a'
c = ' '
c = 's'
c = 't'
c = 'r'
c = 'i'
c = 'n'
c = 'g'
```

Very often, we want to loop over all values from 0 to a given number.
We can do this with `range`

:

```
for i in range(10):
print('i = {0:d}'.format(i))
```

```
i = 0
i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9
```

You don’t have to start at 0:

```
for i in range(2, 10):
print('i = {0:d}'.format(i))
```

```
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9
```

And you can skip numbers:

```
for i in range(2, 10, 2):
print('i = {0:d}'.format(i))
```

```
i = 2
i = 4
i = 6
i = 8
```

And you can also go backwards:

```
for i in range(10, 0, -1):
print('i = {0:d}'.format(i))
```

```
i = 10
i = 9
i = 8
i = 7
i = 6
i = 5
i = 4
i = 3
i = 2
i = 1
```

Let’s now create some random data and write code to calculate the average:

```
data = [0.16295757, 0.06804887, 0.76082389, 0.39089033, 0.63845432,
0.12555193, 0.98251807, 0.38759292, 0.75819177, 0.63329008,
0.94419572, 0.95363126, 0.60753037, 0.55727347, 0.55989237,
0.2482676 , 0.13208943, 0.25090889, 0.7912593 , 0.30045856]
```

Recall that the average of \(N\) numbers \(x_1, x_2,\dots,x_N\) is:

Here is how we can code this with a for loop:

```
# The number of observations
N = len(data)
# A variable that stores the sum of all the x's as we go through
# the data
s = 0.0
# The loop
for i in range(len(data)):
x = data[i]
# This is just a shortcut for s = s + x
s += x
average = s / N
print('Average = {0:1.2f}'.format(average))
```

```
Average = 0.51
```

Let’s compare this to the other way we have learned:

```
print('Average the other way = {0:1.2f}'.format(sum(data) / N))
```

```
Average the other way = 0.51
```

Okay. It worked fine. Here is another way to do the same thing. Instead of using an index, we can directly go over each element in the list like this:

```
s = 0.0
for x in data:
s += x
average = s / N
print('Average = {0:1.2f}'.format(average))
```

```
Average = 0.51
```

### Questions¶

Write a code block that calculates the average of all the

`data`

elements that are greater than 0.5.

```
# Your code here
```

Write a for loop to calculate the second moment of the

`data`

, i.e., this expression:

```
# Your code here
```

In the code block provided below, write a for loop that finds the maximum of the

`data`

.

```
# Your code here
```

`while`

loops¶

While loops are a bit more general than for loops as stopping depends on a boolean expression. Their syntax is like this:

```
while <boolean_expression>:
# Some expressions to execute
# Some other epxression to execute
```

Let’s see some examples of this:

```
i = 0
while i < 10:
print('i = {0:d}'.format(i))
i += 1
```

```
i = 0
i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9
```

Let’s do a more mathematical example. We are going to write code that calculates the sum of a series to a given tolerance. That is, given a sequence of numbers: \(a_1, a_2, \dots,\), we will write code that approximates:

To be specific, we are going to sum this series:

and use it to approximate \(\pi\). This is known as the Leibniz formula. In terms of the \(\alpha_n\) notation above, the formula is:

Here is our first attempt:

```
# The maximum number of iterations you are willing to do:
max_iter = 100000
# The result
a = 0.0
# Start a counter
n = 0
# Start the loop
while n <= max_iter:
# Compute the new term
a_n = (-1) ** n / (2 * n + 1)
# Add it to the sum
a += a_n
# and increase the counter
n += 1
print('4 * a ~= {0:1.12f}'.format(4 * a))
```

```
4 * a ~= 3.141602653490
```

Let’s now make our code print something every few iterations (say every 10,000 iterations).

```
# The maximum number of iterations you are willing to do:
max_iter = 100000
# The result
a = 0.0
# Start a counter
n = 0
# Start the loop
while n <= max_iter:
# Compute the new term
a_n = (-1) ** n / (2 * n + 1)
# Add it to the sum
a += a_n
# Print something about the current iteration
if n % 10000 == 0:
print('Current iteration n = {0:10d}, pi estimate so far: {1:1.12f}'.format(n, 4 * a))
# and increase the counter
n += 1
print('')
print('4 * a ~= {0:1.12f}'.format(4 * a))
```

```
Current iteration n = 0, pi estimate so far: 4.000000000000
Current iteration n = 10000, pi estimate so far: 3.141692643591
Current iteration n = 20000, pi estimate so far: 3.141642651090
Current iteration n = 30000, pi estimate so far: 3.141625985812
Current iteration n = 40000, pi estimate so far: 3.141617652965
Current iteration n = 50000, pi estimate so far: 3.141612653190
Current iteration n = 60000, pi estimate so far: 3.141609319979
Current iteration n = 70000, pi estimate so far: 3.141606939100
Current iteration n = 80000, pi estimate so far: 3.141605153434
Current iteration n = 90000, pi estimate so far: 3.141603764577
Current iteration n = 100000, pi estimate so far: 3.141602653490
4 * a ~= 3.141602653490
```

Now, let’s not run forever. We are going to modify the code so that it loops over \(n\) until \(|a_{n+1}| < \epsilon\), where \(\epsilon\) is a small positive number typically called a *tolerance*. We are going to use the keyword `break`

to achieve this.
`break`

is a command to exit the loop (note that it also works in `for`

loops).
Here we go:

```
# The maximum number of iterations you are willing to do.
# You always add something like this to avoid looping forever
max_iter = 10000000
# The tolerance you wish to reach:
epsilon = 1e-6
# The result
a = 0.0
# Start a counter
n = 0
# Start the loop
while n <= max_iter:
# Compute the new term
a_n = (-1) ** n / (2 * n + 1)
# Check if the absolute value of the new term is smaller than the tolerance
if abs(a_n) < epsilon:
# If it is indeed smaller, exit the loop
# you do this with the command
print('*** Converged in {0:d} iterations! ***'.format(n+1))
break
# Otherwise we just add the new term to our running sum
a += a_n
# Print something about the current iteration
if n % 100000 == 0:
print('Current iteration n = {0:10d}, sum so far: {1:1.12f}'.format(n, 4 * a))
# and increase the counter
n += 1
if n == max_iter + 1:
print('*** Stopped when maximum number of iterations ({0:d}) were reached! ***'.format(max_iter))
print('4 * a ~= {0:1.12f}'.format(4 * a))
```

```
Current iteration n = 0, sum so far: 4.000000000000
Current iteration n = 100000, sum so far: 3.141602653490
Current iteration n = 200000, sum so far: 3.141597653565
Current iteration n = 300000, sum so far: 3.141595986912
Current iteration n = 400000, sum so far: 3.141595153583
*** Converged in 500001 iterations! ***
4 * a ~= 3.141590653590
```

### Questions¶

Modify the code above so that you estimate the series:

which converges to \(2\).