Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Lecture 01: Python primer

University of Lausanne

Course: Deep Learning for Solving and Estimating Dynamic Models in Economics and Finance
Script reference: Front matter and Appendix~E — tuples, lists, dictionaries
Notebook role: primer (pre-course self-study; skip if you write Python every day)
Author: Simon Scheidegger

Python Basics 3: Basics of Tuples and Lists

There are two ways you can have lists in Python. You can either use tuple’s or ... list’s. The difference between the two is that tuple are immutable (you cannot change them once you make them) but you can add or remove items for list’s.

Tuples

Tuples are immutable collections of objectes of any type. Right now, you only know of int’s, float’s, bool’s and NoneType. So, that’s all I will use to make tuples. But you can put anything in a tuple. Here is a tuple with two integers:

x = (1, 2)
x
(1, 2)

Here is a tuple with two floats:

y = (2.1232, 3.14)
y
(2.1232, 3.14)

Here is a tuple with two integers and a float:

z = (5, 0, 34.34)
z
(5, 0, 34.34)

All, these are tuples:

type(x)
tuple
type(y)
tuple
type(z)
tuple

You can ask a tuple how many elements it has using the function len:

len(x)
2
len(y)
2
len(z)
3

Indexing of tuples

You can access any element of a tuple using indexing. Note that indices start at 0 and go up to the len() of the tuple:

z[0]
5
z[1]
0
z[2]
34.34

If you try to try to use an index larger than the length of the tuple, here is what happens:

z[3]
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
/tmp/ipykernel_20982/2762452800.py in <module>
----> 1 z[3]

IndexError: tuple index out of range

You get an IndexError. This is a very common bug. You need to learn how to read it.

You can also use negative indices:

z[-1]
34.34

Negative indices give you the elements starting from the last one. In particular z[-1] gives you the last element of the tuple. What would you get with z[-2]:

z[-2]
0
z[-3]
5

So, z[-3] gives you the third element of the tuple counting from the last. Because z has just three elements, z[-3] is the same as z[0]. What would happen if you tried z[-4]?

z[-4]
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
/tmp/ipykernel_20982/1213793502.py in <module>
----> 1 z[-4]

IndexError: tuple index out of range

You get an IndexError again.

Okay. What if you wanted to get the first and second elements of z and not the last one. You can do this using index ranges:

z[0:2]
(5, 0)

How about the second and the third:

z[1:3]
(0, 34.34)

Let’s make a bigger tuple so that we can do some more indexing:

w = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

This has 10 elements:

len(w)
10

To get the first five you can do:

w[0:5]
(1, 2, 3, 4, 5)

Now, if the first index in your range happens to be zero, then you can skip it. So, instead of w[0:5], you can just write:

w[:5]
(1, 2, 3, 4, 5)

Let’s get the last 3:

w[7:10]
(8, 9, 10)

Note that this gives you w[7], w[8], w[9]. Just like before, if the last index in your range happens to be the length of the tuple, you can skip it. So, instead of w[7:10] you can write:

w[7:]
(8, 9, 10)

So when you ask for w[i:j] you get all elements between w[i] and w[j-1]. Let’s see what you will get when you do this:

w[4:5]
(5,)

This just gave you a tuple with just the fifth element. At this point, look at how a tuple with a single element is defined. It is (5,) instead of (5). The comma is essential. This is because without the comma (5) is just five ...

type((5))
int
type((5,))
tuple
(5)
5
(5,)
(5,)
len((5,))
1

How can I have a tuple with zero elements? Can you guess? Well, it is just ():

()
()
type(())
tuple
len(())
0

Here is another way to get a tuple with zero elements:

w[5:5]
()
len(w[5:5])
0

Alright, now some more advanced indexing. You do not have to remember all these. I am just putting them here for your reference.

First, you can have ranges with negative numbers. This is how to get everything from the fourth element counting from the end to the second element counting from the end:

w[-4:-2]
(7, 8)

And if you want to go all the way to the last element you can write:

w[-4:10]
(7, 8, 9, 10)

Or you can completely skip the last element:

w[-4:]
(7, 8, 9, 10)

Meditate a little bit with this example with negative indices:

w[-4:-1]
(7, 8, 9)

This means that w[-i:-j] gives everything between w[len(w)-i] and w[len(w)-j] for i and j positive numbers in this example.

What happens if you try to give a range that doesn’t make sense. Like, take me all the way from the 5th element to the 2nd element:

w[5:2]
()

You get an empty tuple. Nice. What happens if you mix positive and negative indices:

w[-6:8]
(5, 6, 7, 8)

It works just fine if the ranges make sense. Of course, avoid this because it is quite confusing.

Indexing is getting quite long, but let me give you one more example before we move on. Here is how you can take skip elements in between:

w[0:10:2]
(1, 3, 5, 7, 9)
w[1:5:2]
(2, 4)

So, w[i:j:k] will give you every k-th element from i (inclusive) to j (exclusive). For k=1k=1, of course, you get everything:

w[1:5:1]
(2, 3, 4, 5)

Let’s say now that we want to take every other element as we did above. We can do this:

w[0:10:2]
(1, 3, 5, 7, 9)

But because we start at zero, we can skip the first index:

w[:10:2]
(1, 3, 5, 7, 9)

And because we end at len(w) (which is 10), we can also skip the second index:

w[::2]
(1, 3, 5, 7, 9)

Similarly, if we wanted to take every third element we would do:

w[::3]
(1, 4, 7, 10)

You can also make k negative. Let’s see what happens:

w[::-1]
(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)

Wow, the elements are reveresed. How about this:

w[::-2]
(10, 8, 6, 4, 2)

Reveresed and we skip every other element. Note that w[::-2] is equivalent to:

w[10:0:-2]
(10, 8, 6, 4, 2)

So, the range has to make sense. You say, I want to go from the 10th element to the first one backwards skipping every other one.

Concatentating tuples

You can concatenate tuples. Here is how:

x + y
(1, 2, 2.1232, 3.14)
x + y + z
(1, 2, 2.1232, 3.14, 5, 0, 34.34)

Nice. Observe that x + y and y + x are different:

x + y
(1, 2, 2.1232, 3.14)
y + x
(2.1232, 3.14, 1, 2)

What if you add the () (0-element tuple) to a tuple:

x + ()
(1, 2)
() + x
(1, 2)

You just get the tuple back.

You cannot change tuples

You cannot change a tuple once you create it. For example, you cannot do this:

w[4] = 5
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
/tmp/ipykernel_20982/2101971069.py in <module>
----> 1 w[4] = 5

TypeError: 'tuple' object does not support item assignment

Read the error message. It is quite clear: “TypeError: ‘tuple’ object does not support item assignment.” Always read the error messages.

So, you should not be using tuple’s for things that may change. You should only use tuple’s for things that do not change. As a matter of fact, you should always use tuples’s for things that do not change. In this way, if you try to change that object you will get this nice error above instead of a nasty bug.

If you want something that can be changed, then you need to use a list.

Questions

  • In the code block provided below evaluate the angle between the two vectors:

    r1=4i^+3.5j^+2.5k^,\vec{r}_1 = 4\hat{i} + 3.5\hat{j} + 2.5\hat{k},

    and

    r2=1.5i^+2.5j^.\vec{r}_2 = 1.5\hat{i} + 2.5\hat{j}.

    Remember that the dot product of two vectors is:

    θ=cos1(r1r2r1r2).\theta = \cos^{-1}\left(\frac{\vec{r}_1\cdot \vec{r}_2}{|\vec{r}_1||\vec{r}_2|}\right).

    Hint: Use tuples to represent r1 and r2. You will have to import math and use math.acos().

# Your code here
  • Consider the tuple:

q = (3.0, 23.0, 1e-3, 12.242, 23, 41, 50, 30., 20.)

Do the following:

  • Get the lentgh of q:

  • Get all the elements of q from the second to the second to last

  • Get every other element of q from the second to the second to last

  • Reverse q

Lists

Lists are like tuples, containers of many things, but they can be changed. Here is a list:

a = [1, 2, 3, 4, 5, 6, 7]
a
[1, 2, 3, 4, 5, 6, 7]

Notice that you define it with [] instead of ().

Just like tuples, you can get the length of the list with len:

len(a)
7

And you can index lists in exactly the same way you index tuples:

a[0]
1
a[1]
2
a[2:5]
[3, 4, 5]
a[::2]
[1, 3, 5, 7]
a[::-2]
[7, 5, 3, 1]

You can also combine two lists into a new list, like this:

b = [3.1, 5.0, 2.]
c = a + b
c
[1, 2, 3, 4, 5, 6, 7, 3.1, 5.0, 2.0]

And here is the empty list:

[]
[]
type([])
list
len([])
0
a + []
[1, 2, 3, 4, 5, 6, 7]

So, lists behave just like tuples but with one main difference: You can change lists. For example, you can change the second element of the list to 3.4:

# before
a
[1, 2, 3, 4, 5, 6, 7]
a[1] = 3.4
# after
a
[1, 3.4, 3, 4, 5, 6, 7]

You can also put a new item at the end of the list using list.append(). Here is how:

# before
a
[1, 3.4, 3, 4, 5, 6, 7]
# Push the item at the end
a.append(6.3)
# after
a
[1, 3.4, 3, 4, 5, 6, 7, 6.3]

You can also remove the last item of the list using list.pop():

# before
a
[1, 3.4, 3, 4, 5, 6, 7, 6.3]
# remove the last item
a.pop()
6.3

Notice that the item was returned by pop(). Here is the list after the pop operation:

a
[1, 3.4, 3, 4, 5, 6, 7]

The last item was removed. Now append() and pop() are methods of the class list. They can be accessed by doing a.append() or a.pop(). We will talk about classes and methods in another hands-on activity. You can access the methods of an object by typing the name of the object followed by a . and then pressing the “tab” button of your keyboard. Try it here:

a.  # Get next to the dot and press "tab"
  File "/tmp/ipykernel_20982/527140490.py", line 1
    a.  # Get next to the dot and press "tab"
        ^
SyntaxError: invalid syntax

Another way to get help about an object is to use help() on it. You can do:

help(a)

This will give you essentially all the information about the object you need.

Now back to lists. We saw append() and pop(). There are more built in methods. insert() is a useful one:

# before
a
a.insert(4, 23.5325)
a

Here is what it does:

help(a.insert)

remove() is also useful:

# before
a
a.remove(3)
# after
a

Here is what it does:

help(a.remove)

Let’s try to remove something that is not there:

a.remove(0)

As I said before: Read the error messages!!!

The list.extend() method allows you to append an entire list to a given list:

# before
a
# extend
a.extend([0.3, 0.12, 123.])
# after
a

You can also sort a list like this:

# before
a
a.sort()
# after
a

Here is how you can count the occurence of a specific element in a list:

a.count(3.4)
a.count(0.0)

And here is how you can find the index of a list element:

idx = a.index(23.5325)
idx
a[idx]

There more things you can do with lists, but that’s it for now.

Questions

Consider the list:

data = [23.0, 21.0, 23.0, 23.0, 23.84, 24.52]
  • Find how many occurances of 23.0 there are in data:

# Your code here
  • Find the mean of the data list, i.e., the sum of all elements divided by their number. Hint: Use the built-in python function sum(). Here is the help for sum:

help(sum)

The term iterable above refers to either tuples or lists (it is more general, but we will explain it another time).

# your code here
  • Find the minimum and the maximum of data. Hint: Use the function min() for the minimum.

# your code here
# your code here