CSC108 Assignment 2: Simulating Canadian Elections
Due Date: Thursday March 6, 2025 before 4:00pm
Goals of this Assignment
· Develop code that uses loops, conditionals, and other earlier course concepts.
· Practice with lists, including looping over lists, using list methods, and list mutation.
· Practice reading problem descriptions written in English, together with provided docstring examples,and implementing function bodies to solve the problems.
· Practice reusing functions to help implement other functions.
· Continue to use Python 3, Wing 101, provided starter code, a checker module, and other tools.
Starter code
For this assignment, we are giving you some files, including some Python starter code files. See the Files to Download section below for details on how to download the files.
Background Information
Voting theory is the study of voting systems. A voting system is an algorithm for computing the winner of an election given a list of candidates and a set of ballots. For example, you may be familiar with the Plurality voting system: each voter marks their ballot for exactly one candidate, and the candidate with the most votes is elected.
There are dozens of different voting systems and many different types of ballots. In this assignment, we'll be investigating five systems (Plurality, Approval Voting, Range Voting, Borda Count, and Instant Run-Off) that use four different types of ballots.
In case you're curious, we have provided a table showing you where each voting system is used (https://q.utoronto.ca/courses/379470/pages/use-of-different-voting-systems) . (This is only for the curious since everything you need to know about each voting system and Canadian elections is contained within this handout.)
Canadian Elections: Ridings, Members of Parliament, and the House of Commons
Canada is divided into 338 geographical areas called ridings. In a standard Canadian election, one candidate from each political party runs in each riding. Each riding's voters elect one of these candidates to parliament using the Plurality voting system. These elected Members of Parliament (MPs) get a seat in the House of Commons. (So, there are 338 seats in the House of Commons.)
At present, there are five parties represented in the House of Commons; four of them put forward candidates in all provinces. To simplify the assignment somewhat, our simulation will includeonly those four parties:
· the Conservative Party of Canada (CPC) (http://www.conservative.ca)
· the Green Party (http://www.greenparty.ca)
· the Liberals (http://www.liberal.ca)
· the New Democratic Party (NDP) (http://www.ndp.ca)
For the purposes of the assignment, we will not differentiate candidates from their parties; Ballots will includeonly the party names.
Types of Ballots
Four types of ballots are used in this assignment. This section refers to the constant PARTY_ORDER, which is given a value in the Constants section of the voting_systems.py file.
The Data
For this assignment, we will provide starter code that reads voting data from a Comma Separated Value (CSV) file named sample_votes.csv. Each row of this file contains the following information about a single voter, with a comma between each part:
riding number: the number of the riding to which the voter belongs. (Many different voters will vote in the same riding.)
voter number: a number assigned to a voter. (No two voters in an election a riding have the same voter number.)
rank ballot: the voter's rank ballot with each party separated by the ; character.
range ballot: the voter's range ballot with each party's points separated by the ; character.
approval ballot: the voter's approval ballot with each party's approval/disapproval value separated by the ; character.
Notice that the row of data for each voter does not contain their single-candidate ballot. We will determine their single-candidate ballot from their top choice in their rank ballot.
Custom Data Type
Within our code, the data from each row in sample_votes.csv will be represented as a list that contains ints (for riding number and voter number) and sublists (for representing each of the rank, range and approval ballots). In voting_systems.py, we will refer to this data as VoteData in our type contracts. We use VoteData to represent type:
list[int, int, list[str], list[int], list[bool]].
Here is an example of a list[VoteData]:
[[0, 1, ['CPC', 'LIBERAL', 'NDP', 'GREEN'], [3, 1, 2, 1], [True, False, False, False]], [0, 2, ['LIBERAL', 'NDP', 'CPC', 'GREEN'], [2, 1, 3, 2], [False, False, True, False]], [1, 3, ['LIBERAL', 'NDP', 'GREEN', 'CPC'], [1, 2, 3, 3], [False, False, True, True]], [1, 4, ['LIBERAL', 'GREEN', 'NDP', 'CPC'], [1, 1, 2, 1], [False, False, True, False]], [1, 5, ['CPC', 'GREEN', 'NDP', 'LIBERAL'], [3, 2, 1, 2], [True, False, False, False]]]
Note that the 'YES' and 'NO' that appear in a CSV file are represented as True and False, respectively, in a type VoteData object.
Constants
We have provided the following constants in the starter code for you to use in your solutions. Read on to see how they should be used in your code.
Constants provided in the starter code file voting_systems.py
Files to Download
Please download the Assignment 2 Starter Files (a2.zip)
(https://q.utoronto.ca/courses/379470/files/36292538?wrap=1)
(https://q.utoronto.ca/courses/379470/files/36292538/download?download_frd=1) and extract the zip archive. A description of each of the files that we have provided is given in the paragraphs below.
Starter code: voting_systems.py
The voting_systems.py file contains some constants, some sample data, and function headers and docstrings for the functions you will write. For each function, read both the handout and the header and docstring (especially the examples) to understand what the function should do.
Data: sample_votes.csv
The sample_votes.csv file contains vote data in comma-separated values (CSV) format. You must not modify this file.
Note: do not call open, or read from this file, in your voting_systems.py solution. The file reading tasks are done by us in voting_simulation.py.
Simulation code: voting_simulation.py
The voting_simulation.py file will let you run a simulation that uses the code you write in voting_systems.py. You will not be submitting this file and there is no need to change it. It reads voting data from sample_votes.csv and passes the data as arguments to calls on your functions defined in voting_systems.py.
Checker: a2_checker.py, checker_generic.py and a2_pyta.json
These files provide a checker program that you should use to perform. a simple test of your code. See below in the section called CSC108 A2 Checker for more information about a2_checker.py. The checker program requires the files checker_generic.py and a2_pyta.json. You do not need to do anything with these files, other than keep them all in the same folder as your voting_systems.py file.
Tasks
Suppose you want to analyze how different voting systems change the results of elections. There are multiple voting systems to compare, and hundreds of votes and ridings to consider. To make your life easier, you will write Python functions to help you manage this information.
Docstrings, Preconditions, and Assumptions
You do not need to write any preconditions in this assignment, as we have provided the docstring descriptions for the required functions.
All function docstrings should include at least two examples. If you write your own helper functions, you should write complete docstrings for them. Docstrings, including the requirement to have two examples for each function, make up part of the style. marks.
To simplify the assignment, you can make the following assumptions:
There will only ever be 4 parties, and the party names will always be given in uppercase. Note that while the given PARTY_ORDER constant refers to a list that is sorted in alphabetical order, that may not always be the case. (We may test your code with PARTY_ORDER referring to a list of 4 parties given in a different order.)
If ties occur, they should be broken by choosing the party that comes first in PARTY_ORDER. We suggest you write your code ignoring ties at first, then test to see what happens and then think about how to implement this tie-breaking if necessary.
All lists representing a single rank, range, or approval ballot will have the same number of elements as PARTY_ORDER (that is, 4).
Task 0: Creating testing data
We have provided you with docstrings in the starter code in voting_system.py, however many of the docstrings only have one example. We have provided you with one sample list of VoteData called SAMPLE_DATA_1.
Create a second sample list called SAMPLE_DATA_2 that contains the 3 VoteData items representing the data of the voters in the following image (use the same order) :
We have started this off for you, setting SAMPLE_DATA_2 to be an empty list in the starter code.
Next, as you work through the following tasks, add a second example that uses SAMPLE_DATA_2 in any docstring that does not contain 2 examples.
Task 1: Data cleaning
The code we provided in voting_simulation.py reads data from a CSV file, separates out the commas, and produces a list[list[str]]. Here is a sample of the kind of list produced by our code:
[ ['0', '1', 'NDP;LIBERAL;GREEN;CPC', '1;4;2;3', 'NO;YES;NO;NO'], ['1', '2', 'LIBERAL;NDP;GREEN;CPC', '2;1;4;2', 'NO;NO;YES;YES'], ['1', '3', 'GREEN;NDP;CPC;LIBERAL', '1;5;1;2', 'NO;YES;NO;YES'] ]
You are to write the function clean_data, which should modify the provided list according to the following rules:
ridings should become int s
voter numbers should become int s
rank ballots should become a list of str s
range ballots should become a list of int s
approval ballots should become a list of bool s
Applying the clean_data function to the example list[list[str]] given above mutates it to contain this list[VoteData]:
[ [0, 1, ['NDP', 'LIBERAL', 'GREEN', 'CPC'], [1, 4, 2, 3], [False, True, False, False]], [1, 2, ['LIBERAL', 'NDP', 'GREEN', 'CPC'], [2, 1, 4, 2], [False, False, True, True]], [1, 3, ['GREEN', 'NDP', 'CPC', 'LIBERAL'], [1, 5, 1, 2], [False, True, False, True]] ]
Each of the three sublists above is of type VoteData.
You must not use the built-in function eval in clean_data.
This function is one of the more challenging functions in A2, because it mutates a list. We suggest that you start with some of the other functions in Task 2 and 3, and come back to this one later.
Data cleaning function to implement in voting_systems.py
Function
clean_data (list[list[str]]) -> None
Description
The parameter represents a nested list of strings in the format of data read from a file as described above.
Modify the nested list so that each sublist is of type VoteData.