Python tuples

A sequence is a bunch of ordered objects. Ordered in the sense that you can say, “this is first,” and “this is second.” Obviously, sequences are very useful for data science. Data are made out of sequences of stuff.

There are two ways you can have sequences in Python. You can either have tuple’s or … list’s. The difference between the two is that tuple are frozen (you cannot change them once you make them) but you can add or remove items for list’s. Let’s look at tuples first.

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)
<ipython-input-13-a849b2246650> 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)
<ipython-input-17-e93807d81ae4> 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 you want something like w[7:10] you can instead 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=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)
<ipython-input-59-6b006644fcad> 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: $\( \vec{r}_1 = 4\hat{i} + 3.5\hat{j} + 2.5\hat{k}, \)\( and \)\( \vec{r}_2 = 1.5\hat{i} + 2.5\hat{j}. \)\( Remember that the dot product of two vectors is: \)\( \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