{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "# Chapter 8: Mappings & Sets" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Content Review" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Read [Chapter 8](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/08_mappings.ipynb) of the book. Then work through the twelve review questions." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Essay Questions " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Answer the following questions briefly with *at most* 300 characters per question!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Q1**: `dict` objects are well-suited **to model** discrete mathematical **functions** and to approximate continuous ones. What property of dictionaries is the basis for that claim, and how does it relate to functions in the mathematical sense?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Q2**: Explain why **hash tables** are a **trade-off** between **computational speed** and **memory** usage!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Q3:** The `dict` type is an **iterable** that **contains** a **finite** number of key-value pairs. Despite that, why is it *not* considered a **sequence**?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Q4**: Whereas *key* **look-ups** in a `dict` object run in so-called **[constant time](https://en.wikipedia.org/wiki/Time_complexity#Constant_time)** (i.e., *extremely* fast), that does not hold for *reverse* look-ups. Why is that?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Q5**: Why is it conceptually correct that the Python core developers do not implement **slicing** with the `[]` operator for `dict` objects?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Q6**: **Memoization** is an essential concept to know to solve problems in the real world. Together with the idea of **recursion**, it enables us to solve problems in a \"backwards\" fashion *effectively*.\n", "\n", "\n", "Compare the **recursive** formulation of `fibonacci()` in [Chapter 8](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/08_mappings.ipynb#\"Easy-at-third-Glance\"-Example:-Fibonacci-Numbers-%28revisited%29), the \"*Easy at third Glance*\" example, with the **iterative** version in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#\"Hard-at-first-Glance\"-Example:-Fibonacci-Numbers-%28revisited%29), the \"*Hard at first Glance*\" example!\n", "\n", "How are they similar and how do they differ?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Q7**: How are the `set` and the `dict` type related? How could we use the latter to mimic the former?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### True / False Questions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Motivate your answer with *one short* sentence!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Q8**: We may *not* put `dict` objects inside other `dict` objects because they are **mutable**." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Q9**: **Mutable** objects (e.g., `list`) may generally *not* be used as keys in a `dict` object. However, if we collect, for example, `list` objects in a `tuple` object, the composite object becomes **hashable**." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Q10**: **Mutability** of a `dict` object works until the underlying hash table becomes too crowded. Then, we cannot insert any items any more making the `dict` object effectively **immutable**. Luckily, that almost never happens in practice." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Q11**: A `dict` object's [update()](https://docs.python.org/3/library/stdtypes.html#dict.update) method only inserts key-value pairs whose key is *not* yet in the `dict` object. So, it does *not* overwrite anything." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Q12**: The `set` type is both a mapping and a sequence." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Coding Exercises" ] }, { "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](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": null, "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": [ "**Q13.1**: 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": null, "metadata": {}, "outputs": [], "source": [ "brazilian_players = {...}" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "brazilian_players" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Q13.2**: 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!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def get_players(team):\n", " ..." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "get_players(\"Germany\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Often, we are given a `dict` object like the one returned from `get_players()`: It is characterized by the observation that a large set of unique keys (i.e., the players' names) is mapped onto a smaller set of non-unique values (i.e., the positions).\n", "\n", "**Q13.3**: 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 the [defaultdict](https://docs.python.org/3/library/collections.html#collections.defaultdict) type from the [collections](https://docs.python.org/3/library/collections.html) module in the [standard library](https://docs.python.org/3/library/index.html) or the [setdefault()](https://docs.python.org/3/library/stdtypes.html#dict.setdefault) method of the ordinary `dict` type." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def invert(mapping):\n", " ..." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "invert(brazilian_players)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Q13.4**: 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": null, "metadata": {}, "outputs": [], "source": [ "def score_at_minute(team, minute):\n", " ..." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The score at half time was:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "score_at_minute(\"Brazil\", 45)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "score_at_minute(\"Germany\", 45)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The final score was:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "score_at_minute(\"Brazil\", 90)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "score_at_minute(\"Germany\", 90)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Q13.5**: Write a `goals_by_player()` function: It 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 single dictionary comprehension." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def goals_by_player(players):\n", " ..." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "goals_by_player(players)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Q13.6**: Write a *dictionary comprehension* to filter out the players who did *not* score from the preceding result. Then, write a *set comprehension* that does the same but discards the number of goals scored.\n", "\n", "Hints: Reference the `goals_by_player()` function from before." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "{...}" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "{...}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Q13.7**: Write a `all_goals()` function: It 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()](https://docs.python.org/3/library/functions.html#sorted) function or the `list` type's [sort()](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": null, "metadata": {}, "outputs": [], "source": [ "def all_goals(players):\n", " ..." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "all_goals(players)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Q13.8**: Lastly, write a `summary()` function: It 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": null, "metadata": {}, "outputs": [], "source": [ "def summary(players):\n", " ..." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "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.7.3" }, "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": 2 }