Dictionaries

Counting Frequency of Dice Rolls

Dice Rolls Solution

import random
twos, threes, fours, fives, sixes = 0, 0, 0, 0, 0
for i in range(1000):
	roll = random.randint(1,3) + random.randint(1,3)
	if roll == 2:
		twos += 1
	if roll == 3:
		threes += 1
	if roll == 4:
		fours += 1
	if roll == 5:
		fives += 1
	if roll == 6:
		sixes += 1

print("twos: %s, threes: %s, fours: %s, fives: %s, sixes %s" % (twos, threes, fours, fives, sixes)) 

Dice Rolls

Whew!

That was a long if-else. What if we had to write one for two twenty-sided dice?

If there were only a way to dynamically create names and associate values to them.

We'll get to that in a second. First: compound types, mapping types and dictionaries

Revisiting Compound Types

Compound types: values that are made up of other values.

Sequence types are compound types. We know three sequence types. What are they?

  1. string
  2. list
  3. tuple

Mapping Types

Another kind of compound type is a mapping type.

A mapping type is a data type that is made of a collection of keys (think of names) and their associated values. The only mapping type in Python is a dictionary.

Dictionaries

A dictionary is an unordered collection of key/value pairs.

Dictionaries Syntax

Let's take a look at some examples

{"first_name":"joe", "fav_candy":"cleo's"}
{28:"foo", 6:"bar", "entirely different":["baz"]}
{}

(btw, cleo's are pretty good)

Dictionaries Syntax Continued

What are the keys and what type are they? What are the values and what type are they?

d1 = {"first_name":"joe", "fav_candy":"cleo's"}
d2 = {28:"foo", 6:"bar", "entirely different":["baz"]}

Retrieving Values at Keys

You can use the key like a list index to retrieve a value at that key. What does this code print out?

d = {"first_name":"joe", "fav_candy":"cleo's"}
print(d["first_name"])
print(d["fav_candy"])
joe
cleo's

Keys That Don't Exist!

Let's try that again… but with a key that doesn't exist. What do you think will happen here?

d = {"first_name":"joe", "fav_candy":"cleo's"}
print(d["height"])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'height'

Retrieval Using the get Method

You can also retrieve a value from a dictionary by calling the get method. get takes two arguments:

What do you think this code outputs?

d = {"first_name":"joe", "fav_candy":"cleo's"}
print(d.get("height", None))
None

Key/Value Pairs

So… things are a bit different when adding keys and values to a dictionary. There is no error if you use a key that doesn't exist in order to assign a value to it (that is… to create a new key/value pair).

#  look ma, no keys!
d = {}
#  but I can assign with no probz
d["problems"] = None
d["another key"] = "another value"

Key/Value Pairs Continued

If the key already exists… and you use the assignment operator, you are just associating a new value with an existing key.

What does this code output?

d = {}
d["another key"] = "another value"
d["problems"] = None
d["another key"] = "something"
print(d["another key"])
print(d["problems"])
something
None

Dictionaries and Mutability

Based on what we've seen so far… are dictionaries mutable or immutable?

Dictionaries are mutable!

d = {}
d["did not exist before"] = "some value"
#  ok!

We Can Iterate Over Dictionaries

Let's run this code. What would you expect this to print? Does it match what actually happens?

d = {"first_name":"joe", "fav_candy":"cleo's"}
for item in d:
  print(item)
#  we only get the keys!
fav_candy
first_name

Dictionary Views

Another way to iterate over a dictionary is to convert it to a special sequence called a dictionary view.

What type is the type of variable, i? How many iterations are there? What is the value of i at each iteration?

d = {"first_name":"joe", "fav_candy":"cleo's"}
items = d.items()
for t in items:
	print(t)

Converting a Dictionary to a Bunch of Tuples

d = {"first_name":"joe", "fav_candy":"cleo's"}
items = d.items()
for t in items:
	print(t)

A few notes….

Iterating over a Dictionary Using items()

Now… we have a tuple… how do we print out each key and value individually (how do we unpack again?)

d = {"first_name":"joe", "fav_candy":"cleo's"}
for k, v in d.items():
  print(k, v)

Dictionaries Are Unordered

Unlike sequence types like string, list or tuple, dictionaries are unordered. That means that the order of the key value pairs cannot be guaranteed! Let's take a look. Intuitively, what's the first thing that we think will be printed out?

d = {"one":"foo", "two":"bar", 3:"baz"}
for k, v in d.items():
  print(k, v)
#  this is actually just one possible ordering!
3 baz
two bar
one foo

in and len()

The in and not in operators work with dictionary keys; len() gives back the number of key/value pairs

pizza = {"crust":"thin", "topping":"mushrooms", 'size':'large', 'sauce':'tomato'}
print(len(pizza))
#  prints out 4

result = 'crust' in pizza
print(result)
#  prints out True

Summary Questions

More Dictionary Methods

By the way, here are a few more dictionary methods:

values, keys

values and keys give back dictionary views (they essentially act like lists) of either all values or all keys of the dictionary that they're called on. What does this output?

vehicle = {"name":"bathysphere", "wheels":0, "can fly":False}
print(vehicle.keys())
print(vehicle.values())

Note that the order of the keys and values cannot be guaranteed!

dict_keys(['can fly', 'wheels', 'name'])
dict_values([False, 0, 'bathysphere'])

pop

pop removes and returns the item at the key specified. What does this output?

vehicle = {"name":"bathysphere", "wheels":0, "can fly":False}
result1 = vehicle.pop('can fly')
result2 = vehicle.pop('floats', False)
print(result1)
print(result2) 
print(vehicle)
False
False
{'wheels': 0, 'name': 'bathysphere'}

popitem

popitem removes and returns an arbitrary key/value pair. What does this output?

vehicle = {"name":"bathysphere", "wheels":0, "can fly":False}
result1 = vehicle.popitem()
print(result1)
print(vehicle)

Note that the key/value pair removed and returned will not always be the same!

('can fly', False)
{'wheels': 0, 'name': 'bathysphere'}

update

update adds key/value pairs (or updates values if keys already exist) from another dictionary to the dictionary that update is called on. What does this output?

vehicle = {"name":"bathysphere", "wheels":0, "can fly":False}
another_vehicle = {'can float':True, 'name':'boat'}
vehicle.update(another_vehicle)
print(vehicle)
{'can fly': False, 'wheels': 0, 'name': 'boat', 'can float': True}

Changing a Value Based on the Existing Value

To change a value base on an existing value, such as incrementing a number, you can simply do something like this (adds one to the current value at fga):

d = {'fgm':1, 'fga':2}
d['fga'] = d['fga'] + 1

Changing a Value Based on the Existing Value

But what if it doesn't exist yet? What happens here?

d = {'fgm':1 }
d['fga'] = d['fga'] + 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'fga'

Summary Answers

Back to Dice!

Try reimplementing our dice program so that it uses a dictionary to store the frequency of rolls (each roll as a key, each value as a count). (hint: use in, the get method or a try-except to deal with keys that don't exist).

import random
twos, threes, fours, fives, sixes = 0, 0, 0, 0, 0
for i in range(1000):
	roll = random.randint(1,3) + random.randint(1,3)
	if roll == 2:
		twos += 1
	if roll == 3:
		threes += 1
	if roll == 4:
		fours += 1
	if roll == 5:
		fives += 1
	if roll == 6:
		sixes += 1

print("twos: %s, threes: %s, fours: %s, fives: %s, sixes %s" % (twos, threes, fours, fives, sixes)) 

Dice Rolls Solution

import random
freq_dice_rolls = {}
for i in range(1000):
	roll = random.randint(1,3) + random.randint(1,3)
	freq_dice_rolls[roll] = freq_dice_rolls.get(roll, 0) + 1
print(freq_dice_rolls)

Further Exercises