Python loops#

Loops are used when you need to run the same expression repeatedly for 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(f'x = {x:d}')
x = 1
x = 5
x = 6
x = 10
x = 20
for c in 'This is a string':
    print(f"c = '{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(f'i = {i:d}')
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(f'i = {i:d}')
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(f'i = {i:d}')
i = 2
i = 4
i = 6
i = 8

And you can also go backwards:

for i in range(10, 0, -1):
    print(f'i = {i:d}')
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 data and write code to calculate the average using a forloop:

data = [15.0, 0.25, 3.5, 5.1, 21.16]

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

\[ \bar{x} = \frac{x_1+\dots+x_N}{N}. \]

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(f'Average = {average:1.2f}')
Average = 9.00

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

print(f'Average the other way = {sum(data) / N:1.2f}')
Average the other way = 9.00

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(f'Average = {average:1.2f}')
Average = 9.00

Questions#

  • Write a for loop that calculates the average of all the data elements that are greater than 5.

# Your code here
  • Write a for loop to calculate the second moment of the data, i.e., this expression:

\[ \bar{x}_2 = \frac{x_1^2 + \dots + x_N^2}{N} \]
# Your code here
  • Write a for loop that finds the maximum of the data.

# Your code here

The while loop#

The while loop is a bit more general than the for loop as stopping depends on a boolean expression. Its 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(f'i = {i:d}')
    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:

\[ a = \sum_{n=0}^\infty a_n. \]

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

\[ 1 - \frac{1}{3} + \frac{1}{5} - \frac{1}{7} + \dots = \frac{\pi}{4}, \]

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

\[ \sum_{n=0}^\infty a_n = \sum_{n=0}^\infty\frac{(-1)^{n}}{2n + 1}. \]

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(f'pi ~= {4 * a:1.12f}')
pi ~= 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(f'Current iteration n = {n:10d}, pi ~= {4 * a:1.12f}')
    # and increase the counter
    n += 1
print(f'pi ~= {4 * a:1.12f}')
Current iteration n =          0, pi ~= 4.000000000000
Current iteration n =      10000, pi ~= 3.141692643591
Current iteration n =      20000, pi ~= 3.141642651090
Current iteration n =      30000, pi ~= 3.141625985812
Current iteration n =      40000, pi ~= 3.141617652965
Current iteration n =      50000, pi ~= 3.141612653190
Current iteration n =      60000, pi ~= 3.141609319979
Current iteration n =      70000, pi ~= 3.141606939100
Current iteration n =      80000, pi ~= 3.141605153434
Current iteration n =      90000, pi ~= 3.141603764577
Current iteration n =     100000, pi ~= 3.141602653490
pi ~= 3.141602653490

We are going to end with a final modification: a convergence test. 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 can test for this condition using an if statement. But how can we stop the loop when this condition is met? We can do this by using the break command. The break command exits the loop immediately (note that it also works in for loops). Here we go:

max_iter = 10000000
epsilon = 1e-6
n = 0
a = 0.0
while n <= max_iter:
    a_n = (-1) ** n / (2 * n + 1)
    if abs(a_n) < epsilon:
        print(f'*** Converged in {n+1:d} iterations! ***')
        break
    a += a_n
    if n % 100000 == 0:
        print(f'Current iteration n = {n:10d}, pi ~= {4 * a:1.12f}')
    n += 1
if n == max_iter + 1:
    print(f'*** Stopped when maximum number of iterations ({max_iter:d}) were reached! ***')
print(f'pi ~= {4 * a:1.12f}')
Current iteration n =          0, pi ~= 4.000000000000
Current iteration n =     100000, pi ~= 3.141602653490
Current iteration n =     200000, pi ~= 3.141597653565
Current iteration n =     300000, pi ~= 3.141595986912
Current iteration n =     400000, pi ~= 3.141595153583
*** Converged in 500001 iterations! ***
pi ~= 3.141590653590

Questions#

  • Modify the code above so that you estimate the series:

\[ \sum_{n=0}^\infty\frac{1}{2^n}, \]

which converges to \(2\).