Martin McBride, 2018-02-04

Tags index, slice, 2d arrays

Categories numpy

In section Python libraries

In this section we will look at indexing and slicing. These work in a similar way to indexing and slicing with standard Python lists, with a few differences.

Here is a video covering this topic:

Indexing is used to obtain individual elements from an array, but it can also be used to obtain entire rows, columns or planes from multi-dimensional arrays.

We can create 1 dimensional numpy array from a list like this:

import numpy as np a1 = np.array([1, 2, 3, 4]) print(a1) # [1, 2, 3, 4]

We can index into this array to get an individual element, exactly the same as a normal list or tuple:

print(a1[0]) # 1 print(a1[2]) # 3

We can create a 2 dimensional numpy array from a python list of lists, like this:

import numpy as np a2 = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])

Here is a diagram of the array:

We can index an element of the array using two indices - **i** selects the row, and **j** selects the column:

print(a2[2, 1]) # 8

Notice the syntax - the **i** and **j** values are both inside the square brackets, separated by a comma (the index is
actually a tuple **(2, 1)**, but tuple packing is used). The example picks row 2, column 1, which has the value 8. This compares with the syntax you might use with a 2D list (ie a list of lists):

v = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] print(v[2][1]) # 8

If we can supply a single index, it will pick a row (**i** value) and return that as a rank 1 array:

print(a2[2]) # [7, 8, 9]

That is quite similar to the what would happen with a 2D list. However, numpy allows us to select a single columm as well:

print(a2[:, 1]) # [2, 5, 8]

We are skipping ahead slightly to slicing, later in this tutorial, but what this syntax means is:

- for the
**i**value, take all values (**:**is a full slice, from start to end) - for the
**j**value take 1

Giving this array [2, 5, 8]:

The array you get back when you index or slice a numpy array is a

viewof the original array. It is the same data, just accessed in a different order. If you change the view, you will change the corresponding elements in the original array.

We can create a 3 dimensional numpy array from a python list of lists of lists, like this:

import numpy as np a3 = np.array([[[10, 11, 12], [13, 14, 15], [16, 17, 18]], [[20, 21, 22], [23, 24, 25], [26, 27, 28]], [[30, 31, 32], [33, 34, 35], [36, 37, 38]]])

Here is a diagram of the array:

A 3D array is like a stack of matrices:

- The first index,
**i**, selects the matrix - The second index,
**j**, selects the row - The third index,
**k**, selects the column

Here is the same diagram, spread out a bit so we can see the values:

Here is how to index a particular value in a 3D array:

print(a3[2, 0, 1]) # 31

This selects matrix index 2 (the final matrix), row 0, column 1, giving a value 31.

You can access any row or column in a 3D array. There are 3 cases.

**Case 1** - specifying the first two indices. In this case, you are choosing the **i** value (the matrix), and the
**j** value (the row). This will select a specific row. In this example we are selecting row 2 from matrix 1:

print(a3[1, 2]) # [26 27 28]

**Case 2** - specifying the **i** value (the matrix), and the **k** value (the column), using a full slice (**:**)
for the **j** value (the row). This will select a specific column. In this example we are selecting column 1 from
matrix 0:

print(a3[0, :, 1]) # [11 14 17]

**Case 3** - specifying the **j** value (the row), and the **k** value (the column), using a full slice (**:**)
for the **i** value (the matrix). This will create a row by taking the same element from each matrix. In this case we
are taking row 1, column 2 from each matrix:

print(a3[:, 1, 2]) # [15, 25, 35]

If we only specify the **i** index, numpy will return the corresponding matrix. We will call this **case 1**. In this
example we will request matrix 2:

print(a3[2]) # [[30 31 32] # [33 34 35] # [36 37 38]]

**Case 2** if we specify just the **j** value (using a full slice for the **i** values), we will obtain a matrix made
from the selected row taken from each plane. In this example we will take row 1:

print(a3[:, 1]) # [[13 14 15] # [23 24 25] # [33 34 35]]

**Case 3** if we specify just the **k** value (using full slices for the **i** and **j** values), we will obtain a
matrix made from the selected column taken from each plane. In this example we will take column 0:

print(a3[:, :, 0]) # [[10 13 16] # [20 23 26] # [30 33 36]]

You can slice a numpy array is a similar way to slicing a list - except you can do it in more than one dimension.

As with indexing, the array you get back when you index or slice a numpy array is a

viewof the original array. It is the same data, just accessed in a different order. This is different to lists, where a slice returns a completely new list.

Just a quick recap on how slicing works with normal Python lists. Suppose we have a list:

a = [10, 11, 12, 13, 14]

We can use slicing to take a sub-list, like this:

b = a[1:4] # [11, 12, 13]

The slice notation specifies a start and end value **[start:end]** and copies the list from start up to but not including end.

We can omit the start, in which case the slice start at the beginning of the list. We can omit the end, so the slice continues to the end of the list. If we omit both the slice created is a copy of the entire list:

c = a[:3] # [10, 11, 12] d = a[2:] # [12, 13, 14] e = a[:] # [10, 11, 12, 13, 14]

One final thing to note is the difference between an index and a slice of length 1:

f = a[2] # 12 g = a[2:3] # [12]

The index returns an element of the array, the slice returns a list of one element.

Slicing a 1D numpy array is almost exactly the same as slicing a list:

import numpy as np a1 = np.array([1, 2, 3, 4, 5]) b = a1[1:4] print(b) # [2, 3, 4]

The only thing to remember if that (unlike a list) **a1** and **b** are both looking at the same underlying data
(**b** is a *view* of the data). So if you change an element in **b**, **a1** will be affected (and vice versa):

b[1] = 10 print(b) # [2, 10, 4] print(a1) # [1, 2, 10, 4, 5]

You can slice a 2D array in both axes to obtain a rectangular subset of the original array. For example:

import numpy as np a2 = np.array([[10, 11, 12, 13, 14], [15, 16, 17, 18, 19], [20, 21, 22, 23, 24], [25, 26, 27, 28, 29]]) print(a2[1:,2:4]) # [[17 18] # [22 23] # [27 28]]

This selects rows **1:** (1 to the end of bottom of the array) and columns **2:4** (columns 2 and 3), as shown here:

You can slice a 3D array in all 3 axes to obtain a cuboid subset of the original array:

import numpy as np a3 = np.array([[[10, 11, 12], [13, 14, 15], [16, 17, 18]], [[20, 21, 22], [23, 24, 25], [26, 27, 28]], [[30, 31, 32], [33, 34, 35], [36, 37, 38]]]) print(a3[:2,1:,:2]) # [[ [13 14] [16 17] ] # [ [23 24] [26 27] ]]

This selects:

- planes
**:2**(the first 2 planes) - rows
**1:**(the last 2 rows) - columns
**:2**(the first 2 columns)

As shown here:

You can, of course, use full slices **:** to select all planes, columns or rows. However, for trailing indices, simply
omitting the index counts as a full slice. So for 2D arrays:

a2[1:3,:] # is the same as a2[1:3]

For 3D arrays:

a3[1:,:2,:] # is the same as a3[1:,:2] a3[1:,:,:] # is the same as a3[1:,:] # and is also the same as a3[1:]

As we saw earlier, you can use an index to select a particular plane column or row. Here we select row 1, columns **2:4**:

import numpy as np a2 = np.array([[10, 11, 12, 13, 14], [15, 16, 17, 18, 19], [20, 21, 22, 23, 24], [25, 26, 27, 28, 29]]) print(a2[1,2:4]) # [17 18]

You can also use a slice of length 1 to do something similar (slice **1:2** instead of index 1):

print(a2[1:2,2:4]) # [[17 18]]

Notice the subtle difference. The first creates a 1D array, the second creates a 2D array with only one row.

Visit the PythonInformer Discussion Forum for numeric Python.

*If you found this article useful, you might be interested in the book NumPy Recipes, or other books, by the same author.*

Copyright (c) Axlesoft Ltd 2020