GEOG 485:
GIS Programming and Automation

Lesson 4 Practice Exercise B Solution


This practice exercise is a little trickier than previous exercises. If you were not able to code a solution, study the following solution carefully and make sure you know the purpose of each line of code.

The code below refers to the "winner" and "loser" of each game. This really refers to the first score given and the second score given, in the case of a tie.

# Reads through a text file of soccer (football)
#  scores and reports the highest number of goals
#  in one game for each team
# ***** DEFINE FUNCTIONS *****
# This function checks if the number of goals scored
#  is higher than the team's previous max.
def checkGoals(team, goals, dictionary):
    #Check if the team has a key in the dictionary
    if team in dictionary:
        # If a key was found, check goals against team's current max
        if goals > dictionary[team]:
            dictionary[team] = goals
    # If no key found, add one with current number of goals
        dictionary[team] = goals
# ***** BEGIN SCRIPT BODY *****

import csv
# Open the text file of scores
scoresFilePath = "C:\\Data\\Geog485\\Scores.txt"
scoresFile = open(scoresFilePath)
# Read the header line and get the important field indices
csvReader = csv.reader(scoresFile, delimiter=" ")
header =

winnerIndex = header.index("Winner")
winnerGoalsIndex = header.index("WG")
loserIndex = header.index("Loser")
loserGoalsIndex = header.index("LG")

# Create an empty dictionary. Each key will be a team name.
#  Each value will be the maximum number of goals for that team.
maxGoalsDictionary = {}

for row in csvReader:

    # Create variables for all items of interest in the line of text    
    winner = row[winnerIndex]
    winnerGoals = row[winnerGoalsIndex]
    loser = row[loserIndex]
    loserGoals = row[loserGoalsIndex]
    # Check the winning number of goals against the team's max
    checkGoals(winner, winnerGoals, maxGoalsDictionary)
    # Also check the losing number of goals against the team's max    
    checkGoals(loser, loserGoals, maxGoalsDictionary)
# Print the results
for key in maxGoalsDictionary:
    print key + ": " + maxGoalsDictionary[key]

Below is a video offering some line-by-line commentary on the structure of this solution. Please note two errors in the video: 1) At one point I said "line 30" when I meant "line 31". 2)  I used the word "tie" to refer to the scenario when both teams end up with the same number of goals, when I know you football fans will be quickly reminding me that I should have called this a "draw".  :-)

Click for a transcript of "4B" video.

This video is going to describe one possible solution for lesson 4 practice exercise B wherein you are reading the names of soccer teams in Buenos Aires, and looking at their scores, and then compiling a report about the top number of goals scored by each team over the course of this time covered by this file.

In order to maintain all this information, it's helpful to use a dictionary, which is a way of storing information in the computer's memory based on key value pairs. So the key in this case will be the name of the team, and the value will be the maximum number of goals found for that team as we read the file line by line. Now, this file is sort of like a comma separated value file, although the delimiter in our case is a space. The file does have a header, which we'll use to pull out information.

The header is organized in terms of winner, winning goals, loser, losing goals. Although really, there are some ties in here. So we might say first score and second score rather than winner and loser. For our purposes, it doesn't matter who won or lost, because the maximum number of goals might have come during a loss or a tie.

So this solution is a little more complex than some of the other practice exercises. It involves a function which you can see beginning in line 9, but I'm not going to describe this just yet. I'll wait until we get to that point where we need the logic that's in that function. So I'll start explaining this solution by going to line 23, where we import the Python CSV module.

Now, there's nothing in this script that uses ArcGIS or ArcPy geometries or anything like that, so I don't import ArcPy at all. But you will do it in Project 4 where you'll use a combination of the techniques used here along with ArcGIS geometries and really put everything together from both the practice exercises. In line 26, we set up a variable representing the path to that text file. And in line 27, we actually open the file. By default, it opens here in read mode. The read parameter is not specifically supplied here.

In line 30, we create the CSV reader object. You should be familiar with this from the other examples in the lesson and the other practice exercise. The one thing that's different here is, as a second parameter, we can specify the delimiter using this type of syntax-- delimiter equals, and then a space. This file does have a header. So in line 30, we'll read the header, and then we figure out the index positions of all of the columns in the file. That's what's going on in lines 33 through 36.

Now, we know that these are in the order 0, 1, 2, 3 in position. But writing it in this way where we use the header.index method makes the script a little more flexible in case the column order had been shifted around by somebody, which could easily happen if somebody had previously opened this file in a spreadsheet program and moved things around. In line 40, we're going to create a blank dictionary to keep track of each team and the maximum number of goals they've scored. We'll refer to that dictionary frequently as we read through the file.

In line 42, we begin a loop that actually starts reading the file row by row after the header. And so lines 45 through 48 are pulling out those four pieces of information-- basically, the two team names and the number of goals that each scored. Now, when we get a team name and a number of goals, we need to check it against our dictionary to see if the number of goals scored is greater than that team's max. And we need to do this check for both the winner and the loser-- or in other words, the first team and the second team listed in the row.

To avoid repeating code, this is a good case for a function. Because we're going to use the same logic for both pieces. Why not write the code just once in a function? So in lines 51 and 54, you'll see that I'm invoking a function called Check Goals. And I pass in three things. I pass in the team name, I pass in the number of goals, and the dictionary.

This function is defined up here in line 9. So I've created a function called Check Goals, and I create variables here for those three things that I know will be passed in-- the team, the number of goals, and the dictionary. Line 11 performs a check to see if the team already has a key in the dictionary. If it does, then we need to look at the number of goals or max goals that have been recorded for the team and check it against the current score that we are reading to see if that maximum number of goals needs to be updated.

So in line 13, that check is occurring. And if indeed the number of goals being read in the current line is greater than the maximum that we know about, then in line 14, we update the maximum and set it equal to what we read within the line. If the number of goals in the current line is not greater than our maximum, then we don't want to do anything. So that's what's in line 16 where it says pass. The pass is just a keyword that means don't do anything here.

Now, if the team has never been read before and it doesn't have an entry in the dictionary for a maximum number of goals, then we're going to actually go down to line 19 and just set the team. We're going to give the team a key in the dictionary, and we're going to set its max goals to whatever we have now. Because in this case, we've only read one record for that team, so the maximum is the one value that we've read.

Now, if this hasn't confused you yet, what we could do is put in a break point and look at this in the debugger. And this will hopefully help you to get a visual feel for what's going on in this dictionary. So I'm going to play this script up until the first time this function gets called. And over here, I've set little watches on some of these variables-- team, goals, and dictionary.

So the first line in this file, you will see that Boca scored two goals and Independiente scored one. So the first time we call this function, we're just going to pass in Boca as the team, two as the number of goals. And right now, there's nothing in our dictionary. So what would you expect to happen when you evaluated line 11?

In our case, we're going down to line 19, because the team does not have a key in the dictionary yet. So we're going to add a key for the team Boca and record two goals for them in the dictionary. And you'll see that our dictionary has changed over here to where Boca has two.

Now if we go ahead and play this again, the next one it's going to evaluate is Independiente with one. Again, because Independiente does not have a key in the dictionary, we'd expect this to go down to line 19. And indeed, that's what happens if we play this out. The next one is Rossing with one goal.

And so as we iterate through this the first few times-- we're heading down to line 19 just to add these keys. But as we get into later runs of this, we'll start seeing the same teams over again. So here's a case as we add a few more where, in the current line, River has three goals, and in the dictionary, their maximum is two. So what would you expect to happen when we evaluate line 11?

In this case, because River a already has a key, we're going to go to line 13, and we'll check to see if goals-- which is three-- is greater than their current maximum, which is two. And indeed it is, so we invoke line 14. And watch carefully. The dictionary here performs the update when we go to the next line. So as you're working on Project 4 and you're working with dictionaries in this manner, please keep the debugger open and watch what's happening, and you should be able to tell if your dictionary is being updated in the way that you expect.

So to finish out this script, once we have our dictionary all built, then we're going to loop through it one last time and print each key and each value. And that can be done using a simple for loop, like in line 57. In this case, the variable key represents a key in the dictionary. And in line 58, we print out that key, and we print a colon and a space, and then we print the associated value for that key. If you want to pull a value out of a dictionary, you use square brackets and you pass in the key name. And so that's where we're doing there. So running this all the way through should produce a printout in your interactive window of the different teams as well as the maximum number of goals found for each.