Note: Click on "Kernel" > "Restart Kernel and Run All" in JupyterLab 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 .
The exercises below assume that you have read the first part of Chapter 9.
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.
Let's write some code to analyze the historic soccer game Brazil vs. Germany during the 2014 World Cup.
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.
players = {
"Brazil": [
(12, "Júlio César", "Goalkeeper", []),
(4, "David Luiz", "Defender", []),
(6, "Marcelo", "Defender", []),
(13, "Dante", "Defender", []),
(23, "Maicon", "Defender", []),
(5, "Fernandinho", "Midfielder", []),
(7, "Hulk", "Midfielder", []),
(8, "Paulinho", "Midfielder", []),
(11, "Oscar", "Midfielder", [90]),
(16, "Ramires", "Midfielder", []),
(17, "Luiz Gustavo", "Midfielder", []),
(19, "Willian", "Midfielder", []),
(9, "Fred", "Striker", []),
],
"Germany": [
(1, "Manuel Neuer", "Goalkeeper", []),
(4, "Benedikt Höwedes", "Defender", []),
(5, "Mats Hummels", "Defender", []),
(16, "Philipp Lahm", "Defender", []),
(17, "Per Mertesacker", "Defender", []),
(20, "Jérôme Boateng", "Defender", []),
(6, "Sami Khedira", "Midfielder", [29]),
(7, "Bastian Schweinsteiger", "Midfielder", []),
(8, "Mesut Özil", "Midfielder", []),
(13, "Thomas Müller", "Midfielder", [11]),
(14, "Julian Draxler", "Midfielder", []),
(18, "Toni Kroos", "Midfielder", [24, 26]),
(9, "André Schürrle", "Striker", [69, 79]),
(11, "Miroslav Klose", "Striker", [23]),
],
}
Q1: Write a dictionary comprehension to derive a new dict
object, called brazilian_players
, that maps a Brazilian player's name to his position!
brazilian_players = {...: ...}
brazilian_players
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!
def get_players(team):
"""Creates a dictionary mapping the players' names to their position."""
return {...: ...}
get_players("Germany")
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).
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!
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 type from the collections
module in the standard library
or the .setdefault()
method on the ordinary
dict
type.
def invert(mapping):
"""Invert the keys and values of a mapping argument."""
...
...
...
return ...
invert(brazilian_players)
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.
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.
def score_at_minute(team, minute):
"""Determine the number of goals scored by a team until a given minute."""
...
...
...
...
...
return ...
The score at half time was:
score_at_minute("Brazil", 45)
score_at_minute("Germany", 45)
The final score was:
score_at_minute("Brazil", 90)
score_at_minute("Germany", 90)
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!
Hints: Do not "hard code" the names of the teams! Earn bonus points if you can solve it in a one-line dict
comprehension.
def goals_by_player(players):
"""Create a dictionary mapping the players' names to the number of goals."""
...
...
...
...
return ...
goals_by_player(players)
Q6: Write a dict
comprehension to filter out the players who did not score from the preceding result.
Hints: Reference the goals_by_player()
function from before.
{...: ...}
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.
Hints: You may want to use either the built-in sorted() function or the
list
type's .sort() method. Earn bonus points if you can write a one-line expression with a
generator
expression.
def all_goals(players):
"""Create a time table of the individual goals."""
...
...
...
...
...
return ...
all_goals(players)
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.
Hints: Use the all_goals()
and score_at_minute()
functions from before.
The output should look similar to this:
12' Gerd Müller scores
...
HALFTIME: TeamA 1 TeamB 2
77' Ronaldo scores
...
FINAL: TeamA 1 TeamB 3
def summary(players):
"""Create a written summary of the game."""
# Create two lists with the goals of either half.
...
...
...
# Print the goals of the first half.
...
...
# Print the half time score.
...
...
...
...
...
# Print the goals of the second half.
...
...
# Print the final score.
...
...
...
...
...
summary(players)