{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Homework Assignment: Wonderlandia Election Simulation\n", "## Basics of Programming in Python (Fall'22)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Description \n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In October 2022, elections were finished in Wonderlandia. At stake were 400 seats in the local parliament, and each seat is allocated through a **parallel voting**. Half of the seats are elected by party-list proportional representation (PLPR) with a minimum of 10% electoral threshold of number of votes that enable the party to get seats. The other half elected in single-member constituencies by first-past-the-post voting (plurality voting).\n", "\n", "In this assigment, we offer you to code down an election process similar to Wonderlandia's one, with different voting systems (algorithms to compute an election's winner), including:\n", "* Single-Member Plurality Voting (first-past-the-post)\n", "* Borda count\n", "* Condorcet method" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Goals" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "During the Homework Assignment, you will:\n", "* prepare structurised report in Jupyter Notebook format that contain both detailed problem solving logic and its implementation with code;\n", "* practice in basics of Python (variables, sequences, control flows, etc.);\n", "* practice in stating conditions and and logical expressions from tasks of your assignment;\n", "* apply acquired programming skills for solving practical political science problem." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Organizational stuff" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Separate links to submit [Part I](http://www.linksaredeleted.com/) and [Part II](http://www.linksaredeleted.com/)\n", "\n", "* You should download both parts of homework as `.ipynb` files to the corresponding section of LMS system before the deadline.\n", " * When uploading multiple solutions, the last one before the deadline will be considered final (but the previous ones can help with the appeal). \n", " * There are penalties for submitting your work after the deadline. They depend on how many hours late your submission are.\n", "* **Any type of cheating will be penalized**. For example:\n", " * Non-compilable code won't be considered for the assessment.\n", " * Any type of copy-pasting part or all of the notebook will cause 0 both to subject and object of copy-pasting.\n", " * We are not against helping each other: we are against blind reprinting.\n", "\n", "Don’t hesitate to contact me or teaching assistants if you have any questions! 🙂\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Part I\n", "\n", "**Deadline: October 23, 11:59 pm**\n", "\n", "In this part, you play the role of the Election Сommittee’s analyst. You need to prepare collected bulletins for consequent counting: check ballots for spoilage, select candidates who passed the minimum threshold, and compile a report on preliminary analysis.\n", "\n", "There are 7 parties in our country - here's General List of Parties:\n", "1. Harmony Party\n", "2. Red Party \n", "3. Justice and Truth\n", "4. Adventure Alliance\n", "5. Animal Friends Party\n", "6. Go Greens!\n", "7. Yo-ho-ho Pirate Party\n", "\n", "And there are two types of bulletins:\n", "1. ones arranged in descending order of candidate priority, e.g.: \n", "`['Go Greens!', 'Animal Friends Party', 'Adventure Alliance', 'Yo-ho-ho Pirate Party', 'Justice and Truth', 'Harmony Party', 'Red Party']` - Go Greens! are the first choice here. Some ballot of this type may has different number of parties in it - it means that the voter simply did not choose this party (did not vote for it). \n", "2. with ranked positions according to General List of Parties, e.g.:\n", "`[7, 5, 2, 1, 4, 6, 3]` - Here the Adventure Alliance has the first priority for electioneer as it's position (index `[3]`) marked with 1st place. These type of ballots could contain zeroes (meaning the voter is not interested in choosing this party); hence, there should be 7 values in the ballot." ] }, { "cell_type": "code", "execution_count": 284, "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "general_list_of_parties = ['Harmony Party', 'Red Party', 'Justice and Truth', 'Adventure Alliance', 'Animal Friends Party', 'Go Greens!', 'Yo-ho-ho Pirate Party']" ] }, { "cell_type": "code", "execution_count": 285, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "13159\n" ] } ], "source": [ "from csv import reader \n", "\n", "# read csv and delete empty rows\n", "with open('data/votes.csv') as csv_file:\n", " csv_reader = reader(csv_file)\n", " votes = [x for x in csv_reader if x != []]\n", "\n", "# def int_or_id(num):\n", "# \"\"\"Convert to int if possible, otherwise keep the original.\"\"\"\n", "# try:\n", "# return int(num)\n", "# except:\n", "# return num\n", "\n", "# # convert numbers to int\n", "# votes = map(int_or_id, votes)\n", "\n", "print(len(votes))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Tasks 1.1-1.2\n", "\n", "All votes are stored together into one object. Separate this object to two containers according to bulletin type: save all bulletins with names in it to `votes_names`, others - to `votes_ranks`. After aggregating, display the sentence with lengths of each sublist using **string formatting**.\n", "\n", "*example output:* `There are 1234 ballots with names and 1111 ballots with rankings.`" ] }, { "cell_type": "code", "execution_count": 286, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "There are 5973 ballots with names and 7186 ballots with rankings.\n" ] } ], "source": [ "# 1.1-1.2 answers\n", "# create two new empty lists\n", "votes_names = []\n", "votes_ranks = []\n", "\n", "# Sort out the ballots in `votes` to new lists, one by one\n", "for vote in votes:\n", " try:\n", " # try converting to int\n", " vote = map(int, vote)\n", " votes_ranks.append(vote)\n", " except ValueError:\n", " # int conversion failed, must be a named vote\n", " votes_names.append(vote)\n", "\n", "\n", "# Display the message\n", "print(f\"There are {len(votes_names)} ballots with names and {len(votes_ranks)} ballots with rankings.\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Task 1.3-1.4\n", "\n", "Now we need to check our new lists for correctness. Let's begin with `votes_ranks`: display the **minimum**, **maximum** and **average** (rounded to three decimal places) length of ballot in this list.\n", "\n", "*example output:* \n", "`Minimum ballot length: 3, ` \n", "`Average ballot length: 4.205,` \n", "`Maximum ballot length: 5.`" ] }, { "cell_type": "code", "execution_count": 287, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Minimum ballot length: 7, \n", "Average ballot length: 7.004, \n", "Maximum ballot length: 8.\n" ] } ], "source": [ "# 1.3-1.4 answers\n", "\n", "list_of_len = [len(v) for v in votes_ranks]\n", "print(\n", " f\"Minimum ballot length: {min(list_of_len)},\\n\"\n", " f\"Average ballot length: {sum(list_of_len)/len(list_of_len):.3f},\\n\"\n", " f\"Maximum ballot length: {max(list_of_len)}.\"\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Discuss: what this statistic says about this type of ballot?\n", "\n", "***Answer:** write your answer here* " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Task 1.5-1.7\n", "\n", "We know that some ballots in `votes_ranks` can contain zeros, which means voter did not include this particular party to her vote. This leads us to the following conclusions about the requirements for a valid bulletin:\n", "\n", "1. All ballots have the same length equal to the number of parties;\n", "1. As we have 7 parties in the race, the maximum place is 7th;\n", "2. There should be at least 1 vote in each ballot (meaning no ballots like `[0, 0, 0, 0, 0, 0, 0]`). \n", "\n", "Check all three requirements in following code cells: **delete the ballot from the list if it failed to meet one of requirements**. Additionally you need to **count how many ballots were removed** after the check and display this number with some message like *\"Number of incorrect ballots\"* . " ] }, { "cell_type": "code", "execution_count": 288, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Number of incorrect ballots: 101\n" ] } ], "source": [ "# 1.5-1.7 answer \n", "incorrect_ranks = 0\n", "\n", "votes_ranks_cleared = []\n", "\n", "# Check that the requirements are met\n", "for i in votes_ranks:\n", " if len(i) != 7 or max(i) > 7 or sum(i) < 1:\n", " incorrect_ranks += 1\n", " else:\n", " votes_ranks_cleared.append(i)\n", "\n", "# Display the message below\n", "print(\"Number of incorrect ballots:\", incorrect_ranks)\n", "\n", "# duplicate ranks not checked? consecutive ranks?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Tasks 1.8-1.11\n", "\n", " Now let's return to our `votes_names` list. There are some requirements too:\n", "\n", "1. The number of items on the ballot should not exceed the number of parties in the general list of parties;\n", "2. At least one party should be chosen by each voter.\n", "\n", "Check all three requirements in following code cells: **delete the ballot from the list if it failed to meet one of requirements**. Additionally you need to **count how many ballots were removed** after the check and display this number with some message like *\"Number of incorrect ballots\"* (task 1.6). \n" ] }, { "cell_type": "code", "execution_count": 289, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Number of incorrect bulletins: 58\n" ] } ], "source": [ "# 1.8-1.11 answer \n", "incorrect = 0\n", "\n", "# Check that the requirements 1-2 are met (length of ballot)\n", "votes_names_cleared = []\n", "\n", "for i in votes_names:\n", " if 1 <= len(i) <= 7:\n", " votes_names_cleared.append(i)\n", " else:\n", " incorrect += 1\n", "\n", "# Display the message below\n", "print(\"Number of incorrect ballots:\", incorrect)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**\\*Advanced task**. Complete tasks 1.8-1.11 with [list comprehensions](https://www.w3schools.com/python/python_lists_comprehension.asp)" ] }, { "cell_type": "code", "execution_count": 290, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Number of incorrect bulletins: 58\n" ] } ], "source": [ "votes_names_cleared = [\n", " vote \n", " for vote in votes_names \n", " if 1 <= len(vote) <= 7\n", "]\n", "# votes_names_cleared = [vote for vote in votes_names for party in vote if party in general_list_of_parties]\n", "\n", "print(\"Number of incorrect ballots:\", len(votes_names) - len(votes_names_cleared))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Task 1.12-1.13\n", "\n", "It is uncomfortable to store data in two different formats. Let's: \n", "1. convert `votes_ranks` to `votes_names` format; \n", "2. bind this new list with already existing names ballots;\n", "3. display the message of total number of valid ballots.\n", "\n", "*example output:* \n", "`Total number of valid ballots: 2500` \n", "\n", "How to convert: use `general_list_of_parties` to recall the order of parties in the ballot, e.g.: \n", "`[0,0,1,0,3,0,2]` -> `['Justice and Truth', 'Yo-ho-ho Pirate Party', 'Animal Friends Party']` \n", "\n", "*Hint: to use this, you need to combine [this piece of code](https://stackoverflow.com/a/6618543/10803427) with some condition for zeros*" ] }, { "cell_type": "code", "execution_count": 291, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "13000" ] }, "execution_count": 291, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(votes_ranks_cleared) + len(votes_names_cleared)" ] }, { "cell_type": "code", "execution_count": 292, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Total number of valid ballots: 13000\n" ] } ], "source": [ "# 1.12-1.13 answer \n", "\n", "# create a new list\n", "newlist = []\n", "\n", "# fill the gaps in the code\n", "for vote in votes_ranks_cleared:\n", " newlist.append([\n", " name \n", " for rank, name in sorted(zip(vote, general_list_of_parties)) \n", " if rank != 0\n", " ])\n", "\n", "# bind two lists together\n", "\n", "votes_names_cleared.extend(newlist)\n", "\n", "# display the message\n", "print(f\"Total number of valid ballots: {len(votes_names_cleared)}\") " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Tasks 1.14-1.15\n", "\n", "Now we need to check all parties for passing the required threshold. As all ballots in our country are ranked one, we can expect many parties to be named in different ballots (the sum of probabilities wont be equal to 1, as it is in one-choice elections), so let's set a 40% passing threshold. \n", "1. Create the list of `passed_parties`; \n", "2. Calculate the share of votes for each party separately. If the party has not crossed the threshold, it won't be represented in succeed list; \n", "3. Display a list of succeed parties along with the corresponding message.\n", "\n", "example output: `['Justice and Truth', 'Animal Friends Party', 'Go Greens!', 'Yo-ho-ho Pirate Party'] passed the 40% threshold!`" ] }, { "cell_type": "code", "execution_count": 293, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['Harmony Party', 'Red Party', 'Justice and Truth', 'Adventure Alliance', 'Animal Friends Party'] passed the 40% threshold!\n" ] } ], "source": [ "# 1.14-1.15 answer # 40% doesn't match the initial text\n", "\n", "# ranked voting doesn't usually have thresholds, since it's a majority-based system\n", "\n", "# create a dictionary where you will count occurrences\n", "number_of_votes = {}\n", "for party in general_list_of_parties: \n", " number_of_votes[party] = 0\n", "\n", "# number_of_votes = { party:0 for party in general_list_of_parties }\n", "\n", "# create a list of passed parties\n", "passed_parties = []\n", "\n", "# calculate total votes for each party\n", "for vote in votes_names:\n", " for party in vote:\n", " number_of_votes[party] += 1 # vote.count(party) # DOUBLE COUNTING!\n", "\n", "# calculate the share of each party: if passed, append to the list of passed_parties\n", "for party, votes in number_of_votes.items():\n", " fraction = votes / len(votes_names)\n", " if fraction >= 0.4:\n", " passed_parties.append(party)\n", "\n", "# display the message\n", "print(passed_parties, \"passed the 40% threshold!\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**\\*Advanced task**. Complete tasks 1.14-1.15 with dict and list comprehensions." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Task 1.16\n", "\n", "Preparations are almost complete. It remains to eliminate the parties that did not pass from the ballots.\n", "\n", "1. Create a list of failed parties and display it\n", "2. Remove these parties from ballots.\n", "\n", "*example output:* \n", "`['Harmony Party', 'Red Party', 'Adventure Alliance'] failed to pass the 5% threshold`" ] }, { "cell_type": "code", "execution_count": 294, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['Go Greens!', 'Yo-ho-ho Pirate Party'] failed to pass the 40% threshold\n" ] } ], "source": [ "# task 1.16\n", "\n", "# create a list of failed parties\n", "failed_parties = []\n", "\n", "# append failed parties to the list\n", "for party in general_list_of_parties:\n", " if party not in passed_parties:\n", " failed_parties.append(party)\n", "\n", "# failed_parties = set(general_list_of_parties) - set(passed_parties)\n", "\n", "# display the message \n", "print(failed_parties, \"failed to pass the 40% threshold\")\n", "\n", "# remove them from ballots\n", "final_votes = []\n", "\n", "for vote in votes_names:\n", " new_vote = [party for party in vote if party not in failed_parties]\n", " final_votes.append(new_vote)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, when all the votes are cleared and prepared, we are ready to start the count!\n", "\n", "This is the end of Part I of the Homework Assignment. Make sure that all code cells in your notebook are executable and without Errors, and submit your homework via [SmartLMS](https://edu.hse.ru/mod/assign/view.php?id=613078). " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Part II\n", "\n", "**Deadline: October 30, 11:59 pm**\n", "\n", "For the second part of your homework, you need to calculate the winner of election with different voting techniques. All descriptions were taken from the ***Voting Methods*** paper from [Stanford Encyclopedia of Philosophy Archive](https://plato.stanford.edu/archives/fall2019/entries/voting-methods/#toc).\n", "\n", "We will store each winner to the `final_results` dictionary to eventually compare all methods between one another." ] }, { "cell_type": "code", "execution_count": 295, "metadata": {}, "outputs": [], "source": [ "# Don't forget to run this code line!\n", "final_results = dict()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Single-Member Plurality Voting (first-past-the-post)\n", "\n", "Plurality rule (also called First-Past-the-Post rule) is the simplest method of votes counting, which is widely used despite its known problems.\n", "\n", "> Description: \n", ">\n", "> Each voter selects one candidate (or none if voters can abstain), and the candidate(s) with the most votes win." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To count with first-past-the-post voting method, recall which object stores counts of favourite candidates. \n", "1. Count number of 1st votes all over the ballots we have. Store results in some dictionary with party name as key and number of votes as value; \n", "2. Rearrange the dictionary with counted votes in a descending order by number of votes; \n", "4. Save the winner to `final_results` dictionary; \n", "3. Print a short news message announcing the results of the election (use string formatting to display name and number of votes, no copypasta here): \n", "\n", "*example output:* \n", "`The results of the last election: ` \n", "`According to the first-past-the-post voting system, Yo-ho-ho Pirate Party won with 9999 votes!`" ] }, { "cell_type": "code", "execution_count": 296, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The results of the last election: \n", "According to the first-past-the-post voting system, Harmony Party won with 1252 votes!\n" ] } ], "source": [ "# Count 1st votes\n", "firsts = [vote[0] for vote in final_votes]\n", "\n", "pluralism = {party: firsts.count(party) for party in passed_parties}\n", "\n", "pluralism = sorted(pluralism.items(), key=lambda item: item[1], reverse=True)\n", "\n", "# Display the message\n", "print(f\"The results of the last election: \\nAccording to the first-past-the-post voting system, {pluralism[0][0]} won with {pluralism[0][1]} votes!\")\n", "\n", "# Save the result\n", "final_results['pluralism'] = pluralism[0][0]" ] }, { "cell_type": "code", "execution_count": 297, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[('Harmony Party', 1252),\n", " ('Justice and Truth', 1243),\n", " ('Adventure Alliance', 1166),\n", " ('Red Party', 1163),\n", " ('Animal Friends Party', 1149)]" ] }, "execution_count": 297, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pluralism" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Borda Count\n", "\n", "Borda Counting is the most popular instance of ranking methods. Despite they are much more expensive than selecting a single candidate, they demonstrate more fair calculations.\n", "\n", "> Description: \n", ">\n", ">Each voter provides a ranking of the candidates. Then, a score (the Borda score) is assigned to each candidate by a voter as follows: If there are n candidates, give n−1 points to the candidate ranked first, n−2 points to the candidate ranked second,…, 1 point to the candidate ranked second to last and 0 points to candidate ranked last." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To complete Borda count, you need:\n", "1. Create new dict to store Borda scores;\n", "2. Increase the score of party according to its position in ballot; \n", "3. Rearrange the dictionary with counted votes in a descending order by number of votes; \n", "4. Save the Borda winner to `final_results` dictionary; \n", "6. Print a short news message announcing the results of the election (use string formatting to display name and number of votes, no copypasta here either): \n", "\n", "*example output:* \n", "`The results of the last election: ` \n", "`According to the Borda system, Yo-ho-ho Pirate Party won with 9999 votes!`\n", "\n", "*Note: as here we range all the candidates from the ballot, we need to determine how many parties were included by voter. This variable could also be used as a descending counter for Borda scores.*" ] }, { "cell_type": "code", "execution_count": 298, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The results of the last election: \n", "According to the Borda system, Justice and Truth won with 9222 votes!\n" ] } ], "source": [ "# Create a dict for borda scores\n", "borda = {party: 0 for party in passed_parties}\n", "\n", "# Calculate number of votes for each party according to Borda rule\n", "for vote in final_votes: \n", " k = len(passed_parties)-1\n", " for party in vote:\n", " borda[party] += k\n", " k -=1\n", "\n", "# Sort this dict by value in descending order\n", "borda = sorted(borda.items(), key=lambda item: item[1], reverse=True)\n", "\n", "# Display the message\n", "print(f\"The results of the last election: \\nAccording to the Borda system, {borda[0][0]} won with {borda[0][1]} points!\")\n", "\n", "# Save the result\n", "final_results['borda'] = borda[0][0]" ] }, { "cell_type": "code", "execution_count": 299, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[('Justice and Truth', 9222),\n", " ('Harmony Party', 9180),\n", " ('Red Party', 9016),\n", " ('Adventure Alliance', 8824),\n", " ('Animal Friends Party', 8808)]" ] }, "execution_count": 299, "metadata": {}, "output_type": "execute_result" } ], "source": [ "borda" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Condorcet Count\n", "\n", "The idea of Condorcet voting is to make a set of one-on-one electons. The candidate that beat all others in one-by-one wins the election.\n", "\n", "> Description: \n", ">\n", "> Given a ranking from each voter, the majority relation orders the candidates in terms of how they perform in one-on-one elections. A candidate is called the Condorcet winner in an election scenario if she has the maximum of the majority ordering for that election scenario. The Condorcet loser is the candidate that is the minimum of the majority ordering." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's wrap up the idea of Condorcet winner in programming logic:\n", "\n", "1. Create new dict to store all pairwise comparisons;\n", "2. Calculate the share of ballots in which one candidate beats antoher, and store the share of such cases to corresponding slot.\n", "3. Save the Condorcet winner to `final_results` dictionary; \n", "4. Print a short news message announcing the results of the election (use string formatting to display name of party, no copypasta here either): \n", "\n", "*example output:* \n", "`The results of the last election: ` \n", "`According to the Condorcet system, Yo-ho-ho Pirate Party won!`\n", "\n", "*Note:*" ] }, { "cell_type": "code", "execution_count": 300, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "data": { "text/plain": [ "[{'Harmony Party', 'Red Party'},\n", " {'Harmony Party', 'Justice and Truth'},\n", " {'Adventure Alliance', 'Harmony Party'},\n", " {'Animal Friends Party', 'Harmony Party'},\n", " {'Justice and Truth', 'Red Party'},\n", " {'Adventure Alliance', 'Red Party'},\n", " {'Animal Friends Party', 'Red Party'},\n", " {'Adventure Alliance', 'Justice and Truth'},\n", " {'Animal Friends Party', 'Justice and Truth'},\n", " {'Adventure Alliance', 'Animal Friends Party'}]" ] }, "execution_count": 300, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Create an empty list of parvise combinations\n", "combinations = []\n", "\n", "# Store all possible pairs in this list as set of 2 party names\n", "for party1 in passed_parties:\n", " for party2 in passed_parties:\n", " combination = {party1, party2}\n", " # skip the iteration if there's only one party in set or the combination already exists\n", " if (len(combination) == 1) or (combination in combinations):\n", " continue\n", " # append combination to the list of combinations\n", " combinations.append(combination)\n", "\n", "combinations\n", "\n", "# use itertools!\n", "# combinations = itertools.combinations(passed_parties, 2)" ] }, { "cell_type": "code", "execution_count": 301, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['Adventure Alliance',\n", " 'Harmony Party',\n", " 'Justice and Truth',\n", " 'Animal Friends Party',\n", " 'Red Party']" ] }, "execution_count": 301, "metadata": {}, "output_type": "execute_result" } ], "source": [ "final_votes[0]" ] }, { "cell_type": "code", "execution_count": 302, "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Harmony Party beats Red Party\n", "Justice and Truth beats Harmony Party\n", "Harmony Party beats Adventure Alliance\n", "Harmony Party beats Animal Friends Party\n", "Justice and Truth beats Red Party\n", "Red Party beats Adventure Alliance\n", "Red Party beats Animal Friends Party\n", "Justice and Truth beats Adventure Alliance\n", "Justice and Truth beats Animal Friends Party\n", "Adventure Alliance beats Animal Friends Party\n" ] } ], "source": [ "non_losers = set(passed_parties)\n", "\n", "for combination in combinations:\n", " party_0, party_1 = combination \n", "\n", " # create a counter with 0 value\n", " counter = 0\n", "\n", " # NEGATIONS UPON NEGATIONS MAKE TRACING HARD\n", " # calculate which party has the biggest priority for each ballot\n", " for vote in final_votes:\n", " if party_0 in vote:\n", " candidate_0_score = vote[::-1].index(party_0)\n", " else:\n", " candidate_0_score = 0\n", " if party_1 in vote:\n", " candidate_1_score = vote[::-1].index(party_1)\n", " else:\n", " candidate_1_score = 0\n", "\n", " # move counter correspondingly\n", " if candidate_0_score > candidate_1_score:\n", " counter += 1\n", " elif candidate_1_score > candidate_0_score:\n", " counter -= 1\n", " else:\n", " continue\n", "\n", " # display the pairwise winner with correct counter conditions\n", " if counter > 0:\n", " print(f\"{party_0} beats {party_1}\")\n", " if party_1 in non_losers:\n", " non_losers.remove(party_1) \n", " elif counter < 0:\n", " print(f\"{party_1} beats {party_0}\")\n", " if party_0 in non_losers:\n", " non_losers.remove(party_0) \n", " else:\n", " print(f\"There is no winner between {party_0} and {party_1}\")\n", "\n", "final_results['condorcet'] = list(non_losers)[0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Summary\n", "\n", "**\\*Advanced task:** to wrap-up the assignment, display the `final_results` dictionary of the winners by different methods. Which method seemed the fairest to you? What are the advantages and disadvantages of each method? Using the scientific literature, write a short, detailed answer (200 words minimum). Don't forget to cite references." ] }, { "cell_type": "code", "execution_count": 303, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'pluralism': 'Harmony Party',\n", " 'borda': 'Justice and Truth',\n", " 'condorcet': 'Justice and Truth'}" ] }, "execution_count": 303, "metadata": {}, "output_type": "execute_result" } ], "source": [ "final_results" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "***Answer:** write your answer here* " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is the end of Part II of the Homework Assignment. Make sure that all code cells in your notebook are executable and without Errors, and submit your homework via [LMS](http://www.linksaredeleted.com/). " ] } ], "metadata": { "kernelspec": { "display_name": "Python 3.10.4 64-bit", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.4" }, "orig_nbformat": 4, "vscode": { "interpreter": { "hash": "db967a82a5395c9886e36287b4fc3d4cdf41cf28b68e7cdaae12bdbf290d326d" } } }, "nbformat": 4, "nbformat_minor": 2 }