Learn all about Python dictionary comprehension: how you can use it to create dictionaries, to replace (nested) for loops or lambda functions with map(), filter() and reduce(), ...!
Dictionaries (or
dict
in Python) are a way of storing elements just like you would in a Python list. But, rather than accessing elements using its index, you assign a fixed key to it and access the element using the key. What you now deal with is a "key-value" pair, which is sometimes a more appropriate data structure for many problem instead of a simple list. You will often have to deal with dictionaries when doing data science, which makes dictionary comprehension a skill that you will want to master.- First you'll see what a Python dictionary really is and how you can use it effectively.
- Next, you'll learn about Python dictionary comprehensions: you will see what it is, why it is important and how it can serve as an alternative to for loops and lambda functions.
- You will learn how to add conditionals into dictionary comprehensions: you will work with if conditions, multiple if conditions and also if-else statements.
- Lastly, you will see what nested dictionary comprehension is, how you can use it and how you can potentially rewrite it with for loops.
Let's get started...
Python Dictionary
A dictionary in Python is a collection of items accessed by a specific key rather than by index. What does this mean?
Imagine a dictionary in the real world... when you need to look up the meaning of a word, you try to find the meaning using the word itself and not the possible index of the word. Python dictionaries work with the same concept, the word whose meaning you are looking for is the key and the meaning of the word is the value, you do not need to know the index of the word in a dictionary to find its meaning.
Note: The keys in a dictionary have to be hashable.
Hashing is the process of running an item through a specific kind of function. This function is called a "hash function". This hash function returns a unique output for a unique input value. Integers, floating point numbers, strings, tuples, and frozensets are hashable. While lists, dictionaries, and sets other than frozensets are not. Hashing is a somewhat complex topic and this is only the basic concept behind hashing.
You can initialize a dictionary in Python this way:
a = {'apple': 'fruit', 'beetroot': 'vegetable', 'cake': 'dessert'}
a['doughnut'] = 'snack'
print(a['apple'])
fruit
print(a[0])
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-9-00d4a978143a> in <module>()
----> 1 print(a[0])
KeyError: 0
Note that the line of code above gives you an error message because there doesn't exist a key
'0'
.
The items in a dictionary can have any data type. Check out some more examples of a dictionary to get a hang of it:
a = {'one': 1, 'two': 'to', 'three': 3.0, 'four': [4,4.0]}
print(a)
{'four': [4, 4.0], 'two': 'to', 'three': 3.0, 'one': 1}
# Update a dictionary
a['one'] = 1.0
print(a)
{'four': [4, 4.0], 'two': 'to', 'three': 3.0, 'one': 1.0}
# Delete a single element
del a['one']
print(a)
{'four': [4, 4.0], 'two': 'to', 'three': 3.0}
# Delete all elements in the dictionary
a.clear()
print(a)
{}
# Delete the dictionary
del a
print(a)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-12-701c9d6596da> in <module>()
1 del a #Deletes the dictionary
----> 2 print(a)
NameError: name 'a' is not defined
Important to remember is that a key has to be unique in a dictionary, no duplicates are allowed. However, in case of duplicate keys rather than giving an error, Python will take the last instance of the key to be valid and simply ignore the first key-value pair. See it for yourself:
sweet_dict = {'a1': 'cake', 'a2':'cookie', 'a1': 'icecream'}
print(sweet_dict['a1'])
icecream
If you want to know more about dictionaries in Python, check out this tutorial.
Python Dictionary Comprehension
Dictionary comprehension is a method for transforming one dictionary into another dictionary. During this transformation, items within the original dictionary can be conditionally included in the new dictionary and each item can be transformed as needed.
A good list comprehension can make your code more expressive and thus, easier to read. The key with creating comprehensions is to not let them get so complex that your head spins when you try to decipher what they are actually doing. Keeping the idea of "easy to read" alive.
The way to do dictionary comprehension in Python is to be able to access the
key
objects and the value
objects of a dictionary.
How can this be done?
Python has you covered! You can simply use the built-in methods for the same:
dict1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
# Put all keys of `dict1` in a list and returns the list
dict1.keys()
dict_keys(['c', 'd', 'a', 'b'])
# Put all values saved in `dict1` in a list and returns the list
dict1.values()
dict_values([3, 4, 1, 2])
So, now that you know how to access all the keys and their values in a dictionary. You can also access each key-value pair within a dictionary using the
items()
method:dict1.items()
dict_items([('c', 3), ('d', 4), ('a', 1), ('b', 2)])
This is the general template you can follow for dictionary comprehension in Python:
dict_variable = {key:value for (key,value) in dictonary.items()}
This can serve as the basic and the most simple template. This can get more and more complex as you add conditionalities to it.
Let's start off with a simple dictionary comprehension:
dict1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
# Double each value in the dictionary
double_dict1 = {k:v*2 for (k,v) in dict1.items()}
print(double_dict1)
{'e': 10, 'a': 2, 'c': 6, 'b': 4, 'd': 8}
In the comprehension code above, you create a new dictionary
double_dict1
from a dictionary dict1
by simply doubling each value in it.
You can also make changes to the key values. For example, let's create the same dictionary as above but also change the names of the key.
dict1_keys = {k*2:v for (k,v) in dict1.items()}
print(dict1_keys)
{'dd': 4, 'ee': 5, 'aa': 1, 'bb': 2, 'cc': 3}
Why Use Dictionary Comprehension?
Dictionary comprehension is a powerful concept and can be used to substitute
for
loops and lambda
functions. However, not all for loop can be written as a dictionary comprehension but all dictionary comprehension can be written with a for loop.
Consider the following problem, where you want to create a new dictionary where the key is a number divisible by 2 in a range of 0-10 and it's value is the square of the number.
Let's see how the same probem can be solved using a for loop and dictionary comprehension:
numbers = range(10)
new_dict_for = {}
# Add values to `new_dict` using for loop
for n in numbers:
if n%2==0:
new_dict_for[n] = n**2
print(new_dict_for)
{0: 0, 8: 64, 2: 4, 4: 16, 6: 36}
# Use dictionary comprehension
new_dict_comp = {n:n**2 for n in numbers if n%2 == 0}
print(new_dict_comp)
{0: 0, 8: 64, 2: 4, 4: 16, 6: 36}
Alternative to for
loops
For loops are used to repeat a certain operation or a block of instructions in a program for a given number of times. However, nested for loops (for loop inside another for loop) can get confusing and complex. Dictionary comprehensions are better in such situations and can simplify the readability and your understanding of the code.
Tip: check out DataCamp's Loops in Python tutorial for more information on loops in Python.
Alternative to lambda
functions
Lambda functions are a way of creating small anonymous functions. They are functions without a name. These functions are throw-away functions, which are only needed where they have been created. Lambda functions are mainly used in combination with the functions
filter()
, map()
and reduce()
.
Let's look at the lambda function along with the
map()
function:# Initialize `fahrenheit` dictionary
fahrenheit = {'t1':-30, 't2':-20, 't3':-10, 't4':0}
#Get the corresponding `celsius` values
celsius = list(map(lambda x: (float(5)/9)*(x-32), fahrenheit.values()))
#Create the `celsius` dictionary
celsius_dict = dict(zip(fahrenheit.keys(), celsius))
print(celsius_dict)
{'t2': -28.88888888888889, 't3': -23.333333333333336, 't1': -34.44444444444444, 't4': -17.77777777777778}
Let's take a look at another situation, where you want to convert a dictionary of Fahrenheit temperatures into celsius.
Let's break the code down: first, you need to define a mathematical formula that does the conversion from Fahrenheit to Celsius. In the code, this is done with the help of the lambda function. You then pass this function as an argument to the
map()
function which then applies the operation to every item in the fahrenheit.values()
list.
Remember the
values()
function? It returns a list containing the items stored in the dictionary.
What you have now is a list containing the temperature value in celsius, but the solution requires it to be a dictionary. Python has a built-in function called
zip()
which goes over the elements of iterators and aggregates them. You can read more about the zip()
function here. In the example above, the zip function aggregates the item from fahrenheit.keys()
and the celsius
list, giving a key-value pair that you can put together in a dictionary using the dict
function, which is the desired result.
Now, let's try to solve the same problem using dictionary comprehension:
# Initialize the `fahrenheit` dictionary
fahrenheit = {'t1': -30,'t2': -20,'t3': -10,'t4': 0}
# Get the corresponding `celsius` values and create the new dictionary
celsius = {k:(float(5)/9)*(v-32) for (k,v) in fahrenheit.items()}
print(celsius_dict)
{'t2': -28.88888888888889, 't3': -23.333333333333336, 't1': -34.44444444444444, 't4': -17.77777777777778}
As you can see, the problem can be solved with a single line of code using dictionary comprehension as compared to the two step process and understanding the working of three functions (
lambda
, map()
and zip()
) for the first implementation.
Moreover, the solution is intuitive and easy to understand with the dictionary comprehension. Hence, dictionary comprehension can serve as a good alternative to the lambda functions.
Adding Conditionals to Dictionary Comprehension
You often need to add conditions to a solution while tackling problems. Let's explore how you can add conditionals into dictionary comprehension to make it more powerful.
If Condition
Let's suppose you need to create a new dictionary from a given dictionary but with items that are greater than 2. This means that you need to add a condition to the original template you saw above...
dict1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
# Check for items greater than 2
dict1_cond = {k:v for (k,v) in dict1.items() if v>2}
print(dict1_cond)
{'e': 5, 'c': 3, 'd': 4}
This isn't so hard! But what if you have multiple conditions?
Multiple If Conditions
In the problem above, what if you have to not only get the items greater than 2 but also need to check if they are multiples of 2 at the same time.
dict1_doubleCond = {k:v for (k,v) in dict1.items() if v>2 if v%2 == 0}
print(dict1_doubleCond)
{'d': 4}
The solution to adding multiple conditionals is as easy as simply adding the conditions one after another in your comprehension. However, you need to be careful about what you are trying to do in the problem. Remember, that the consecutive if statements work as if they had
and
clauses between them.
Lets see one more example with three conditionals:
dict1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f':6}
dict1_tripleCond = {k:v for (k,v) in dict1.items() if v>2 if v%2 == 0 if v%3 == 0}
print(dict1_tripleCond)
{'f': 6}
In a for loop, this will correspond to:
dict1_tripleCond = {}
for (k,v) in dict1.items():
if (v>=2 and v%2 == 0 and v%3 == 0):
dict1_tripleCond[k] = v
print(dict1_tripleCond)
{'f': 6}
If-Else Conditions
Dealing with an if-else condition is also easy with dictionary comprehension. Check out the following example to see it for yourself:
dict1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f':6}
# Identify odd and even entries
dict1_tripleCond = {k:('even' if v%2==0 else 'odd') for (k,v) in dict1.items()}
print(dict1_tripleCond)
{'f': 'even', 'c': 'odd', 'b': 'even', 'd': 'even', 'e': 'odd', 'a': 'odd'}
Nested Dictionary Comprehension
Nesting is a programming concept where data is organized in layers, or where objects contain other similar objects. You must have often seen a nested 'if' structure, which is an if condition inside another if condition.
Similarly, dictionaries can be nested and thus their comprehensions can be nested as well. Let's see what this means:
nested_dict = {'first':{'a':1}, 'second':{'b':2}}
float_dict = {outer_k: {float(inner_v) for (inner_k, inner_v) in outer_v.items()} for (outer_k, outer_v) in nested_dict.items()}
print(float_dict)
{'first': {1.0}, 'second': {2.0}}
This is an example of a nested dictionary. The
nested_dict
is a dictionary with the keys: first
and second
, which hold dictionary objects in their values. The code works with the inner dictionary values and converts them to float and then combines the outer keys with the new float inner values into a new dictionary.
The code also has a nested dictionary comprehension, which is dictionary comprehension inside another one. The dictionary comprehension when nested as you can see can get pretty hard to read as well as understand, which takes away the whole point of using comprehensions in the first place. As the structure of the dictionary you are working with gets complicated, the dictionary comprehension starts to get complicated as well. For such situations, you might be better off not using complicated comprehensions in your code.
Note that you can rewrite the above code chunk also with a nested for loop:
nested_dict = {'first':{'a':1}, 'second':{'b':2}}
for (outer_k, outer_v) in nested_dict.items():
for (inner_k, inner_v) in outer_v.items():
outer_v.update({inner_k: float(inner_v)})
nested_dict.update({outer_k:outer_v})
print(nested_dict)
{'first': {'a': 1.0}, 'second': {'b': 2.0}}
No hay comentarios:
Publicar un comentario