# Introductory Python: Lists, Dictionaries, and Arrays

*We can store many related pieces of data within a single variable called a data structure.*

*This post is part of the series Introduction to Python Programming.*

When programming, we often work with many related pieces of data. To be organized and efficient, we store them within a single variable called a **data structure.** This way, we can efficiently perform operations on the data structure as a whole rather than having to operate on each piece of data as a separate variable.

We will primarily be using 3 types of data structures: lists, dictionaries, and arrays.

**Lists**store pieces of data in a sequence, so that data can be referenced by its index in the list.**Dictionaries**store pieces of data under labels called*keys*, so that data can be referenced its key in the dictionary.**Arrays**store pieces of data in matrices and higher-dimensional matrices called*tensors*, so that data can be referenced by its index in the matrix or tensor.

## Lists

A list is defined by enclosing a sequence of data within brackets.

```
mixed_list = [5,-9,3.14,'a','b','c',True,False]
print(mixed_list)
OUTPUT
------
[5, -9, 3.14, 'a', 'b', 'c', True, False]
```

Two lists can be concatenated into a single list by adding them together.

```
print([1,2,3] + ['a','b','c'])
OUTPUT
------
[1, 2, 3, 'a', 'b', 'c']
```

## Exercise 1

Add the following lists in some order to create the list `['a','c',5,1.2,True]`

.

```
list1 = [True]
list2 = ['a','c']
list3 = [5,1.2]
# change the order of addition to create
# the list ['a','c',5,1.2,True]
print(list1 + list2 + list3)
OUTPUT
------
[True, 'a', 'c', 5, 1.2]
```

## Indexing

Referencing items in lists is very similar to picking out characters from strings – we can simply use the index of the item. (Really, a string is just a list of characters.)

Remember, the index just counts how many items are before our desired item – so the first item has an index of 0, the second item has an index of 1, the third item has an index of 2, and so on.

```
mixed_list = [5,-9,3.14,'a','b','c',True,False]
print(mixed_list[0])
OUTPUT
------
5
```

```
mixed_list = [5,-9,3.14,'a','b','c',True,False]
print(mixed_list[3])
OUTPUT
------
a
```

## Exercise 2

Reference the item `'c'`

in `mixed_list`

using its index in the list.

```
mixed_list = [5,-9,3.14,'a','b','c',True,False]
# change the index to reference the item 'c'
print(mixed_list[1])
OUTPUT
------
-9
```

## Sublists

Just as substrings can be picked out of strings, sublists can be picked out of sublists by using two indices and a colon.

Remember, the first index tells us where we should start picking out items, and the second index tells us where we should stop picking out items. (The item located at the second index is not included in the sublist.)

```
mixed_list = [5,-9,3.14,'a','b','c',True,False]
print(mixed_list[2:4])
OUTPUT
------
[3.14, a]
```

## Exercise 3

Pick out the sublist `['a','b','c']`

from `mixed_list`

by using two indices and a colon.

```
mixed_list = [5,-9,3.14,'a','b','c',True,False]
# change the two indices so that the resulting
# sublist is ['a','b','c']
print(mixed_list[2:4])
OUTPUT
------
[3.14, a]
```

## Functions and Methods

There are various functions and methods that can be used to process lists.

For example, the `len()`

function tells us how many items are in a list.

```
mixed_list = [5,-9,3.14,'a','b','c',True,False]
print(len(mixed_list))
OUTPUT
------
8
```

Likewise, the `.insert(i,x)`

method inserts the item `x`

into the list so that it occupies index `i`

.

```
alphabet_missing_c = ['a','b','d','e']
alphabet_missing_c.insert(2,'c')
print(alphabet_missing_c)
OUTPUT
------
['a', 'b', 'c', 'd', 'e']
```

There are many functions and methods that can be applied to lists. As such, we will not go through all of them.

However, it is important to know how to find a function or method when you need it. You can usually find them by searching Google.

## Exercise 4

Use functions or methods to perform the desired operations on the following lists.

```
counting_up = [1,2,3,4,5]
counting_down = # reverse the list so that it is counting down
print(counting_down)
DESIRED OUTPUT
--------------
[5,4,3,2,1]
```

```
has_lots_of_Es = ['a','b','e','c','d','e','e','f','e','e']
num_Es = # count how many items in the list have the value 'e'
print(num_Es)
DESIRED OUTPUT
--------------
5
```

## Dictionaries

A dictionary is defined by enclosing a sequence of `key:value`

pairs within braces.

```
my_dict = {'Leroy':5, 'Amy':2, 'Pete':True, 'Carly':'hi'}
print(my_dict)
OUTPUT
------
{'Amy': 2, 'Carly': 'hi', 'Leroy': 5, 'Pete': True}
```

To reference an item in a dictionary, we can simply use the key of that item.

```
my_dict = {'Leroy':5, 'Amy':2, 'Pete':True, 'Carly':'hi'}
print(my_dict['Amy'])
OUTPUT
------
2
```

## Exercise 5

Reference the item in `my_dict`

corresponding to the key `'Pete'`

.

```
my_dict = {'Leroy':5, 'Amy':2, 'Pete':True, 'Carly':'hi'}
print(my_dict['Amy']) # change the key 'Amy' to the key 'Pete'
DESIRED OUTPUT
--------------
True
```

## Modifying Dictionaries

To add a new item to a dictionary, we can reference a new key and assign a value to it just like we would assign a new value to a variable.

```
my_dict = {'Leroy':5, 'Amy':2, 'Pete':True, 'Carly':'hi'}
# create a key 'Jaspreet' with value 9
my_dict['Jaspreet'] = 9
# dictionary now contains a new key:value pair!
print(my_dict)
OUTPUT
------
{'Amy': 2, 'Carly': 'hi', 'Jaspreet': 9, 'Leroy': 5, 'Pete': True}
```

We can use the same technique to change the value of an existing key in a dictionary.

```
my_dict = {'Leroy':5, 'Amy':2, 'Pete':True, 'Carly':'hi'}
# re-assign the key 'Pete' to the value False
my_dict['Pete'] = False
# dictionary has been modified
print(my_dict)
OUTPUT
------
{'Amy': 2, 'Carly': 'hi', 'Leroy': 5, 'Pete': False}
```

## Exercise 6

Add a key-value pair `'e':3`

to `my_dict`

, and change the value of the key `'a'`

to 4.

```
my_dict = {'a':8, 'b':6, 's':5, 'i':1}
# insert some code here to add a key-value pair 'e':3
# insert some code here to change the value of the key 'a' to 4
print(my_dict)
DESIRED OUTPUT
--------------
{'a': 4, 'b': 6, 'e': 3, 'i': 1, 's': 5}
```

## Dictionary Functions and Methods

There are various functions and methods that can be used to process dictionaries.

For example, the `len()`

function tells us how many `key:value`

pairs are in the dictionary.

```
my_dict = {'Leroy':5, 'Amy':2, 'Pete':True, 'Carly':'hi'}
print(len(my_dict))
OUTPUT
------
4
```

Likewise, the `.pop(keyname)`

method allows us to remove the `key:value`

pair corresponding to the key `keyname`

.

```
my_dict = {'Leroy':5, 'Amy':2, 'Pete':True, 'Carly':'hi'}
my_dict.pop('Pete') # remove Pete
print(my_dict) # Pete is gone!
OUTPUT
------
{'Amy': 2, 'Carly': 'hi', 'Leroy': 5}
```

## Arrays

To use arrays, we need to import the NumPy module that contains the code needed to create and manipulate arrays. We do this by adding `import numpy`

at the top of our code.

Then, we can call `numpy.array(`

)</code> to create a matrix array.

```
import numpy
my_array = numpy.array([[1,2,3],[4,5,6],[7,8,9]])
print(my_array)
OUTPUT
------
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
```

We can reference the entry in the *i*-th row and *j*-th column of the array using the notation `[i,j]`

. Remember to start counting with 0.

```
import numpy
my_array = numpy.array([[1,2,3],[4,5,6],[7,8,9]])
print(my_array[0,0])
OUTPUT
------
1
```

```
import numpy
my_array = numpy.array([[1,2,3],[4,5,6],[7,8,9]])
print(my_array[1,2])
OUTPUT
------
6
```

## Exercise 7

Reference the entry in the bottom-left corner of the array (which is 7) using `[i,j]`

notation.

```
import numpy
my_array = numpy.array([[1,2,3],[4,5,6],[7,8,9]])
# change this [i,j] notation to reference the entry
# in the bottom-left corner (which is 7)
print(my_array[1,2])
DESIRED OUTPUT
--------------
7
```

## Subarrays

We can reference subarrays within arrays just like we reference sublists within lists.

```
import numpy
my_array = numpy.array([[1,2,3],[4,5,6],[7,8,9]])
# get the 2x2 square in the top-left corner
subarray = my_array[0:2,0:2]
print(subarray)
OUTPUT
------
array([[1, 2],
[4, 5]])
```

## Exercise 8

Reference the 2x2 square in the top-right corner of the array in the bottom-left corner of the array (which is $\begin{bmatrix} 2 & 3 \\ 5 & 6 \end{bmatrix}$) using subarray notation.

```
import numpy
my_array = numpy.array([[1,2,3],[4,5,6],[7,8,9]])
# change the subarray notation to get the 2x2 square
# in the top-right corner
subarray = my_array[0:2,0:2]
print(subarray)
DESIRED OUTPUT
--------------
array([[2, 3],
[5, 6]])
```

## Array Arithmetic

Arrays of the same shape can be added and subtracted.

```
import numpy
A = numpy.array([[10,10],[20,20]])
B = numpy.array([[1,2],[3,4]])
print(A + B)
OUTPUT
------
array([[11, 12],
[23, 24]])
```

```
import numpy
A = numpy.array([[10,10],[20,20]])
B = numpy.array([[1,2],[3,4]])
print(A - B)
OUTPUT
------
array([[ 9, 8],
[17, 16]])
```

## Caution 1

Arrays of the same shape can also be multiplied and divided. But the multiplication and division is carried out separately in each component – so array multiplication is not the same as the matrix multiplication procedure you may have learned in math class.

```
import numpy
A = numpy.array([[10,10],[20,20]])
B = numpy.array([[1,2],[3,4]])
print(A * B)
OUTPUT
------
array([[1, 2],
[6, 8]])
```

```
import numpy
A = numpy.array([[10,10],[20,20]])
B = numpy.array([[1,2],[3,4]])
print(A / B)
OUTPUT
------
array([[1. , 2. ],
[1.5, 2. ]])
```

## "Improper" Array Operations

Some seemingly improper array operations are allowed if there is a way to make intuitive sense of them.

For example, if a single number is added to an array, then the number is added to each entry of the array.

```
import numpy
A = numpy.array([[1,2],[3,4]])
print(A + 10)
OUTPUT
------
array([[11, 12],
[13, 14]])
```

Likewise, if a single row is added to a square array of the same length, then the row is added to each row of the square array.

```
import numpy
A = numpy.array([[1,2],[3,4]])
b = numpy.array([10,20])
print(A + b)
OUTPUT
------
array([[11, 22],
[13, 24]])
```

## Exercise 9

Change array `B`

so that `A + B`

comes out to a matrix whose entries are all `10`

.

```
import numpy
A = numpy.array([[1,2],[3,4]])
# change the entries in B so that A + B has
# all entries equal to 10
B = numpy.array([[1,1],[1,1]])
print(A + B)
DESIRED OUTPUT
--------------
array([[2, 3],
[4, 5]])
```

## Array Methods and Attributes

There are various methods and attributes that can be used to process arrays.

(You can think of attributes as being the same as methods, but without parentheses.)

For example, the `.max()`

method tells us the maximum entry in the array.

```
import numpy
A = numpy.array([[1,2],[3,4]])
print(A.max())
OUTPUT
------
4
```

Also, the `.dot()`

method allows us to compute a product with another array in the mathematical sense of matrix multiplication.

```
import numpy
A = numpy.array([[1,2],[3,4]])
b = numpy.array([[5],[6]])
print(A.dot(b))
OUTPUT
------
array([[17],
[39]])
```

Likewise, the `.shape`

attribute tells us how many rows and columns an array has.

```
import numpy
A = numpy.array([[1,2],[3,4],[5,6]])
print(A)
print()
print(A.shape)
OUTPUT
------
array([[1, 2],
[3, 4],
[5, 6]])
(3, 2)
```

There are many methods that can be applied to arrays. As such, we will not go through all of them.

However, it is important to know how to find a method when you need it. You can usually find them by searching Google.

## Exercise 10

Use methods to perform the desired operations on the following arrays.

```
import numpy
A = numpy.array([[1,2],[3,4]])
# apply a method to find the minimum entry in the array
DESIRED OUTPUT
--------------
1
```

```
import numpy
A = numpy.array([[1,2],[3,4]])
# apply a method to sum all the entries in the array
DESIRED OUTPUT
--------------
10
```

## Tensors

We can also stack two 2x2 matrices to form a 2x2x2 cube of numbers, called a 3-dimensional tensor.

```
import numpy
bottom_layer = numpy.array([[1,2],[3,4]])
top_layer = numpy.array([[5,6],[7,8]])
cube = numpy.stack((bottom_layer,top_layer))
```

Indexing for a tensor is the same as indexing for a matrix – except that the tensor has more dimensions.

When stacking matrices to form a tensor, the new “depth” dimension becomes the first dimension.

```
print(cube[0,0:2,0:2]) # bottom layer
OUTPUT
------
array([[1, 2],
[3, 4]])
```

```
print(cube[1,0:2,0:2]) # top layer
OUTPUT
------
array([[5, 6],
[7, 8]])
```

## Exercise 11

Adjust the indices referenced in the cube so that the resulting matrix cross-section is $\begin{bmatrix} 3 & 4 \\ 7 & 8 \end{bmatrix}.$ (Hint: take a cross-section that cuts the cube along a different dimension.)

## Solutions

**Exercise 1**

```
list1 = [True]
list2 = ['a','c']
list3 = [5,1.2]
print(list2 + list3 + list1)
OUTPUT
------
['a', 'c', 5, 1.2, True]
```

**Exercise 2**

```
mixed_list = [5,-9,3.14,'a','b','c',True,False]
print(mixed_list[5])
OUTPUT
------
c
```

**Exercise 3**

```
mixed_list = [5,-9,3.14,'a','b','c',True,False]
print(mixed_list[3:6])
OUTPUT
------
['a','b','c']
```

**Exercise 4**

```
counting_up = [1,2,3,4,5]
counting_down = counting_up.reverse()
print(counting_down)
OUTPUT
------
[5,4,3,2,1]
```

```
has_lots_of_Es = ['a','b','e','c','d','e','e','f','e','e']
num_Es = has_lots_of_Es.count('e')
OUTPUT
------
5
```

**Exercise 5**

```
my_dict = {'Leroy':5, 'Amy':2, 'Pete':True, 'Carly':'hi'}
print(my_dict['Pete'])
OUTPUT
------
True
```

**Exercise 6**

```
my_dict = {'a':8, 'b':6, 's':5, 'i':1}
my_dict['e'] = 3
my_dict['a'] = 4
print(my_dict)
OUTPUT
------
{'a': 4, 'b': 6, 'e': 3, 'i': 1, 's': 5}
```

**Exercise 7**

```
import numpy
my_array = numpy.array([[1,2,3],[4,5,6],[7,8,9]])
print(my_array[2,0])
OUTPUT
------
7
```

**Exercise 8**

```
import numpy
my_array = numpy.array([[1,2,3],[4,5,6],[7,8,9]])
subarray = my_array[0:2,1:3]
print(subarray)
OUTPUT
------
array([[2, 3],
[5, 6]])
```

**Exercise 9**

```
import numpy
A = numpy.array([[1,2],[3,4]])
B = numpy.array([[9,8],[7,6]])
print(A + B)
OUTPUT
------
array([[2, 3],
[4, 5]])
```

**Exercise 10**

```
import numpy
A = numpy.array([[1,2],[3,4]])
print(A.min())
DESIRED OUTPUT
--------------
1
```

```
import numpy
A = numpy.array([[1,2],[3,4]])
print(A.sum())
DESIRED OUTPUT
--------------
10
```

**Exercise 11**

```
import numpy
bottom_layer = numpy.array([[1,2],[3,4]])
top_layer = numpy.array([[5,6],[7,8]])
cube = numpy.stack((bottom_layer,top_layer))
print(cube[0:2,1,0:2])
OUTPUT
------
array([[3, 4],
[7, 8]])
```

*This post is part of the series Introduction to Python Programming.*