Path: blob/main/Lessons/Lesson 01 - LP 1/extras/Python Refresher - Loops.ipynb
871 views
Loops Introduction
Loops are fundamental programming concepts, and they're used extensively when we're hand-coding optimization problems. Understanding how loops work will be crucial to your ability to successfully complete your homework. Generally, we use for loops and while loops. Both are similar, but have slightly different stopping conditions.
The easiest way to understand what loops are doing is to practice using them with simple conditions, printing out your variables for each iteration of the loop. Flow diagram images in this tutorial are from https://medium.com/datadriveninvestor/how-to-understand-for-and-while-loop-visually-c11052479df5
For Loops
For loops are used when you have a specific number of iterations you'd like to loop. You can either loop using a static number, or you can loop over an object that is iterable (a list, a dictionary, a numpy array, a dataframe, etc.)
Conceptually, a for loop looks like this:
In other words, for each item in a sequence, we execute some code. If at the end of the code execution, there is another item in the sequence, we go back to the top of our code block and execute again, using the next item of the sequence. Let's see how it works with looping over a numpy array of random numbers. We'll manually sum up the random numbers using a for loop.
So far we've looped through twice, but we haven't done anything to total our numbers. This time, we'll add to our total with each loop. We have to have a variable that gets set before we start our loop, so that we can add to it inside the loop. If we initialized the total variable inside the loop, we'd be resetting it each time.
What if we only wanted to add to our total if our current item was an even number? We can evaluate our current item inside the loop and use a conditional if statement to decide whether or not to add to the total.
That's a lot of lines of code just to sum some numbers. Python has shortcut ways to do these kinds of for loops. One of those is called a list comprehension. If all we wanted to do was print the sum of all the even numbers, we could do it in a single line, like below.
Let's breakdown what's happening in that list comprehension, starting with the line inside the inner set of brackets [].
This line i for i in items
is our for loop. Notice the syntax is a little bit different. First we're telling Python to return i
, and then we're telling it what i
is (each item in the list of items).
What this returns is a list with just the items that meet our condition.
Python is smart enough that you can just wrap that whole list in sum, to get the sum of all the items in the list. Because this is the only line in the code block, it automatically prints. But we could also save this to another variable, or print it by wrapping the whole thing with print()
.
What if we needed a boolean vector instead with true for even and false for odd. The syntax is slightly different. The value we want if our condition is met comes first (True), then our conditional logic, then our else value, and finally we tell Python what we are looping over.
Now you try. Create a list comprehension that adds up the ODD numbers of items in our item list. (Hint: the answer is 443.)
While Loops
While loops happen while some condition is true.
In a flow diagram while loops look like this:
We can do everything that we did above using a while loop with a few changes in syntax.
When you're using a while loop, you're not directly looping over an object, so you don't have the item. You have an index that you can use to fetch the item from the iterable. Let's see how that's done.
Oh oh. What happened? We were going along just fine and then bam, error. Can you see what the problem is?
Python indexes start from zero. Since we incremented our counter at the start of the loop, we started counting from one. We ran out of items in the array before we finished our loop.
We can easily fix this by moving our counter += 1
to the end of our loop.
Again, if we only wanted to total all our numbers, it works the same way as it did in the for loop, but we have to fetch the item to add.
Conditionally adding to the total works similarly, too - we're just fetching what to evaluate and add. Since we'd have to fetch the item twice in this code, it makes sense to set it as a local variable. This local variable will get rewritten each time we loop through the code.
More Complex Data
We can also loop over more complex data structures. In this course, we'll often loop over dictionaries with compound keys. Understanding how to access data in dictionaries of this type is important. It looks complex, but it's really exactly like what we've done before. The dictionary is an iterable, just like a simple list.
Let's start by creating a list of possible driving routes by making a list of tuples of 2 cities. Note: a tuple is a collection which is ordered and unchangeable. A tuple can work as a key in a dictionary, because we know we won't be changing anything in the tuple (messing up the key). https://www.w3schools.com/python/python_tuples.asp
Our list of cities doesn't do us much good without some additional information about the cities. What if we wanted to know distance and traffic level (light, medium, heavy). We could make 2 dictionaries and zip our additional information together with our tuple keys.
But, this is just a zip object, which isn't what we want. We really want a dictionary. We could do it in another step, or we could do it all in one step.
Let's do it all in one step for our traffic conditions. We'll nest the zip function inside a dict function.
If we want to loop over our route_distances dictionary, we use a for loop like we have before. But, this time, because our key is a tuple, we need to give Python a tuple to feed each iteration into. That's what we're doing with the (t, f) bit. We can pull back our value for each tuple by using bracket notation.
What if we knew our list of cities, but we weren't sure which cities were in our distances dictionary? We could loop over each possible combination of cities to find out.
We can also do the same thing by first determining all permutations of pairs of items in our city list, and then looping over the permutations.
Just like with lists, you can use comprehensions with dictionaries. Let's get the average distance of routes with Madison in the to or from. First we'll do it the long way:
Now let's do it with a comprehension. We'll use a regular list and roll our own function for determining the average of a list.
One final example. If what we want to do with our list comprehension is a little bit messy, we can also write a function that we use in the list comprehension. In this scenario, we want to create a new dictionary that holds the time to drive our routes. We know that, on average, people drive 70 mph on these roads (all our destinations are connected by interstates). But, if traffic is heavy, it takes approximately 10% longer to get there. We'll make a function that determines drive time and use it to generate a new dictionary of route drive times.