Add instructor notes for chapter 09
This commit is contained in:
parent
b59079dc39
commit
f8c25664bb
2 changed files with 981 additions and 0 deletions
689
09_mappings/01_exercises_solved.ipynb
Normal file
689
09_mappings/01_exercises_solved.ipynb
Normal file
|
@ -0,0 +1,689 @@
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
292
09_mappings/03_exercises_solved.ipynb
Normal file
292
09_mappings/03_exercises_solved.ipynb
Normal file
|
@ -0,0 +1,292 @@
|
||||||
|
{
|
||||||
|
"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/03_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 [second 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/02_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": [
|
||||||
|
"## Memoization without Side Effects"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {
|
||||||
|
"slideshow": {
|
||||||
|
"slide_type": "skip"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"source": [
|
||||||
|
"It is considered *bad practice* to make a function and thereby its correctness dependent on a program's *global state*: For example, in the \"*Easy at second Glance: Fibonacci Numbers*\" section in [Chapter 9 <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/02_content.ipynb#\"Easy-at-second-Glance\"-Example:-Fibonacci-Numbers--%28revisited%29), we use a global `memo` to store the Fibonacci numbers that have already been calculated.\n",
|
||||||
|
"\n",
|
||||||
|
"That `memo` dictionary could be \"manipulated.\" More often than not, such things happen by accident: Imagine we wrote two independent recursive functions that both rely on memoization to solve different problems, and, unintentionally, we made both work with the *same* global `memo`. As a result, we would observe \"random\" bugs depending on the order in which we executed these functions. Such bugs are hard to track down in practice.\n",
|
||||||
|
"\n",
|
||||||
|
"A common remedy is to avoid global state and pass intermediate results \"down\" the recursion tree in a \"hidden\" argument. By convention, we prefix parameter names with a single leading underscore `_`, such as with `_memo` below, to indicate that the caller of our `fibonacci()` function *must not* use it. Also, we make `_memo` a *keyword-only* argument to force ourselves to always explicitly name it in a function call. Because it is an **implementation detail**, the `_memo` parameter is *not* mentioned in the docstring.\n",
|
||||||
|
"\n",
|
||||||
|
"Your task is to complete this version of `fibonacci()` so that the function works *without* any **side effects** in the global scope."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {
|
||||||
|
"slideshow": {
|
||||||
|
"slide_type": "skip"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"source": [
|
||||||
|
"### \"Easy at third Glance\" Example: [Fibonacci Numbers <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Fibonacci_number)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 1,
|
||||||
|
"metadata": {
|
||||||
|
"code_folding": [],
|
||||||
|
"slideshow": {
|
||||||
|
"slide_type": "skip"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"def fibonacci(i, *, debug=False, _memo=None):\n",
|
||||||
|
" \"\"\"Calculate the ith Fibonacci number.\n",
|
||||||
|
"\n",
|
||||||
|
" Args:\n",
|
||||||
|
" i (int): index of the Fibonacci number to calculate\n",
|
||||||
|
" debug (bool): show non-cached calls; defaults to False\n",
|
||||||
|
"\n",
|
||||||
|
" Returns:\n",
|
||||||
|
" ith_fibonacci (int)\n",
|
||||||
|
" \"\"\"\n",
|
||||||
|
" # answer to Q1\n",
|
||||||
|
" if _memo is None:\n",
|
||||||
|
" _memo = {\n",
|
||||||
|
" 0: 0,\n",
|
||||||
|
" 1: 1,\n",
|
||||||
|
" }\n",
|
||||||
|
"\n",
|
||||||
|
" # answer to Q2\n",
|
||||||
|
" if i in _memo:\n",
|
||||||
|
" return _memo[i]\n",
|
||||||
|
"\n",
|
||||||
|
" if debug: # added for didactical purposes\n",
|
||||||
|
" print(f\"fibonacci({i}) is calculated\")\n",
|
||||||
|
"\n",
|
||||||
|
" # answer to Q3\n",
|
||||||
|
" recurse = (\n",
|
||||||
|
" fibonacci(i - 1, debug=debug, _memo=_memo)\n",
|
||||||
|
" + fibonacci(i - 2, debug=debug, _memo=_memo)\n",
|
||||||
|
" )\n",
|
||||||
|
" # answer to Q4\n",
|
||||||
|
" _memo[i] = recurse\n",
|
||||||
|
" return recurse"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {
|
||||||
|
"slideshow": {
|
||||||
|
"slide_type": "skip"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"source": [
|
||||||
|
"**Q1**: When `fibonacci()` is initially called, `_memo` is set to `None`. So, there is *no* `dict` object yet. Implement the *two* base cases in the first `if` statement!\n",
|
||||||
|
"\n",
|
||||||
|
"Hints: All you need to do is create a *new* `dict` object with the results for `i=0` and `i=1`. This object is then passed on in the recursive function calls. Use the `is` operator in the `if` statement."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**Q2**: When `fibonacci()` is called for non-base cases (i.e., `i > 1`), it first checks if the result is already in the `_memo`. Implement that step in the second `if` statement!\n",
|
||||||
|
"\n",
|
||||||
|
"Hint: Use the early exit pattern."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**Q3**: If `fibonacci()` is called for an `i` argument whose result is not yet in the `_memo`, it must calculate it with the usual recursive function calls. Fill in the arguments to the two recursive `fibonacci()` calls!\n",
|
||||||
|
"\n",
|
||||||
|
"Hint: You must pass on the hidden `_memo`."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**Q4**: Lastly, after the two recursive calls have returned, `fibonacci()` must store the `recurse` result for the given `i` in the `_memo` *before* returning it. Implement that logic!"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**Q5**: What happens to the hidden `_memo` after the initial call to `fibonacci()` returned? How many hidden `_memo` objects exist in memory during the entire computation?"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
" < your answer >"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {
|
||||||
|
"slideshow": {
|
||||||
|
"slide_type": "skip"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"source": [
|
||||||
|
"Because `fibonacci()` is now independent of the *global state*, the same eleven recursive function calls are made each time! So, this `fibonacci()` is a **pure** function, meaning it has *no* side effects.\n",
|
||||||
|
"\n",
|
||||||
|
"**Q6**: Execute the following code cell a couple of times to observe that!"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 2,
|
||||||
|
"metadata": {
|
||||||
|
"slideshow": {
|
||||||
|
"slide_type": "skip"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"fibonacci(12) is calculated\n",
|
||||||
|
"fibonacci(11) is calculated\n",
|
||||||
|
"fibonacci(10) is calculated\n",
|
||||||
|
"fibonacci(9) is calculated\n",
|
||||||
|
"fibonacci(8) is calculated\n",
|
||||||
|
"fibonacci(7) is calculated\n",
|
||||||
|
"fibonacci(6) is calculated\n",
|
||||||
|
"fibonacci(5) is calculated\n",
|
||||||
|
"fibonacci(4) is calculated\n",
|
||||||
|
"fibonacci(3) is calculated\n",
|
||||||
|
"fibonacci(2) is calculated\n"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"144"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 2,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"fibonacci(12, debug=True) # = 13th Fibonacci number -> 11 recursive calls necessary"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"The runtime of `fibonacci()` is now stable: There is no message that \"an intermediate result is being cached\" as in [Chapter 9 <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/02_content.ipynb#\"Easy-at-second-Glance\"-Example:-Fibonacci-Numbers--%28revisited%29).\n",
|
||||||
|
"\n",
|
||||||
|
"**Q7**: Execute the following code cells a couple of times to observe that!"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 3,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"287 µs ± 40.1 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"%%timeit -n 1\n",
|
||||||
|
"fibonacci(99)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 4,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"2.45 ms ± 1.18 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"%%timeit -n 1\n",
|
||||||
|
"fibonacci(999)"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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
|
||||||
|
}
|
Loading…
Reference in a new issue