# 2D Lists in python
:::info
This is a note part of the coding course for IB Computer Science program. If you want to check out other parts of the same course you can see them in [This link of teaching material](/68GDv_RgT-yh9oERMvdnFw)
:::
:::warning
:warning: Note still under construction :warning:
:::
## 2 Dimensional lists in python
This is going to remind you of excel/google sheets/libre calc.

They are like tables and they are lists of lists. Because, yeah, you can have lists inside a list.
Let's say that we have 4 students with
This is how it looks like:
```python
names = ["Pepa", "Pipo", "Pepe", "Thomas Jefferson"]
```
And they do one exam of Bussiness, here we have their grades
```python
bussiness = [4,6,6,5]
```
And then they got an exam on Math, here we have their grades
```python
math = [6,3,7,4]
```
And then they got an exam on Spanish A, here we have their grades
```python
spanish = [4,6,4,3]
```
And then they got an exam on English B, here we have their grades
```python
english = [7,6,7,7]
```
And of course, Computer Science exam:
```python
computer = [5,6,6,7]
```
:::info
If we want to know, for example which grades Pepa had, we need to go to the **first element** of each element. So she had a 4 in Bussiness, a 6 in Math, a 4 in Spanish A, a 7 in English B and a 5 in Computer Science.
(Can you find who got that 3 in Spanish A?)
:::
Now, if we have more and more exams having lots of variables it's going to be a mess (it already is), so for that, we can have just a list of lists that is called `grades`. And it would be defined as a lists of list. If we do a new exam, we only need to append the data to that list!
```python
names = ["Pepa", "Pipo", "Pepe", "Thomas Jefferson"]
grades = [[4,6,6,5], [6,3,7,4],[4,6,4,3],[7,6,7,7],[5,6,6,7]]
```
As you can see you can write them with one set of brackets `[]` that have inside several sets of brackets.
:::info
**Limitations of 2DLists in python**
lists in python are a bit slow in comparasion with arrays (that they have fixed size in memory). For that, however, in many cases you can use numpy, that is a library that includes a type of arrays.

https://numpy.org/devdocs/user/whatisnumpy.html
The idea is that behind the library there is some pre-compiled code written in c.
For data science this is a basic library with pandas.
But for some small programs the lists of python can work as well.
:::
### Creating a 2D list
One way was the one that you have just seen:
```python
grades = [[4,6,6,5], [6,3,7,4],[4,6,4,3],[7,6,7,7],[5,6,6,7]]
```
Also identing with python can help you to be more clear and write something like this:
```python
grades = [[4,6,6,5],
[6,3,7,4],
[4,6,4,3],
[7,6,7,7],
[5,6,6,7]]
```
that, of course you can comment if you need:
```python
grades = [[4,6,6,5], #bussiness
[6,3,7,4], #math
[4,6,4,3], #spanish
[7,6,7,7], #english
[5,6,6,7]] #Computer
```
You can also create the lists and then append them in an empty list.
```python
bussiness = [4,6,6,5]
math = [6,3,7,4]
spanish = [4,6,4,3]
english = [7,6,7,7]
computer = [5,6,6,7]
grades = list()
grades.append(bussiness)
grades.append(math)
grades.append(spanish)
grades.append(english)
grades.append(computer)
```
We can create the list also on the go while appending
```python
bussiness = [4,6,6,5]
grades = list()
grades.append(bussiness)
grades.append([6,3,7,4])
```
Other times you can fetch the information with some libraries and the result is given to you in a cozy 2D list. In the exam you're not supposed to know how to do it (unless they explain to you) but in regular programming you may get a n dimensional list (for example analyzing an image).
In our example we could get grades with some kind of connection (I'm inventing here) that should be defined elsewhere in the code.
```python!
grades = connectionToMoodle.get(securityToken, type= "Grades ",class="IBclassFromPepa", progress = False)
```
And it just fills it up. In an Internal this is a good analysis of where you have a 2D colection that you made yourself.
### Creating 2D list with zeros
#### List comprehension to create an "empty" list
Sometimes we want to create a list that is filled with a specific element.
To create empty lists (with zeros) we can use list comprehension
For example if we want to create a list with 10 zeros we can write
```python=
myList = list(0 for x in range(10))
```
This is the same as writing:
```python=
myList = [0 for x in range(10)]
```
What it does is going to iterate x in the range(10) (so 10 times) and each of them is going to write a zero. The result will be sent to the constructor of lists `list()`
If we want empty strings (`""`) we can write
```python=
myList = list("" for x in range(10))
```
If we want to make it in 2D we need to double it. So if we want to create 3 rows of 10 columns of zeros we write this:
```python=
myList = list( list(0 for x in range(10)) for y in range(3))
```
#### Instanciatin without list comprehension
If you don't know or don't care about list comprenshesion you can still create a new list with a specific value (such as `0` or `""`)
```python=
myList = []
rows = 7 #number of rows
columns = 5 #number of columns
for x in range(rows):
myList.append([]) #append a new row
for y in range(columns):
myList[x].append(0) #append in that row the value
```
#### Exercise
Construct in python the code to create a list called `potato` that consists in the word "potato" written in a 2D list that has 5 rows and 6 columns.
### Accessing an element of a 2D list
For that we're going to use our good ol' friends: the indexes.

_[source](https://www.youtube.com/watch?v=-XzeZhFvEmk)_
To access an element of the 2Dlist we need to access first to the inner list, using an index (with brackets) and then the element of that inner list using a second index (with brackets).
The first element of all would be `mylist[0][0]` for example.
:::info
Notation:
Usually the outer part of the list are the "rows" of that list.
:::
Each of the element will have this 2 indexes unique to them, like a coordinate system.

_[source](https://www.informit.com/articles/article.aspx?p=2963463&seqNum=16)_
In this case the 0,0 is up left and the biggest positive number will be bottom right.
So going back to our example of the grades of Pepa, Pipo, Pepe and Thomas Jefferson
```python
grades = [[4,6,6,5],
[6,3,7,4],
[4,6,4,3],
[7,6,7,7],
[5,6,6,7]]
```
Remind that you can use still negative indexes for this.
```python
#grades already defined
grades[0][0] #first element of the first list, so it's a 4
grades[-1][0] #first element of the last list so it's a 5
grades[3][-1] #last element of the 4th list
```
:::info
Vocabulary:
The plural of "index" is "indices" (like in Spanish) but also you can name *subscripts*. In some exams this has been seen, so be aware!
:::
#### Small exercises of accessing.
Given the grades that we have already, write how we access:
* The grade that Pepa had in Math.
* The grades that are a "5" (one at a time)
* The grades that are a "3"
### Traverse a 2D list
We already have traversed lists we need to use iteration.
In this case if we loop for the 2D list we are going to print all the sublists (all the "rows")
For example:
```python!
#grades already defined
for row in grades:
print(row)
```
We usually go with something that has meaning, such us row or in this case `exam`
If we want to actually access all the grades in grades (each specific grade) then we need an inner loop, a loop inside the loop.
```python!
#grades already defined
for row in grades:
for data in row:
print(data)
```
Or using more context specific names:
```python=
#grades already defined
for exam in grades:
for grade in exam:
print(grade)
```
If we need to use indexes we need to use range. Let's do it again
To access the specific list:
```python!
#grades already defined
for rowIndex in range(len(grades)):
print(rowIndex, grades[rowIndex])
```
This print the indexes of the rows and the rows themselves.
If we want to access all the element we need to go deeper and use the other loop:
```python
#grades already defined
for rowIndex in range(len(grades)):
for colIndex in range(len(grades[rowIndex])):
print(rowIndex, colIndex)
```
:::warning
I know that this implementation is a **mess** being the other one so much simple but you need to know it also.
:::
:::danger
**How not to traverse a 2D array**
If we try this:
```python!
bit_image= [
[1,1,1,1,1],
[1,0,1,0,1],
[1,1,1,1,1],
[0,1,1,1,0],
[1,0,0,0,1],
[1,1,1,1,1],
]
for x, y in bit_image:
print(x, y)
```
This will be the error:

It's _almost_ never going to work. What the interpreter is going to try is to put the whole list in 2 values, but bit_image has 6 values (six lists of 5 elements to be precise). So **don't use it**
:::
### First batch of exercises (simple)
#### Sum all the elements
Sum all the grades from Pipo and company.
For that we need a counter (let's call it sum) and then sum the elements **traversing** the 2D list.
```python=
sum = 0
for exam in grades:
for grade in exam:
sum += grade
print(sum)
```
#### Count how many 7 have been given in these exams
We need a counter that starts with 0
```python=
count = 0
for exam in grades:
for grade in exam:
if grade == 7:
count+=1
print(count)
```
#### Count all the elements
Here we don't need to do the whole loop since we can multiply the len of (for example) the first list and then the length of the outer list with `len()`
```python=
print(len(grades)*len(grades[0]))
```
#### Average all the elements
Sum then divide by the total number of elements.
```python=
elements = len(grades)*len(grades[0])
print(sum(grades[0]))
sum = 0
for exam in grades:
for grade in exam:
sum += grade
print(sum/elements)
```
#### Minimum/maximum within all the elements
We need to remember how we do minimums and maximums in 1Dlists.
We usually assume that the first elemen is the one that we're looking for and then **traverse** the 2D list. If we find a value that is better we change our guess.
Option with only the value:
```python!
minimum = grades[0][0]
for exam in grades:
for grade in exam:
if grade < minimum:
minimum = grade
print(minimum)
```
#### Partial results by column or row
If we have a 2dlist scores[6][5] it means that we have 6 rows and 5 columns. So the total number of elements will be 30. Usually in this cases the rows are the students and the columns the scores. So we have 6 students with 5 scores each.
Output all the scores from the students. We have another array with names[6] where the name of the student is.
//TO-DO by the students
### Villains exercise
We have the spendings of several villains over a year from Evil co.
The villains are the following:
```python
villains = ["Placton", "Darth Vader", "Moriarty", "Ender Dragon", "Úrsula"]
```
And we have the monthly expenses over a year, in this 2D list:
```python
spendings = [
[100, 100, 100, 200, 150, 200, 300, 100, 150, 100, 102, 140],
[500, 200, 500, 200, 550, 100, 500, 100, 580, 100, 600, 300],
[20, 20, 100, 40, 500, 10, 10, 40, 10, 20, 80, 40],
[0, 0, 0, 9, 0, 0, 1000, 0, 0, 0 ,0 , 4],
[300, 100, 100, 100, 200, 102, 300, 100, 103, 150, 102, 103]]
```
#### Find the averages (year averages)
Find the averages of each villain and fill a list called "yearAverages" following the same order as the villains
Solution
:::spoiler
So for doing that we're going to create an empty list called yearAverages and then fill them with the average of each list. So we loop for the each of the rows and then for each of the elements to find the average.
```python=
yearAverages = []
for row in spendings:
sum = 0
for element in row:
sum += element
yearAverages.append(sum/len(row))
```
:::info
**Do I have to use len() if I know that is 12 the length already?**
No and yes. It's better that you use len() because is more consistent. But sometimes in some specific situations you can work (or give you a mark in an exam).
:::
Other implementation:
//TO-DO
:::
#### Find the month totals
Now we want a list called `monthTotals` with the total spendings of each month and **output** it.
Solution
:::spoiler
```python=
monthTotals= []
for row in spendings:
for colIndex in range(len(row)):
if colIndex == len(monthTotals):
monthTotals.append(0)
monthTotals[colIndex]+=row[colIndex]
print(monthTotals)
```
In this case we create the empty list. Then for each row we're going to check each **index** of the column. The first time we need to check if the index is bigger than the size of the list to add a 0. We add a zero because later we're going to add the different totals.
:::success
Using list comprehension for creating the emtpy list:
```python=
monthTotals = list(0 for x in range(12))
for row in spendings:
for colIndex in range(len(row)):
monthTotals[colIndex]+=row[colIndex]
print(monthTotals)
```
:::
:::
#### Find the month with the most spending
Similar to this, we need to output the index of the month with the most spending (total)
Solution
:::spoiler
```python=
monthTotals= []
for row in spendings:
for colIndex in range(len(row)):
if colIndex == len(monthTotals):
monthTotals.append(0)
monthTotals[colIndex]+=row[colIndex]
counter = monthTotals[0]
for month in monthTotals:
if month > counter:
counter = month
print(monthTotals.index(counter))
```
_Credit D.P._
:::
#### Find the Villain who spends the least and how much did they spent
Construct in python an algorithm that outputs the **name** of the villain who has spent the least during the year.
Solution
:::spoiler
```python=
monthTotals= []
for row in spendings:
for colIndex in ange(len(row)):
if colIndex = len(monthTotals):
monthTotals.append(0) monthTotals[colIndex]+=row[colIndex]
#filled the totals of each mohtn
minimum = monthTotals[0]
for monthSpending in monthTotals:
if monthSpending > minimum:
minimum = monthSpending
#beware that the name is held in villains
print(villains[monthTotals.index(minimum)])
```
:::
#### Summer Villain
Construct in python an algorithm that outputs the **name** of the villain who has spent the most during Nothern hemisphere Summer months (June/July/August/September)
:::info
In an exam they usually give you one of this examples and they ask you 2 elements of this.
:::
Solution
:::spoiler
Remember slicing? Now it's back
```Python=
summerAverages = []
for row in spendings:
sum = 0
for element in row[5:10]: #slicing the row to get only the summer
sum += element
summerAverages.append(sum/len(row))
x = 0
i = 0
for index in range(len(summerAverages)):
if x < summerAverages[index]:
x = summerAverages[index]
i = index
print(villains[i])
```
_Credit V.K._
:::
### Matrixes concepts
In IB Math they don't teach matrixes and it's a pity:

_Great movie and also analogy of trans experience by 2 trans authors_
Now, matrixes in math are numbers in a bidimensional way and you can write them usually between parenthesis like this:
$$A = \begin{bmatrix}
a_{11} & a_{12} & \cdots & a_{1n} \\
a_{21} & a_{22} & \cdots & a_{2n} \\
\vdots & \vdots & \ddots & \vdots \\
a_{m1} & a_{m2} & \cdots & a_{mn} \\ \end{bmatrix}_{ m\times n}$$
And an example would be this matrix
$$
A =
\begin{bmatrix}
1 & 2 & 3 \\
1 & 2 & 7 \\
4 & 9 & 2 \\
6 & 0 & 5 \\
\end{bmatrix}
$$
That, well. We have rows, columns. It's another notation as we have written in python this:
```python=
a = [
[1,2,3],
[1,2,7],
[4,9,2],
[6,0,5],
]
```
So a you can think of a bidimensional matrix as a 2Dlist (or a 2D array) with columns and rows. I'm not going to enter into all the math stuff (how we can operate with matrixes?)
#### Square 2D lists (or matrix)
A square matrix is a matrix that has the same number of rows than columns. And like regular squares they have... diagonals!
#### Diagonal (main and secondary)
:::info
This types of question happens from time to time in IB. They usually explain it in the exam, but if you are familiar with it already that's being one step ahead!
:::
These are the primary (or main) diagonal and the secondary diagonal of a square matrix (or 2dList):

So the main diagonal are the items that they have the same index for the columns and rows.
The elements of the secondary diagonal are those whose sum of both indexes is the maximum index.
#### Traverse a main diagonal
```python=
mat = [
[1,2,3],
[1,2,7],
[4,9,2],
]
```
To loop (and print) for all the elements of a 2D squared list (`mat`) you can use:
```python
for index in len(mat):
print(mat[index][index])
```
If you want to do the same for the secondary diagonal is a bit more tricky
```python
for index in len(mat)
print(mat[index][len(mat)-1-index])
```
or, using negative indexes:
```python
for index in len(mat)
print(mat[index][-1-index])
```
#### Partial results by diagonals
Construct in python a program that adds all the elements in both diagonals of the 2D list `mat`
//TO-DO by the students
#### Triangular matrixes
We may also want to represent data with a lots of zeros, that is common in something called triangular and diagonal matrixes. We're not going to deal with diagonal matrixes (because we could just use a 1D list for it).

We can check that something is in the diagonal if both indexes are the same.
We can check if a value is in the lower triangle if the index of the rows is bigger than the index of columns
Ans finally, we can check if a value is in the upper triangle if the index of the columns is bigger thant the index of the rows.
##### Exercise
Construct a 2D list size 15x15 whose values in the upper triangle are "up", the values in the lower triangle are "down" and the values in the diagonal are "diagonal". Output it row by row.
Something like this:
```python=
mat = [
["diagonal","up","up"],
["down","diagonal","up"],
["down","down","diagonal"],
]
```
But bigger!
//TO-DO by the students
Where you can find this? Well, in distances charts!
### Distances exercise
We have this list of towns:
```python!
towns = ["Rabat", "Lagos", "Kyoto", "Zaragoza", "Lyon", "Palermo"]
```
And we have a 2D list where we store the information about how to get from 1 town to the next one in _elf hours_ (the hours that an elf needs to go with magic with one place to the other)
```python!
distances = [
[0,0,0,0,0,0],
[3,0,0,0,0,0],
[2,7,0,0,0,0],
[3,8,8,0,0,0],
[1,9,5,6,0,0],
[8,4,4,2,1,0],
]
```
If you see this is a triangular matrix, lower triangular matrix to be precise.
In the main diagonal all the values are 0 because the distance between "Kyoto" and itself is... 0, naturally.
The distance between 2 elements is only written once because the distance between Lagos and Palermo is the same as the distance between Palermo and Lagos. If we want _consistency_ is better not to have duplicated data.
The distance betwen Palermo and Lagos is stored in [5,1] and it's 4.
What is the distance between Rabat and Lagos?
//TO-DO by the students
What is the distance between Lyon and Zaragoza?
//TO-DO by the students
#### Validating the matrix
:::info
Validating is a common topic for IB exams using 2Dlists, we may have different considerations.
:::
In this case we need to make sure that the 2d list is valid and all the data is in the lower triangle.
Construc in python the method "isValid(distances)" that is going to return true if all the values in the upper triangle and in the diagonal are 0 and the values that are in the lower diagonal are bigger than 0 (we don't want to have negative distances!)
So:
```python!
distances = [
[0,0,0,0,0,0],
[3,0,0,0,0,0],
[2,7,0,0,0,0],
[3,8,8,0,0,0],
[1,9,5,6,0,0],
[8,4,4,2,1,0],
]
```
Should return `True` but
```python!
distances = [
[0,0,0,0,0,0],
[3,0,0,0,0,0],
[2,7,0,0,0,0],
[3,8,8,1,0,0],
[1,9,5,6,0,0],
[8,4,4,2,1,0],
]
```
Should return `False` as
```python!
distances = [
[0,0,0,0,0,0],
[3,0,0,0,0,0],
[2,7,0,0,0,0],
[3,8,8,0,0,0],
[1,9,5,6,0,0],
[0,4,4,2,1,0],
]
```
//TO-DO by the students
#### Construct a query
Construct a function in Python that receives a couple of names and, if both of them are in the list it's going to return the distance. If any is missing it's going to return -1
//TO-DO by the students
#### Finding the farthers city
What is the farthest city from Lagos?
//TO-DO by the students
Construct an algorithm in python that finds the farthest city from Kyoto and outputs its distance and name.
### Images
Images can be thought also as 2D lists. If you need more info in [A.1.2. Notes and resources](/W_rCaPb-Sy-QabJVLhnCZg) you can find mor information.
Let's suposse that we're working with a small image where 0 are black and 1 are white (1bit pixels)
We can define an image as something like this:
```python=
bit_image= [
[1,1,1,1,1],
[1,0,1,0,1],
[1,1,1,1,1],
[0,1,1,1,0],
[1,0,0,0,1],
[1,1,1,1,1],
]
```
This is a smiley face 5x6
Now we want to rotate the image 90 degrees to the left so we end with
```python=
bit_image_rotated= [
[1,1,0,1,1,1],
[1,0,1,1,0,1],
[1,0,1,1,1,1],
[1,0,1,1,0,1],
[1,1,0,1,1,1],
]
```
Construct in Python that given bit_image constructs bit_image_rotated.
```python=
newImage = []
for col in range(len(bit_image[0])):
new_row = []
for row in bit_image:
new_row.append(row[col])
newImage.append(new_row[::-1])
```
_credit D.P._
```python!
new=[[] for i in range(len(bit_image[0]))]
#creating a list of empty lists with as many rows as columns in the original image
for y in range(len(bit_image[0])):
for x in bit_image: #this is the original one
new[y].append(x[-1-y])
```
_credit E.C._
:::success
**Trasposing**
This is called, apart from rotating, **trasposing**.
Traspose is to interchange rows with columns. You can see it also in excel as a tool.


:::
### Compresing algorithm with RLE
Now let's do some exercise with compression, a topic that we cover also in [A1.1 Notes and resources](/6LgCmT4IToOkOu0CuV3UBQ). Remember that you should be able to talk about compression, lossless compresion and this algorithm in particular, **Run-Length Encoding**
For the context of this exercise we're going to consider 1 bit depth images. That is, images whose pixels can be only **black** or _white_.
Run length encoding is going to group repeated data with a number of times that happens the same.
So if we have a pixel image represented with 0 and 1s like this
```python=
lineImage = [0,0,0,0,0,1,1,1,1,1,1,0,0,0]
```
It's going to compress using this (denary) logic of counting the number of 0 or 1s, multplying by ten and add them 1 if it was a 1 what we're compressing.
The previous "line" image would be compressed like this
```python=
lineImage = [50,61,30]
```
From a list whose length was 14 digits, now we have 6 digits. Not bad!
Now try with this image that is 2Dimensional
```python=
bit_image= [
[1,1,1,1,1],
[1,0,1,0,1],
[1,1,1,1,1],
[0,1,1,1,0],
[1,0,0,0,1],
[1,1,1,1,1],
]
```
In this case we could go line by line or we could go using diagonals ([like in the JPG compression video that we saw](https://www.youtube.com/watch?v=Kv1Hiv3ox8I)). But for now let's keep it row by row.
The compressed would be this:
```python=
compressed_bit_image= [
[51],
[11,0,11,0,11],
[51],
[0,31,0],
[11,30,11],
[51],
]
```
You noticed that the second line it wasn't compressed at all! And that is true, but the rest could be compressed.
In the 10x10 grid below is an image of a simple heart. Convert the pixel grid into RLE format.

_Credit: Andrew DiGiovanni_
//TO-DO by the students
#### The encoder
Construct in python a function that given a 2Dlist you return a compress version of it following the instructions given.
You may consider that the 2D list is valid.
//TO-DO by the students
#### The mystery image
Now let's try to reverse the process.
```python=
compressed_mistery_image= [
[101],
[31,40,31],
[21,60,21],
[11,20,41,20,11],
[11,20,41,20,11],
[21,60,21],
[31,40,31],
[101],
]
```
_adapted from Andrew DiGiovanni_
From this 2D list, **draw the corresponding image**(10x10). For this I suggest to actually draw the image in some grid paper.
//TO-DO by the students
#### The decoder
Now in python a function that given a 2Dlist you return a decompressed version of it following the instructions given.
You may consider that the 2D list is valid.
//TO-DO by the students
### Exam like exercise. Spiral of pain
:::warning
This is based in an actual exam that happened. This is the most complicated one that happened, so if you manage something like this, you can face probably whatever. But please don't start here.
:::
A 2D list A has N rows and N columns where N is a positive integer. The following function fillSquarePain() is written to fill array A with numbers 1,2, 3, ... , N^2^ and return it.
```python!
def fillSquarePain(N):
K = N*N
A = list(list(0 for x in range(N)) for x in range(N)) #This uses list comprenhesion to create a list of 0 that is n times n
for row in range(N):
for column in range(N):
A[row][column] = K
K = K-1
return A
```
a) Trace the algorithm with an input of N=3, to show the contents of the 2D list A after the algorithm has been executed [3 marks]
Solution
:::spoiler
| | | |
| -------- | -------- | -------- |
| 9 | 8 | 7 |
| 6 | 5 | 4 |
| 3 | 2 | 1 |
Another solution

_Credit D.P._
The important part is that you **show** the contents of the 2D list.
:::
b) There are many different ways of placing the numbers 1 to N^2^ into an NxN two dimensional list. The following two-dimensional list with dimensions 5x5 has been filled in a circular (spiral) pattern with numbers 1 to 5^2^.

The general process of filling a NxN two-dimensional list ina circle with numbers from 1 to N^2 could be described as follows:
* initialize `Z=1`
* initialize `top`, `bottom`, `left` and `right`
* iterate until the whole array is filled
* each time `Z` is placed correctly increase the value of `Z` by 1.
* fill the elements of the `top` row starting from `left` to `right`
* increase `top` by 1 before filling the elements of the `right` column
* fill the elements of the `right` column starting from `top` to `bottom`
* decrease `right` by 1 before filling the elements of `bottom` row and `left`column ina similar way, adjusting `top`, `right`, `bottom` and `left` accordingly
_Note from teacher: what a ride, huh? What you have seen is the description of a program_
b) i) State the initial values for `top`, `bottom`, `left` and `right` [1 mark]
Hint 1:
:::spoiler
These are the values that these variables should have at the beginning for the program to work
:::
Hint 2:
::: spoiler
Remember what is the size of the 2D list. In the exam I have a lots of 5 or 4 when that is not the correct size.
:::
Solution:
:::spoiler
```python
top = 0
bottom = N-1
left = 0
right = N-1
```
:::warning
```python
top, bottom, left, right = 0, N-1, 0, N-1
```
Even if this is python correct (in the background it's using tuples) they are not asking for python code, but for values so this would be in grace of the marker put it as good. Even if I'd mark it as good.
:::
:::
b) ii) State the consequence of not increasing `top` by 1 before starting to fill the elements of the `right` column [1 mark]
:::spoiler
We would have overwriting problems. (you can describe this in different ways)
:::
b) iii) In the algorithm described above, state de indices (subscripts) of the first and the last element to be filled in the `bottom` row [1 mark]
Hint:
::: spoiler
Remember what is the size of the 2D list.
:::
Solution:
:::spoiler
first to fill: (N-1, N-1)
last to fill: (N-1, 0)
_credit V.K._
:::
c) Construct in Python an algorithm to fill a 2D list called `mat` that has a size of NxN in a circular (spiral) pattern, with numbers from 1 to N^2^ as desribed above. [9 marks]
Hint:
::: spoiler
If you are lost because the previous description is messy, remember that it needs to do the same, but you can do it in other way. If, for example, you want to start by the end and go to the exterior that could be a way (not my advise, because it will be messy).
:::
Hint 2:
::: spoiler
I told you to travers using the regular order but you can traverse a 2D list in _other_ ways.
:::
Hint 3:
::: spoiler
Remember what is the size of the 2D list.
:::
Solution
:::spoiler
```python=
def fillMat(N):
mat = list(list(0 for x in range(N)) for x in range(N))
Z = 1
top = 0
bottom = N-1
left = 0
right = N-1
while Z <= N*N:
for column in range(left, right + 1):
mat[top][column] = Z
Z += 1
top += 1
for row in range(top, bottom + 1):
mat[row][right] = Z
Z += 1
right -= 1
if top <= bottom:
for column in range(right, left - 1, -1):
mat[bottom][column] = Z
Z += 1
bottom -= 1
if left <= right:
for row in range(bottom, top - 1, -1):
mat[row][left] = Z
Z += 1
left += 1
```
_Credit V.K._
Other comments:
You can create the empty list without using list comprehension:
```python=
mat = []
for i in range(N):
row = []
for j in range(N):
row.append(0)
mat.append(row)
```
_credit D.P._
Also you can have the ending of the list using this condition:
```python=
while top <= bottom and left <= right:
```
_credit D.P._
:::
### Exam exercise: reading data from a csv file
:::info
:link: This is an exercise that combines 2D lists, splitting, reading files and exceptions!
You can see the whole thing here and then the solution in the corresponding places!
:::
I have a CSV downloaded from the Canarian Statistics Institute (From the Canary Islands, Spain) with the names of the countries and their coordinates. The full project needs to create a map. The file has this structure (the grey text is the line number, it’s not part of the file but the number that is nonconsecutive is part of the line)

The information required is the name of the country and its coordinates (longitude and latitude).
A- Construct in Python an algorithm that reads “data.csv” and creates a 2D list called countries with the following structure: [5]

Solution here: https://hackmd.io/LG5DF-vbStKA5nUfx6-Nsg?both#Exam-exercise-the-CSV-of-countries
B- After the parsing of the data into a 2D list we need to validate it. In this case we need to check that the longitude is between –180 and 180 (both included) and latitude is between –90 to 90 (both included) and they are numbers. In case that any of this data is not valid, the program should raise an exception with an approiate message.
Construct in Python the described algorithm to validate the 2D list countries [5]
Solution here: https://hackmd.io/RCQ32fpjS-up6rYC74jPoQ#Validating-country-data