2D Lists, Dictionaries and Sets#
In this episode, we will learn more about Python data types, such as dictionaries and sets. However, I want to begin with a brief overview of 2-dimensional lists, as they can be used effectively to understand some basics of linear algebra.
2-Dimensional Lists#
In our previous discussion, we learned that we can create lists in Python. I mentioned that we can use objects of different data types as elements of a list. But have you ever thought about using lists themselves as elements? Surprisingly, there is nothing stopping us from doing so.
M = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print(M)
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
After that we can work with such list as it was a matrix:
M = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print(M[0][0])
print(M[1][2])
1
6
You can create more complex nested structures. Now, I will show you examples of how to create 2D lists using list comprehensions:
n, m = 5, 10 # let n corresponds to rows, and m to columns
zeros = [[0] * m for i in range(n)]
for row in zeros:
print(row)
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
Let’s take a look at matrix multiplication as an example. Here, I would like to ask if you know how matrix multiplication looks like and why it is defined in this way?
Attempt to implement matrix multiplication for 2D lists.
Here is possible implementation for two lists A
and B
C = [[0] * len(B[0]) for i in range(len(A))]
for i in range(len(A)):
for j in range(len(B[0])):
for k in range(len(B)):
C[i][j] += A[i][k] * B[k][j]
Dictionaries#
A Python’s dictionary (dict
) is like a real-life dictionary or a phone book. It contains key-value pairs, where each key maps to a specific value. In Python, we create dictionaries using curly braces {}
.
person = {
"name": "Vladimir",
"dob": "10/08/1994",
"age": 29
}
print(person)
Show code cell output
{'name': 'Vladimir', 'dob': '10/08/1994', 'age': 29}
Here, as you can see, we have a map between keys and values. In this specific case, we have the following keys: "name"
, "dob"
, "age"
, and their corresponding values: "Vladimir"
, "10/08/1994"
, and 29
. It’s important to note that as keys of a dictionary, you can use not just strings, but also other immutable objects.
Warning
Python does not permit the use of mutable objects as dictionary keys. Therefore, the following example will result in an error:
wrong_dict = {[1, 2, 3]: "list"}
Access the element of a dict#
We can access values in dictionary by specifying the key.
name = person["name"]
print(name)
Show code cell output
Vladimir
As you can see, the syntax is similar to a list where we use the index to access specific elements. However, when it comes to dictionaries (dict
), we should use key instead.
Warning
In Python dictionaries, the order of keys was traditionally considered arbitrary and not guaranteed. This means that the keys were stored in an unpredictable order, based on how Python’s internal data structures managed them. However, starting from Python 3.7 and later versions, dictionaries maintain the insertion order of keys. This means that when you iterate through a dictionary, the keys are returned in the order in which they were added. This order-preserving behavior simplifies many operations, making dictionaries even more versatile for tasks like data processing and maintaining a sense of order in your data structures. So, while in older Python versions, the order of keys was unpredictable, in Python 3.7 and beyond, you can rely on the order of keys in dictionaries.
Modifying Elements#
You can change values just like you would in a list
person["name"] = "Vlad"
print(person)
Show code cell output
{'name': 'Vlad', 'dob': '10/08/1994', 'age': 29}
Adding a new element#
You can also add new key-value pairs to a dictionary.
person["interests"] = ["Photography", "Chess"]
print(person)
Show code cell output
{'name': 'Vlad', 'dob': '10/08/1994', 'age': 29, 'interests': ['Photography', 'Chess']}
Here we used a new key, "interests"
, and assigned it a list value of ["Photography", "Chess"]
.
Iterating throught a Dictionary#
There are various ways to iterate over dictionaries. Let’s explore some examples.
Iterate over dict keys
# In this example we will iterate throught the values
for k in person:
print("Current key:", k)
print("Value for this key:", person[k])
Show code cell output
Current key: name
Value for this key: Vlad
Current key: dob
Value for this key: 10/08/1994
Current key: age
Value for this key: 29
Current key: interests
Value for this key: ['Photography', 'Chess']
Here, we use the syntax k in person
within a for
loop to iterate over all possible keys inside the dictionary.
Iterate over dict values
# In this example we will iterate throught the keys
for value in person.values():
print("Current value:", value)
Show code cell output
Current value: Vlad
Current value: 10/08/1994
Current value: 29
Current value: ['Photography', 'Chess']
Here, we use the .values()
method of the dict which generates a sequence of all values in the dict.
Iterate over dict key-value pairs
for k, v in person.items():
print(k, v)
Show code cell output
name Vlad
dob 10/08/1994
age 29
interests ['Photography', 'Chess']
Here, we use the .items()
method of the dict which generates a sequence of all key-value pairs of the dict.
Sometimes, there may be a need to convert either keys, values, or key-value pairs into a list. To accomplish this, we can use the following syntax:
keys = list(person.keys())
values = list(person.values())
items = list(person.items())
print("keys:", keys)
print("values:", values)
print("itmes:", items)
Show code cell output
keys: ['name', 'dob', 'age', 'interests']
values: ['Vlad', '10/08/1994', 29, ['Photography', 'Chess']]
itmes: [('name', 'Vlad'), ('dob', '10/08/1994'), ('age', 29), ('interests', ['Photography', 'Chess'])]
Checking for Key Existence#
We can check if a key exists in a dictionary using in
.
print("name" in person)
print("country" in person)
Show code cell output
True
False
Why is it important? If you try to access an element with the wrong key, you will receive an error. Here is an example:
print(person["Country"])
In this case we get the following error:
KeyError: 'Country'
Dictionary comprehension#
Last week, we explored list comprehension, a more advanced approach for creating lists. A similar method can be used for creating dictionaries. Let’s examine an example
letters = {i: chr(97 + i) for i in range(26)}
print(letters)
Show code cell output
{0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e', 5: 'f', 6: 'g', 7: 'h', 8: 'i', 9: 'j', 10: 'k', 11: 'l', 12: 'm', 13: 'n', 14: 'o', 15: 'p', 16: 'q', 17: 'r', 18: 's', 19: 't', 20: 'u', 21: 'v', 22: 'w', 23: 'x', 24: 'y', 25: 'z'}
Here, we set our key-value pair with the expression i: chr(97 + i)
. After that, we defined the interval for the i
value using the range
function.
Note
The ord()
and chr()
functions in Python are a dynamic duo for working with character data. ord()
stands for “ordinal” and is used to find the Unicode code point of a character. For example, ord('A')
returns 65, which is the code point for the uppercase ‘A’. On the other hand, chr()
is short for “character” and does the reverse - it converts a Unicode code point back into the corresponding character. So, chr(65)
will give you ‘A’. These functions are handy for text processing and character manipulation in various applications, from encoding to cryptography.
Try to complete the following task: you’re given a string. Your goal is to find the frequency of all unique elements, separated by whitespaces.
Here is possible implementation for some string line
freq_dict = {}
for w in line.split():
if w not in freq_dict:
freq_dict[w] = line.count(w)
print(freq_dict)
Sets#
A set is a collection of unique elements, just like a bag of distinct objects. In Python, we create sets using curly braces {}
or the set()
constructor.
digits = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
print(digits)
Show code cell output
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
Here, you can notice that we use the same braces as we do for dict
definition. However, in this case, we don’t use key: value
syntax, which signals to Python that this is a different data type. As always, you can verify the type of an object by using the built-in type
function.
print(type(digits))
Show code cell output
<class 'set'>
We can check whether an element is in the set by using the in
operator:
print(0 in digits)
print(10 not in digits) # here we check wheather 10 is not in the set by using 'not in'
Show code cell output
True
True
Set methods#
Here I will cover just some part of all possible set methods:
add()
- adds an element to the setclear()
- removes all elements from the setcopy()
- returns a copy of the setremove()
- removes the specified element from the set
numbers = {10, 20}
numbers.add(30)
numbers.add(10)
print("numbers after add:", numbers)
numbers.remove(10)
print("numbers after remove:", numbers)
values = numbers.copy()
print("values after copy:", values)
values.clear()
print("values after clear:", values)
Show code cell output
numbers after add: {10, 20, 30}
numbers after remove: {20, 30}
values after copy: {20, 30}
values after clear: set()
Set Operations#
Let’s create two sets:
A = {1, 2, 3, 4, 5}
B = {2, 4, 5, 6, 7}
Python allows basic operations on sets, working as expected from a mathematical perspective:
==
- checks whether two sets are equal (i.e., composed of the same elements)|
- represents the union of two sets&
- stands for set intersection-
- stands for set difference<=
- checks whether the left operand is a subset of the right one>=
- checks whether the right operand is a subset of the left one
print("A == B:", A == B)
print("A | B:", A | B)
print("A & B:", A & B)
print("A - B", A - B)
print("A <= B:", A <= B)
print("A >= B:", A >= B)
Show code cell output
A == B: False
A | B: {1, 2, 3, 4, 5, 6, 7}
A & B: {2, 4, 5}
A - B {1, 3}
A <= B: False
A >= B: False
Iterating Through a Set#
You can use for
loops to go through all elements in a set:
A = {1, 2, 3, 4, 5}
for elem in A:
print(elem)
Show code cell output
1
2
3
4
5
Warning
Sets in Python are unordered objects. This means there’s no guarantee that different runs of your program will have the same order of elements during iterations. You can verify this by running the following example several times:
elements = set(["ABC", "L" * 10, "B" * 5, "DEF"])
for elem in elements:
print(elem)