Hello everybody,
Michael here, and today’s lesson will be on indexing, slicing, and iterating through NumPy arrays-this is part 3 in my NumPy series.
First off, let’s discuss how to access elements in NumPy arrays. Here’s a simple example of this:
arrA = np.array([7, 14, 21, 28, 35, 42])
print(arrA[2])
21
As you can see, accessing elements in NumPy arrays is similar to accessing elements in regular Python arrays.
But that was just a simple 1-D array. How would you access elements from a 2-D array?
arrB = np.array([[9, 18, 27, 36, 45], [11, 22, 33, 44, 55]])
print(arrB[0, 2])
27
Turns out, accessing elements from a 2-D array is simple as well, except you’d use two parameters rather than just one. The first parameter specifies which dimension you’d like to search in and the second parameter specifies the element in that dimension you’d like to retrieve. In this example, I’m retrieving the third element from the first dimension (recall that Python arrays have an indexing system that starts with 0).
Simple enough right? Now let’s see how we can access elements from a 3-D array:
arrC = np.array([[[12, 24, 36, 48, 60], [13, 26, 39, 52, 65], [14, 28, 42, 56, 70]], [[20, 40, 60, 80, 100], [21, 42, 63, 84, 105], [22, 44, 66, 88, 110]]])
print(arrC[1, 2, 1])
44
This one is a bit more complex since there is more to break down, but there’s an easy way to explain 3-D array indexing. First of all, since there are three dimensions, there are obviously three parameters you’d need to use.
Aside from that, the first parameter is 1, which tells Python to look for an element in the second dimension. The second parameter is 2, which narrows the search down to the third array within the second dimension. The third parameter is 1, which further narrows down the search to the second element within the third array within the second dimension-the element that is returned from this search is 44.
Now let’s demonstrate how to perform negative (or reverse) indexing on a 1-D array:
arrD = np.array([0.2, 0.4, 0.6, 0.8, 1, 1.2, 1.4])
print(arrD[-2])
1.2
Reverse indexing on a NumPy array is the same as reverse indexing on a regular Python array. In this example, I am retrieving the element at index -2 (1.2), which refers to the second index from the right of the array.
Now here’s reverse indexing at work on a multi-dimensional array:
arrE = np.array([[-12, -9, -6, -3, 0], [-28, -21, -14, -7, 0], [-32, -24, -16, -8, -0]])
print(arrE[1, -1])
0
Reverse indexing on a multi-dimensional array is also fairly simple. In this example, I am retrieving the last element (or rightmost element) from the second array-within-an-array.
One more neat thing about NumPy array indexing is that it allows you to perform basic arithmetic on certain elements of the array. Here’s an example of that:
arrF = np.array([2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048])
print(arrF[2]+arrF[-2])
print(arrF[3]-arrF[0])
print(arrF[-1]*arrF[1])
print(arrF[2]/arrF[0])
print(arrF[2]**2)
1032
14
8192
4.0
64
In this example, I am performing the four basic arithmetic operations (addition, multiplication, subtraction, and division) and exponentiation (raising a number to a certain power) on certain elements in the array; I’m using different elements for each operation.
Next up, let’s discuss slicing an array. In the context of programming, array slicing is when you take certain element(s)/portion(s) from an array to create a new, smaller array. Here’s a simple example of NumPy array slicing:
arrG = np.array([1982, 1986, 1990, 1994, 1998, 2002, 2006, 2010, 2014, 2018, 2022])
print(arrG[0:5])
[1982 1986 1990 1994 1998]
In this example, Python is printing out the first five elements of arrG. Recall that when retrieving a range of elements from an array, the element corresponding to the end index is not included-so in this case, the 6th element wasn’t included.
Now, just as with regular Python arrays, you can slice arrays without a start index or end index-you just need one of these indexes to slice an array. Let me show you what I’m talking about:
print(arrG[5:])
[2002 2006 2010 2014 2018 2022]
In this example, I used [5:] to retrieve the sixth element in the array onward.
print(arrG[:5])
[1982 1986 1990 1994 1998]
In this example, I used [:5] to retrieve all elements in the array up to BUT NOT INCLUDING the sixth element.
The next topic I want to discuss is negative (or reverse) slicing; just as you can perform reverse indexing on an array, you can also perform reverse slicing on an array as well.
Let me show you an example of reverse slicing:
print(arrG[-4:-1])
[2010 2014 2018]
Reverse slicing has the same idea as reverse indexing-indexing starts at -1, which corresponds to the rightmost element in the array (2022 in the case of arrG).
If I wanted to include 2022 in my reverse slicing, I would’ve used this code:
print(arrG[-4:])
[2010 2014 2018 2022]
Had I used the line print(arrG[-4:0]), I would’ve recieved an empty array as output.
Now let’s explore a new array concept that I haven’t discussed here before-the concept of step. The step concept allows you to set a step for the slicing (e.g. return every other element, etc.).
Let’s see a simple example of slicing an array by step:
print(arrG[::3])
[1982 1994 2006 2018]
In this example, I retrieved every third element from arrG starting with the first element-1982. In other words, I retrieved the 1st, 4th, 7th, and 10th elements from arrG.
Now what if I wanted to perform step slicing only on a certain portion of the array:
print(arrG[0:6:2])
[1982 1990 1998]
In this example, I am performing step slicing on the 1st-7th elements of arrG (which correspond to indexes 0-6) by retrieving every other element from this portion of the array. If you want to perform step slicing on a portion of the array (rather than the whole array), you need to specify the portion of the array you would like to slice in the first two parameters-in this example, since I wanted to perform my slicing on elements 1-7, I used 0 and 6 as the first two parameters in the above example.
Now, how would we slice multi-dimensional arrays? Here’s an example (using a 2-D array):
arrH = np.array([[22, 44, 66, 88, 100], [33, 66, 99, 132, 165]])
print(arrH[0, 1:4])
[44 66 88]
In this example, the slicing function (referring to the line arrH[0, 1:4]) has two parameters rather than one-the first parameter contains the dimension where you wish to perform the slicing and the second parameter contains the range of elements you would like to retrieve (up to but not including the end index).
In the slicing function, I retrieved the 2nd through 4th elements (indexes 1-4) in the first dimension of the array (index 0). Step slicing also works for multi-dimensional arrays; for instance, the line print(arrH[0, ::2]) would have worked here (and it would have returned the 1st, 3rd, and 5th elements from the 1st dimension).
Now, how would we go about slicing a 3-D array? Take a look at the example below:
arrI = np.array([[[0, -9, -18, -27, -36], [0, -8, -16, -24, -32], [0, -7, -14, -21, -28]]])
print(arrI[0, 1, 0:3])
[ 0 -8 -16]
In order to slice a 3-D array, you’d need 3 parameters for the slicing function-the first parameter represents the 2-D array within the 3-D array where you wish to perform the slicing, the second parameter represents narrows the slicing focus to a 1-D array within the 2-D array you selected, and the third parameter represents the range of elements in the 1-D array that you want to retrieve.
In this example, the first parameter is 0, which tells Python to start the array slicing in the first (and only) 2-D array within the 3-D array. The next parameter is 1, which tells Python to narrow the array slicing focus to the second 1-D array within the 2-D array. The last parameter is 0:3, which tells Python to retrieve the 1st-3rd elements from the second 1-D array within the 2-D array.
Last but not least, let’s explore how to iterate through NumPy arrays (which involves looping through all array elements).
To iterate through NumPy arrays, a for loop while do the trick. Here’s an example of iterating through a 1-D array:
arrJ = np.arange(10, 75, 5, int)
print(arrJ)
[10 15 20 25 30 35 40 45 50 55 60 65 70]
for x in arrJ:
print(x)
10
15
20
25
30
35
40
45
50
55
60
65
70
To iterate through a simple 1-D array, all you need is the lines for x in [array name]: print(x). Not hard at all.
Let me discuss the arange function I used. The arange function is simply another way to create a NumPy array; this function has 4 parameters-the first element in the array, the last element in the array, the increment/decrement that is used to generate each element of the array, and the number type of the elements in the array (you usually use int or float).
Now let’s demonstrate iterating through a 2-D array:
arrK = np.array([[1, 1, 2, 3, 5, 8], [13, 21, 34, 55, 89, 144]])
for y in arrK:
print(y)
[1, 1, 2, 3, 5, 8]
[13, 21, 34, 55, 89, 144]
Looks good right? The for loop syntax I used might work if you want to iterate through each 1-D array but this doesn’t work if you want to iterate through each element (known in programming lingo as a scalar) in each array-within-an-array.
Here’s how to iterate through each 1-D array in the 2-D array:
for x in arrK:
for y in x:
print(y)
1
1
2
3
5
8
13
21
34
55
89
144
A simple way to iterate through multi-dimensional arrays is the use of nested for loops. In this example, the outer for loop iterates through the main 2-D array while the inner for loop iterates through each element in each 1-D array within the 2-D array.
This is a great way to iterate through multi-dimensional arrays, but here’s an even better way to iterate through multi-dimensional arrays:
for x in np.nditer(arrK):
print(x)
1
1
2
3
5
8
13
21
34
55
89
144
Simply using NumPy’s nditer function (and passing your array as a parameter) will iterate through each element in your array with just a simple line of code. The nditer function is super helpful because it allows you to iterate through a multi-dimensional array with a single line of code; recall that NumPy arrays can go up to 32-D, which would make for a very cumbersome-to-write nested for loop.
Now check out the two methods when used for iterating through a 3-D array. Which do you think is more efficient?
arrL = np.array([[[2, 4, 8, 16, 32], [3, 9, 27, 81, 243], [4, 16, 64, 256, 1024]]])
for x in arrL:
for y in x:
for z in y:
print(z)
2
4
8
16
32
3
9
27
81
243
4
16
64
256
1024
for x in np.nditer(arrL):
print(x)
2
4
8
16
32
3
9
27
81
243
4
16
64
256
1024
As you can see, both methods work great for iterating through arrL. However, the nditer method is more efficient and accomplishes the iterating with one line of code (the nested for loops method uses three lines of code).
Finally, I wanted to discuss step iterating through an array. Just as you can perform step slicing on an array, you can also step iterate through an array too:
for x in np.nditer(arrL[:, ::3]):
print(x)
2
4
8
16
32
In this example, I am iterating through arrL but only returning every third element.
- For step slicing and step iteration, remember that the step starts with the first element in the array unless you specify otherwise!
Thanks for reading,
Michael