intro-to-python/09_mappings/01_exercises_solved.ipynb

689 lines
21 KiB
Text

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_mb.png\">](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/01_exercises.ipynb)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Chapter 9: Mappings & Sets (Coding Exercises)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The exercises below assume that you have read the [first part <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/00_content.ipynb) of Chapter 9.\n",
"\n",
"The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Working with Nested Data"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's write some code to analyze the historic soccer game [Brazil vs. Germany <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Brazil_v_Germany_%282014_FIFA_World_Cup%29) during the 2014 World Cup.\n",
"\n",
"Below, `players` consists of two nested `dict` objects, one for each team, that hold `tuple` objects (i.e., records) with information on the players. Besides the jersey number, name, and position, each `tuple` objects contains a `list` object with the times when the player scored."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"players = {\n",
" \"Brazil\": [\n",
" (12, \"Júlio César\", \"Goalkeeper\", []),\n",
" (4, \"David Luiz\", \"Defender\", []),\n",
" (6, \"Marcelo\", \"Defender\", []),\n",
" (13, \"Dante\", \"Defender\", []),\n",
" (23, \"Maicon\", \"Defender\", []),\n",
" (5, \"Fernandinho\", \"Midfielder\", []),\n",
" (7, \"Hulk\", \"Midfielder\", []),\n",
" (8, \"Paulinho\", \"Midfielder\", []),\n",
" (11, \"Oscar\", \"Midfielder\", [90]),\n",
" (16, \"Ramires\", \"Midfielder\", []),\n",
" (17, \"Luiz Gustavo\", \"Midfielder\", []),\n",
" (19, \"Willian\", \"Midfielder\", []),\n",
" (9, \"Fred\", \"Striker\", []),\n",
" ],\n",
" \"Germany\": [\n",
" (1, \"Manuel Neuer\", \"Goalkeeper\", []),\n",
" (4, \"Benedikt Höwedes\", \"Defender\", []),\n",
" (5, \"Mats Hummels\", \"Defender\", []),\n",
" (16, \"Philipp Lahm\", \"Defender\", []),\n",
" (17, \"Per Mertesacker\", \"Defender\", []),\n",
" (20, \"Jérôme Boateng\", \"Defender\", []),\n",
" (6, \"Sami Khedira\", \"Midfielder\", [29]),\n",
" (7, \"Bastian Schweinsteiger\", \"Midfielder\", []),\n",
" (8, \"Mesut Özil\", \"Midfielder\", []),\n",
" (13, \"Thomas Müller\", \"Midfielder\", [11]),\n",
" (14, \"Julian Draxler\", \"Midfielder\", []),\n",
" (18, \"Toni Kroos\", \"Midfielder\", [24, 26]),\n",
" (9, \"André Schürrle\", \"Striker\", [69, 79]),\n",
" (11, \"Miroslav Klose\", \"Striker\", [23]),\n",
" ],\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q1**: Write a dictionary comprehension to derive a new `dict` object, called `brazilian_players`, that maps a Brazilian player's name to his position!"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"brazilian_players = {name: position for _, name, position, _ in players[\"Brazil\"]}"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'Júlio César': 'Goalkeeper',\n",
" 'David Luiz': 'Defender',\n",
" 'Marcelo': 'Defender',\n",
" 'Dante': 'Defender',\n",
" 'Maicon': 'Defender',\n",
" 'Fernandinho': 'Midfielder',\n",
" 'Hulk': 'Midfielder',\n",
" 'Paulinho': 'Midfielder',\n",
" 'Oscar': 'Midfielder',\n",
" 'Ramires': 'Midfielder',\n",
" 'Luiz Gustavo': 'Midfielder',\n",
" 'Willian': 'Midfielder',\n",
" 'Fred': 'Striker'}"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"brazilian_players"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q2**: Generalize the code fragment into a `get_players()` function: Passed a `team` name, it returns a `dict` object like `brazilian_players`. Verify that the function works for the German team as well!"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"def get_players(team):\n",
" \"\"\"Creates a dictionary mapping the players' names to their position.\"\"\"\n",
" return {name: position for _, name, position, _ in players[team]}"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'Manuel Neuer': 'Goalkeeper',\n",
" 'Benedikt Höwedes': 'Defender',\n",
" 'Mats Hummels': 'Defender',\n",
" 'Philipp Lahm': 'Defender',\n",
" 'Per Mertesacker': 'Defender',\n",
" 'Jérôme Boateng': 'Defender',\n",
" 'Sami Khedira': 'Midfielder',\n",
" 'Bastian Schweinsteiger': 'Midfielder',\n",
" 'Mesut Özil': 'Midfielder',\n",
" 'Thomas Müller': 'Midfielder',\n",
" 'Julian Draxler': 'Midfielder',\n",
" 'Toni Kroos': 'Midfielder',\n",
" 'André Schürrle': 'Striker',\n",
" 'Miroslav Klose': 'Striker'}"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"get_players(\"Germany\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Often, we are given a `dict` object like the one returned from `get_players()`: Its main characteristic is that it maps a large set of unique keys (i.e., the players' names) onto a smaller set of non-unique values (i.e., the positions).\n",
"\n",
"**Q3**: Create a generic `invert()` function that swaps the keys and values of a `mapping` argument passed to it and returns them in a *new* `dict` object! Ensure that *no* key gets lost! Verify your implementation with the `brazilian_players` dictionary!\n",
"\n",
"Hints: Think of this as a grouping operation. The *new* values are `list` or `tuple` objects that hold the original keys. You may want to use either the [defaultdict <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/collections.html#collections.defaultdict) type from the [collections <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/collections.html) module in the [standard library <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/index.html) or the [.setdefault() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/stdtypes.html#dict.setdefault) method on the ordinary `dict` type."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"def invert(mapping):\n",
" \"\"\"Invert the keys and values of a mapping argument.\"\"\"\n",
" answer = {}\n",
" for key, value in mapping.items():\n",
" if value not in answer:\n",
" answer[value] = [key]\n",
" else:\n",
" answer[value].append(key)\n",
" return answer\n",
"\n",
"# Alternative 1\n",
"def invert(mapping):\n",
" \"\"\"Invert the keys and values of a mapping argument.\"\"\"\n",
" answer = {}\n",
" for key, value in mapping.items():\n",
" answer.setdefault(value, []).append(key)\n",
" return answer\n",
"\n",
"# Alternative 2\n",
"from collections import defaultdict\n",
"\n",
"def invert(mapping):\n",
" \"\"\"Invert the keys and values of a mapping argument.\"\"\"\n",
" answer = defaultdict(list)\n",
" for key, value in mapping.items():\n",
" answer[value].append(key)\n",
" return dict(answer)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'Goalkeeper': ['Júlio César'],\n",
" 'Defender': ['David Luiz', 'Marcelo', 'Dante', 'Maicon'],\n",
" 'Midfielder': ['Fernandinho',\n",
" 'Hulk',\n",
" 'Paulinho',\n",
" 'Oscar',\n",
" 'Ramires',\n",
" 'Luiz Gustavo',\n",
" 'Willian'],\n",
" 'Striker': ['Fred']}"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"invert(brazilian_players)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q4**: Write a `score_at_minute()` function: It takes two arguments, `team` and `minute`, and returns the number of goals the `team` has scored up until this time in the game.\n",
"\n",
"Hints: The function may reference the global `players` for simplicity. Earn bonus points if you can write this in a one-line expression using some *reduction* function and a `generator` expression."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"def score_at_minute(team, minute):\n",
" \"\"\"Determine the number of goals scored by a team until a given minute.\"\"\"\n",
" score = 0\n",
" for _, _, _, mins in players[team]:\n",
" for m in mins:\n",
" if m <= minute:\n",
" score += 1\n",
" return score\n",
"\n",
"# Alternative: with a one-line expression.\n",
"def score_at_minute(team, minute):\n",
" \"\"\"Determine the number of goals scored by a team until a given minute.\"\"\"\n",
" return sum(1 for _, _, _, mins in players[team] for m in mins if m <= minute)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The score at half time was:"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"score_at_minute(\"Brazil\", 45)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"5"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"score_at_minute(\"Germany\", 45)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The final score was:"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"1"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"score_at_minute(\"Brazil\", 90)"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"7"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"score_at_minute(\"Germany\", 90)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q5**: Write a `goals_by_player()` function that takes an argument like the global `players`, and returns a `dict` object mapping the players to the number of goals they scored!\n",
"\n",
"Hints: Do *not* \"hard code\" the names of the teams! Earn bonus points if you can solve it in a one-line `dict` comprehension."
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [],
"source": [
"def goals_by_player(players):\n",
" \"\"\"Create a dictionary mapping the players' names to the number of goals.\"\"\"\n",
" scorers = {}\n",
" for team in players.keys():\n",
" for _, name, _, goals in players[team]:\n",
" scorers[name] = len(goals)\n",
" return scorers\n",
"\n",
"# Alternative: with a one-line expression.\n",
"def goals_by_player(players):\n",
" \"\"\"Create a dictionary mapping the players' names to the number of goals.\"\"\"\n",
" return {n: len(g) for t in players for _, n, _, g in players[t]}"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'Júlio César': 0,\n",
" 'David Luiz': 0,\n",
" 'Marcelo': 0,\n",
" 'Dante': 0,\n",
" 'Maicon': 0,\n",
" 'Fernandinho': 0,\n",
" 'Hulk': 0,\n",
" 'Paulinho': 0,\n",
" 'Oscar': 1,\n",
" 'Ramires': 0,\n",
" 'Luiz Gustavo': 0,\n",
" 'Willian': 0,\n",
" 'Fred': 0,\n",
" 'Manuel Neuer': 0,\n",
" 'Benedikt Höwedes': 0,\n",
" 'Mats Hummels': 0,\n",
" 'Philipp Lahm': 0,\n",
" 'Per Mertesacker': 0,\n",
" 'Jérôme Boateng': 0,\n",
" 'Sami Khedira': 1,\n",
" 'Bastian Schweinsteiger': 0,\n",
" 'Mesut Özil': 0,\n",
" 'Thomas Müller': 1,\n",
" 'Julian Draxler': 0,\n",
" 'Toni Kroos': 2,\n",
" 'André Schürrle': 2,\n",
" 'Miroslav Klose': 1}"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"goals_by_player(players)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q6**: Write a `dict` comprehension to filter out the players who did *not* score from the preceding result.\n",
"\n",
"Hints: Reference the `goals_by_player()` function from before."
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'Oscar': 1,\n",
" 'Sami Khedira': 1,\n",
" 'Thomas Müller': 1,\n",
" 'Toni Kroos': 2,\n",
" 'André Schürrle': 2,\n",
" 'Miroslav Klose': 1}"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"{k: v for k, v in goals_by_player(players).items() if v > 0}"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'André Schürrle',\n",
" 'Miroslav Klose',\n",
" 'Oscar',\n",
" 'Sami Khedira',\n",
" 'Thomas Müller',\n",
" 'Toni Kroos'}"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# As a set comprehension.\n",
"{k for k, v in goals_by_player(players).items() if v > 0}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q7**: Write a `all_goals()` function that takes one argument like the global `players` and returns a `list` object containing $2$-element `tuple` objects where the first element is the minute a player scored and the second his name! The list should be sorted by the time.\n",
"\n",
"Hints: You may want to use either the built-in [sorted() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#sorted) function or the `list` type's [.sort() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/stdtypes.html#list.sort) method. Earn bonus points if you can write a one-line expression with a `generator` expression."
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [],
"source": [
"def all_goals(players):\n",
" \"\"\"Create a time table of the individual goals.\"\"\"\n",
" answer = []\n",
" for team in players.keys():\n",
" for _, name, _, minutes in players[team]:\n",
" for minute in minutes:\n",
" answer.append((minute, name))\n",
" return sorted(answer)\n",
"\n",
"# Alternative: with a one-line expression.\n",
"def all_goals(players):\n",
" \"\"\"Create a time table of the individual goals.\"\"\"\n",
" return sorted((m, n) for t in players for _, n, _, mins in players[t] for m in mins)"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[(11, 'Thomas Müller'),\n",
" (23, 'Miroslav Klose'),\n",
" (24, 'Toni Kroos'),\n",
" (26, 'Toni Kroos'),\n",
" (29, 'Sami Khedira'),\n",
" (69, 'André Schürrle'),\n",
" (79, 'André Schürrle'),\n",
" (90, 'Oscar')]"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"all_goals(players)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q8**: Lastly, write a `summary()` function that takes one argument like the global `players` and prints out a concise report of the goals, the score at the half, and the final result.\n",
"\n",
"Hints: Use the `all_goals()` and `score_at_minute()` functions from before.\n",
"\n",
"The output should look similar to this:\n",
"```\n",
"12' Gerd Müller scores\n",
"...\n",
"HALFTIME: TeamA 1 TeamB 2\n",
"77' Ronaldo scores\n",
"...\n",
"FINAL: TeamA 1 TeamB 3\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [],
"source": [
"def summary(players):\n",
" \"\"\"Create a written summary of the game.\"\"\"\n",
" # Create two lists with the goals of either half.\n",
" goals = all_goals(players)\n",
" first_half_goals = [(m, n) for m, n in goals if m <= 45]\n",
" second_half_goals = [(m, n) for m, n in goals if m > 45]\n",
"\n",
" # Print the goals of the first half.\n",
" for minute, name in first_half_goals:\n",
" print(f\"{minute}' {name} scores\")\n",
"\n",
" # Print the half time score.\n",
" print(\"HALFTIME:\", end= \" \")\n",
" for team in players.keys():\n",
" score = score_at_minute(team, 45)\n",
" print(f\"{team} {score}\", end=\" \")\n",
" print(\"\")\n",
"\n",
" # Print the goals of the second half.\n",
" for minute, name in second_half_goals:\n",
" print(f\"{minute}' {name} scores\")\n",
"\n",
" # Print the final score.\n",
" print(\"FINAL:\", end=\" \")\n",
" for team in players.keys():\n",
" score = score_at_minute(team, 90)\n",
" print(f\"{team} {score}\", end=\" \")\n",
" print(\"\")"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"11' Thomas Müller scores\n",
"23' Miroslav Klose scores\n",
"24' Toni Kroos scores\n",
"26' Toni Kroos scores\n",
"29' Sami Khedira scores\n",
"HALFTIME: Brazil 0 Germany 5 \n",
"69' André Schürrle scores\n",
"79' André Schürrle scores\n",
"90' Oscar scores\n",
"FINAL: Brazil 1 Germany 7 \n"
]
}
],
"source": [
"summary(players)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"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.8.6"
},
"toc": {
"base_numbering": 1,
"nav_menu": {},
"number_sections": false,
"sideBar": true,
"skip_h1_title": true,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": false,
"toc_position": {},
"toc_section_display": false,
"toc_window_display": false
}
},
"nbformat": 4,
"nbformat_minor": 4
}