Lesson 6: Python Programming 1 - Loops#
This lesson is modified from for-loops from geo-python.
Objectives#
By the end of this lesson, you will be able to
repeat tasks using
for
andwhile
loopsiterate over all of the items in a list to perform calculation
use list and dictionary comprehensions to create a python list and python dictionary (optional)
1. Basics of for loops#
In our last lesson we covered lists in Python, one form of a collection of values that can be referenced (indexed) by a single variable. In this lesson we will learn how to use loops. Loops allow parts of code to be repeated some number of times, such as using a section of code to process each item in a list.
Source: https://www.bugmartini.com/comic/cats-eye/
1.1 A (bad) example#
Let’s consider an example using the list below:
time_slots = ["7:00 AM", "7:13 AM", "3:00 PM", "3:10 PM", "4:30 PM" , "5:15 PM", "6:33 PM"]
daily_activities = ["Wake up", "Nap", "Beg for food", "Doze", "Rest", "Sleep", "Act bat sh*t crazy for no good reason"]
a=[1,2, 3, 4, 5]
len(a)
for i in range(len(a)):
print(i,a[i])
0 1
1 2
2 3
3 4
4 5
Suppose we want to check each activity in our lists and for each item print something like this:
Output: At 7:00 am, I Wake Up
We could use the index value for each time and activity and do the following:
print(f"At {time_slots[0]}, I {daily_activities[0]}")
At 7:00 AM, I Wake up
print(f"At {time_slots[1]}, I {daily_activities[1]}")
At 7:13 AM, I Nap
print(f"At {time_slots[2]}, I {daily_activities[2]}")
At 3:00 PM, I Beg for food
print(f"At {time_slots[3]}, I {daily_activities[3]}")
At 3:10 PM, I Doze
But this is a bad idea. Why? Well there are two reasons:
It does not scale nicely for long lists; it will take forever to type in.
It won’t work if the length of the list has fewer or more activies
Here is an example of how it will not work with new lists.
time_slot2 = ["7:00 AM", "7:13 AM"]
daily_activities2 = ["Wake up", "Nap"]
print(f"At {time_slot2[0]}, I {daily_activities2[0]}")
At 7:00 AM, I Wake up
print(f"At {time_slot2[1]}, I {daily_activities2[1]}")
At 7:13 AM, I Nap
print(f"At {time_slot2[2]}, I {daily_activities2[2]}")
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
Cell In[10], line 1
----> 1 print(f"At {time_slot2[2]}, I {daily_activities2[2]}")
IndexError: list index out of range
Here we encounter an IndexError
because we have tried to access a value outside the range of values in the updated european_cities
list.
Would not it be easier if we just did this:
for time_slot, daily_activity in zip(time_slots, daily_activities):
print(f"At {time_slot}, I {daily_activity}")
At 7:00 AM, I Wake up
At 7:13 AM, I Nap
At 3:00 PM, I Beg for food
At 3:10 PM, I Doze
At 4:30 PM, I Rest
At 5:15 PM, I Sleep
At 6:33 PM, I Act bat sh*t crazy for no good reason
1.2 Introducing the for loop#
In such situations it is far more efficient to use a for
loop. Let’s see an example.
florida_cities = ["Fort Myers", "Cape Coral", "Lehigh Acres", "Estero"]
for city in florida_cities:
print(city)
Fort Myers
Cape Coral
Lehigh Acres
Estero
Not only is this shorter than indexing through the list item by item, but it is also more flexible.
Try doing your first for loop to print out each city in this list.
us_cities = [
"Detroit",
"Chicago",
"Denver",
"Boston",
"Portland",
"San Francisco",
"Houston",
"Orlando",
]
for city in us_cities:
print(city)
Detroit
Chicago
Denver
Boston
Portland
San Francisco
Houston
Orlando
1.3 for loop format#
The for
loops in Python have the general form below.
for variable in collection:
use the variable to do things
Let’s break down the code above to see some essential aspects of for
loops:
The
variable
can be any name you like other than a reserved keywordThe statement of the
for
loop must end with a:
The code that should be executed as part of the loop must be indented beneath the
for
statement
The typical indentation is 4 spaces
There is no additional special word needed to end the loop, you simply change the indentation back to normal.
Hint
for
loops are useful to repeat some part of the code a finite number of times.
1.4 for loop variables#
Note that the variable used in a for
loop is just a normal variable. Thus, its value still exists after the loop has been run. Let’s loop over the list of weather conditions below and print them to the screen. If you use weather
for the loop variable, what is its value after the for
loop has completed?
For this list below, write a for loop that prints each weather condition in the list.
After the loop, print the variable name weather
in a new cell.
weather_conditions = [
"rain",
"sleet",
"snow",
"freezing fog",
"sunny",
"cloudy",
"ice pellets",
]
for weather in weather_conditions:
print(weather)
rain
sleet
snow
freezing fog
sunny
cloudy
ice pellets
print(f"After the loop, weather is {weather}")
After the loop, weather is ice pellets
1.5 for loops and the range() function#
A loop can be used to iterate over any collection of values in Python. So far we have considered only iterating over lists, but we could also write a loop that performs a calculation a specified number of times by using the range()
function.
Use the range()
function to create a list using list(range())
or [*range()]
. Here is how to do it to create, for example, this list “[0, 1, 2, 4]”
list(range(5))
[0, 1, 2, 3, 4]
[*range(5)]
[0, 1, 2, 3, 4]
In this case, we used a special function called range()
to give us a list of 5 numbers [0, 1, 2, 3, 4]
. When given an integer (whole number) as an argument, range(number)
will produce a list of numbers with a length equal to the specified number
.
The list starts at 0
and ends with number - 1
.
In case you are wondering about the syntax `[* range()]`, asterisk `*` is used for unpacking elements from iterables like lists, tuples, or other iterable objects.
You can learn a bit more about range by typing range?
or help(range)
?
range?
Init signature: range(self, /, *args, **kwargs)
Docstring:
range(stop) -> range object
range(start, stop[, step]) -> range object
Return an object that produces a sequence of integers from start (inclusive)
to stop (exclusive) by step. range(i, j) produces i, i+1, i+2, ..., j-1.
start defaults to 0, and stop is omitted! range(4) produces 0, 1, 2, 3.
These are exactly the valid indices for a list of 4 elements.
When step is given, it specifies the increment (or decrement).
Type: type
Subclasses:
Let’s consider an example where we use a for loop with value
as the loop variable and range(5)
as the collection. What happens when you print value
at each iteration?
for value in range(5):
print(value)
0
1
2
3
4
Check your understanding#
The program below will print numbers to the screen using the range()
function.
for i in range(...):
print(i)
Using the documentation that is produced when you run help(range)
, what values would you replace the ...
in the parentheses of the range()
function with to have the following output printed to the screen?
2
5
8
You can test your solution in the cell below
Show code cell content
# Here's one possible solution
for i in range(2, 9, 3):
print(i)
2
5
8
weather_conditions = [
"rain",
"sleet",
"snow",
"freezing fog",
"sunny",
"cloudy",
"ice pellets",
]
# Here's one possible solution
for i in range(2,len(weather_conditions),3):
print(weather_conditions[i])
snow
cloudy
1.6 Looping over the length of lists using index values#
Since we already know how to find the length of a list using the len()
function, we can now take advantage of this knowledge to make our for
loops more flexible. Let’s start by doing something we have done earlier, printing out city names using a for
loop. However, this time we will also use the range()
function to print the cities.
1.6.1 How to use index values to loop over a list?#
Since we already know how to find the length of a list using the len()
function, we can now take advantage of this knowledge to make our for
loops more flexible. Let’s start by doing something we have done earlier, printing out city names using a for
loop. However, this time we will also use the range()
function to print the cities.
african_cities = ["Dakar", "Addis Ababa", "Nairobi", "Cairo", "Rabat", "Kampala"]
for idx in range(len(african_cities)):
print(f"{african_cities[idx]} is at index {idx} in the list.")
Dakar is at index 0 in the list.
Addis Ababa is at index 1 in the list.
Nairobi is at index 2 in the list.
Cairo is at index 3 in the list.
Rabat is at index 4 in the list.
Kampala is at index 5 in the list.
Let’s see what is different now:
You can see that because we are using the
range()
function, the value assigned to the loop variableidx
starts with0
and increases by1
each time through the loop.In order to access individual cities in the
african_cities
list, we need to include the index value (e.g.,african_cities[idx]
. Here, the variableidx
will be replaced by the current value assigned from therange()
function.
Note
While the variable i
is commonly used to denote the index variable in loops, a good coding practice often involves using meaningful and descriptive variable names to enhance code readability. For example, if you are iterating over elements in an array representing a number of items, you might use itemIdx
or idx
instead of i
. If you are working with matrices, you could use rowIdx
and colIdx
instead of i
and j
. For nested loops, consider using more descriptive names like outerIdx
and innerIdx
.
1.6.2 Why use index values to loop over a list?#
Good question. There are two common case where you might need to loop using index values:
If you want to update individual values in a list you’re likely going to need a loop that includes the index values. There are functions such as
enumerate()
as shown below that can help, but their use can be somewhat confusing for new programmers.In cases where you have multiple lists that are related to one another, it can be handy to use a loop with the index values to be able to access corresponding locations in each list. For this, let’s consider an example with the two lists below.
Check your understanding#
What output would the following program produce?
odd_numbers = [1, 3, 5, 7, 9]
even_numbers = [10, 4, 6, 8, 2]
for idx in range(len(odd_numbers)):
print(odd_numbers[idx]*3 + even_numbers[idx]*5)
Try to think about the loop without running the code
Show code cell content
# Here's the solution
odd_numbers = [1, 3, 5, 7, 9]
even_numbers = [10, 4, 6, 8, 2]
for idx in range(len(odd_numbers)):
print(odd_numbers[idx]*3 + even_numbers[idx]*5)
53
29
45
61
37
Try to do this one. As you can see we have 5 cities and 5 corresponding counties. Print out each pair using a single for loop and print the output to screer to look like this
Washington D.C. is the capital of USA
for each city.
cities = ["Washington D.C.", "Berlin", "Cairo", "Sydney", "Kathmandu"]
countries = ["USA", "Germany", "Egypt", "Australia", "Nepal"]
for idx in range(len(cities)):
print(f"{cities[idx]} is the capital of {countries[idx]}")
Washington D.C. is the capital of USA
Berlin is the capital of Germany
Cairo is the capital of Egypt
Sydney is the capital of Australia
Kathmandu is the capital of Nepal
Cool. So as you can see, the index idx
is used in this case to access each item in the two lists of cities and countries and allow us to print out the city/country pairs. We’ll get more practice with this kind of thing in the exercises for this week.
Note
In the example above, we used the length of the list cities
or countries
in the range()
function since both lists are the same length.
1.7 Loop with zip
- no idexing required#
You can use zip
function to iterate over multiple lists simultaneously, something like this
for variable1, variable2 in zip(list1, list2):
use variable1, variable2 to do something
Try the above sample using zip
instead of indexing.
cities = ["Washington D.C.", "Berlin", "Cairo", "Sydney", "Kathmandu"]
countries = ["USA", "Germany", "Egypt", "Australia", "Nepal"]
for city, country in zip(cities, countries):
print(f"{city} is the capital of {country}")
Washington D.C. is the capital of USA
Berlin is the capital of Germany
Cairo is the capital of Egypt
Sydney is the capital of Australia
Kathmandu is the capital of Nepal
In that case zip
is pairing corresponding elements from cities
and countries
, and you can iterate over these pairs in the for loop. Using zip
is more pythonic. Using indexing is more like MATLAB.
1.8 Looping over the length of list using enumerate()
(advanced topic)#
If you want to use python as python (not as MATLAB) instead of using range(len())
use the enumerate()
function. Here we do the above example but with enumerate()
instead of range(len())
.
1.8.1 Understanding enumerate()
step-by-step#
Before we use enumerate()
let use first understand what it does with this example
african_cities = ["Dakar", "Addis Ababa", "Nairobi", "Cairo", "Rabat", "Kampala"]
for object in enumerate(african_cities):
print(object)
print(f"{african_cities[object[0]]} is at index {object[1]} in the list.")
(0, 'Dakar')
Dakar is at index Dakar in the list.
(1, 'Addis Ababa')
Addis Ababa is at index Addis Ababa in the list.
(2, 'Nairobi')
Nairobi is at index Nairobi in the list.
(3, 'Cairo')
Cairo is at index Cairo in the list.
(4, 'Rabat')
Rabat is at index Rabat in the list.
(5, 'Kampala')
Kampala is at index Kampala in the list.
In case you are wondering, the above object is a tuple
, which is a data strcuture similar to list
, that is defined using parentheses, and can contain elements of different data types. Other data strctures in python include dict
and set
. Check Chapter 3. Built-In Data Structures, Functions, and Files in your textbook if you want to learn more about data strctures in python.
1.8.2 How to use enumerate()
to loop over a list?#
The above example is still more or less like how you will do it in MATLAB, but to do this in python style:
african_cities = ["Dakar", "Addis Ababa", "Nairobi", "Cairo", "Rabat", "Kampala"]
for idx,city in enumerate(african_cities):
print(f"{idx} is at index {city} in the list.")
0 is at index Dakar in the list.
1 is at index Addis Ababa in the list.
2 is at index Nairobi in the list.
3 is at index Cairo in the list.
4 is at index Rabat in the list.
5 is at index Kampala in the list.
Much simpler than MATLAB style as shown in Section 1.6. But it might take you sometime to migrate from coding the MATLAB way to the python way.
1.9 Using list and dictionary comprehensions (advanced topic)#
Comprehensions in python provide a concise and efficient way to create lists and dictionaries. They are particularly useful when dealing with large datasets.
1.9.1 The syntax and concept of list comprehensions#
A list comprehension consists of brackets containing an expression followed by a for clause, then zero or more for or if clauses. The result will be a new list resulting from evaluating the expression in the context of the for and if clauses which follow it.
[expression for item in list if condition]
Example, let us create a list of 5 positive integers and call it list1
list1=[value for value in range(1,6)]
print(list1)
[1, 2, 3, 4, 5]
let us create this list [2, 4, 6, 8] by at least three different ways
print([value*2 for value in range(1,5)])
print([value for value in range(2,10,2)])
print([value for value in range(2,10,2) if value % 2 == 0])
[2, 4, 6, 8]
[2, 4, 6, 8]
[2, 4, 6, 8]
You are familiar with the first two methods. I am adding the third method to make you curious. We will learn the if condition in the next lesson. But you are probably wondering what is this %
? The %
operator in python is the modulus operator. It returns the remainder when the left operand is divided by the right operand. In the context of if value % 2 == 0
, it is checking if the variable value
is even. Here is the breakdown: value % 2
calculates the remainder when value
is divided by 2, and value % 2 == 0
checks if the remainder is equal to 0, which means that value
is divisible evenly by 2. So, if value % 2 == 0
is a common way to test for even numbers.
Test your understanding#
Use list comprehension to convert a list of temperatures in Celsius to Fahrenheit.
fahrenheit_temp = (celsius_temp * 9/5) + 32
remember here is the formula
[expression for item in iterable]
To think about this: Identify your expression, item, and iterable
celsius_temperatures = [-50, -40, -30, -20, -10, 0, 5, 10, 15, 20, 25, 30, 35, 40, 40, 45, 50, 56.7]
fahrenheit_temperatures= [(celsius_temperature * 9/5) + 32 for celsius_temperature in celsius_temperatures]
print(fahrenheit_temperatures)
[-58.0, -40.0, -22.0, -4.0, 14.0, 32.0, 41.0, 50.0, 59.0, 68.0, 77.0, 86.0, 95.0, 104.0, 104.0, 113.0, 122.0, 134.06]
Note that 134°F (56.7°C), which was recorded in Death Valley, California on July 10, 1913, is the highest recorded temperature on Earth. The lowest temperature ever recorded in Florida is -2°F, which was recorded in Tallahassee on February 13, 1899.
1.9.2 Dictionary Comprehensions#
A concept similar to list comprehensions, is dictionary comprehensions that allow you to create dictionaries in a single, readable line.
Before we learn how to do this, what is a python dictionary ? We learned about the list
data structure (e.g., mylist=[1,2,3]
). Python dictionary dic
is another python data structure that stores key-value pairs. Each key in the dictionary must be unique, and it is associated with a corresponding value. Dictionaries are defined within braces {}
with each item being a pair in the form key: value
.
Here’s a brief example of a Python dictionary:
# Creating a dictionary
city_data = {
'city': 'Fort Myers',
'temperature': 40.5,
'humidity': 60,
'air_quality': 'good'
}
# Accessing values using keys
print("City:", city_data['city'])
print("Temperature:", city_data['temperature'])
print("Humidity:", city_data['humidity'])
print("Air Quality:", city_data['air_quality'])
City: Fort Myers
Temperature: 40.5
Humidity: 60
Air Quality: good
So dictionary is another form of storing data in python like a list. In the above example, we created the dictionary city_data
manually. Now hwo to create a dictionary with dictionary comprehensions?
Here is one syntax
{key: value for key, value in zip(keys,values)}
Try it out with the following example:
# Key parameters and corresponding values for a location
parameters = ['City', 'Temperature', 'Humidity', 'Air Quality']
values = ['Fort Myers', '40.5', '60','good']
# Create a dictionary mapping each parameter to its value
city_data={parameter : value for parameter, value in zip(parameters, values)}
print(city_data)
{'City': 'Fort Myers', 'Temperature': '40.5', 'Humidity': '60', 'Air Quality': 'good'}
You can also create a dictionary from lists. Here is an example:
#Multiple lists
models=['A', 'B', 'C', 'D']
launched=[1983,1984,1984,1984]
discontinued=[1986, 1985, 1984, 1986]
#Dictionary with list comprehension (the easy way flowing the syntax above)
keys=['models','launched', 'discontinued']
values=[models, launched, discontinued]
data1 = {key: value for key, value in zip(keys,values)}
#Dictionary with list comprehension (another way of doing it)
data2 = {key:values[idx] for idx, key in enumerate(keys)}
#Printing some outputs
print(data1['models'][0])
print(data1['models'])
print(data1)
print(data2)
A
['A', 'B', 'C', 'D']
{'models': ['A', 'B', 'C', 'D'], 'launched': [1983, 1984, 1984, 1984], 'discontinued': [1986, 1985, 1984, 1986]}
{'models': ['A', 'B', 'C', 'D'], 'launched': [1983, 1984, 1984, 1984], 'discontinued': [1986, 1985, 1984, 1986]}
1.9.3 Challaning problem#
This is a challanging problem where you can apply what you have learned in Lesson 4 about the method list.count()
, list comprehesions, and diconary comprehesions. You need to apply these three tools to solve this problem.
Suppose you have a dataset that includes species names and their counts
species = ['Oak', 'Pine', 'Birch', 'Oak', 'Pine', 'Oak']
How can you count each species and produce a dictionary showning each species with its corresponding count as follows:
Output: {'Oak': 3, 'Pine': 2, 'Birch': 1}
This is not an easy puzzle, but you already have a clue about the three tools that you can use. Start by remembering what list.count()
can do.
species = ['Oak', 'Pine', 'Birch', 'Oak', 'Pine', 'Oak']
species.count('Oak')
3
Can I loop over the list species
can keep counting. Something like this
[expression for item in list]
In that case my expression will be the counting.
species = ['Oak', 'Pine', 'Birch', 'Oak', 'Pine', 'Oak']
counts = [species.count(name) for name in species]
print(counts)
[3, 2, 1, 3, 2, 3]
Now remeber each key in the dictionary must be unique, so if we created a dictionary using the two lists that we have that are species
and counts
, we will get what we want. Here is the dictionary syntax again
{key: value for key, value in zip(keys,values}
Apply this syntax with the two lists that you have, to create an new dictionary species_dict
species_dict = {name: count for name, count in zip(species, counts)}
print(species_dict)
{'Oak': 3, 'Pine': 2, 'Birch': 1}
If you can understand that much, this means that you have a very good command of python.
So far you have learned how to use list and dictionary comprehensions to create lists and dictionaries efficiently. These techniques are valuable for handling and processing large datasets, allowing us to perform data transformations and analysis.
1.10 Using for-loop to apply a mathematical formula to a list#
We will do this later more effectively with the numpy library similar to MATLAB. Here we will use for-loops to perform calculations on elements of a list and store the results in a new list. We will see this with both explicit for-loop and implicit for-loop (i.e., list comprehensions).
1.10.1 Using explicit for-loop#
We want to create a new list new_list
based on values from another list existing_list
and a formula. To do this:
create new empty list
new_list = []
use for-loop to calculate formula element by element
append
new_list
with the calculated value using append method
For example, apply the formua y= x * 3
to each element in this list
existing_list=[2, 4, 6, 8]
such the the output will be
output: new_list=[6, 12, 18, 24]
Here is how to do this.`
#One list
existing_list=[2, 4, 6, 8]
#Create new empty list "new_list"
new_list = []
#for loop to apply formula y= x* 3 to the existing list element by element and save output to the new list
for value in existing_list:
y=value*3 #calculate formula
new_list.append(y) #append list with calculated value
#Print new list
print(new_list)
[6, 12, 18, 24]
1.10.2 Using list comprehension (advanced topic)#
List comprehension can achieve the same result without explicitly writing a loop. Here is the syntax again
new_list=[expression for item in existing_list]
Here’s how you can do it:
#One list
existing_list=[2, 4, 6, 8]
# Apply formula y= x * 3 using list comprehension
new_list = [value*3 for value in existing_list]
# Print new list
print(new_list)
[6, 12, 18, 24]
This code does exactly the same thing as the previous one, but it does so in a more concise way by using a list comprehension instead of a for loop.
1.11 For loop with unpacking#
How can you loop through a nested list that has four sublists.
list_of_lists=[
[10, 100, 1000],
[20, 200, 2000],
[40, 500, 6000],
[50, 500, 5000],
]
You have many opitions. You can do indexing, or unpacking for the sublist.
For example, add the three numbers in each of the above four sublists.
list_of_lists=[
[10, 100, 1000],
[20, 200, 2000],
[40, 400, 4000],
[50, 500, 5000],
]
This is the indexing method (the MATLAB way)
for sublist in list_of_lists:
sum= sublist[0]+ sublist[1]+ sublist[2]
print(sum)
1110
2220
4440
5550
This is the unpacking method
for sublist in list_of_lists:
num1, num2, num3 = sublist
sum= num1 + num2 + num3
print(sum)
1110
2220
4440
5550
Unpacking, more concisely
for num1,num2,num3 in list_of_lists:
sum= num1 + num2 + num3
print(sum)
1110
2220
4440
5550
You can also do a nested loop that is a loop inside a loop, but that should be your last resort, when no other approach can solve your problem.
2. Intorduction to while loop#
Control structures in python are loops (i.e, for-loop and while-loop) and conditional statements (next lesson). While for loop loops a collection of values, while loop loops until some condition is met. Here is a simple example.
x = 0
while x < 5:
print('x is ', x)
x = x+2
x is 0
x is 2
x is 4
Note: In Python, incrementing the value of x by 2, you will see the syntax x += 2
used more often than x = x+2
as shown above. The reason for using +=
is not just brevity, but it can also saves some memory, making the code slightly more efficient. Let us rewrite the above code to be more pythonic
x = 0
while x < 5:
print('x is ', x)
x += 2
x is 0
x is 2
x is 4
References#
for-loops by Geo-python
Intorduction to Python by Python for Scientific Computing
1.2 The Python language by Scientific Python Lectures
3 Built-In Data Structures, Functions, and Files in Python for Data Analysis, 3rd edition