From Novice to Ninja: NumPy Arrays in Python — Day 04 (Indexing and Slicing)

Data Science Delight
6 min readJul 8, 2023

--

Welcome back to another exciting NumPy series. In our previous article, we explored the world of NumPy, where we discussed some of the common attributes and methods used in NumPy. If you missed those articles, you can find them here:

In this article, we will expand our knowledge and dive deeper into the indexing & slicing that NumPy offers.

But before we dive into the exciting content, it would be really great if you could support us in this exciting journey. Liking, sharing, and following our Medium profile would be greatly appreciated as it motivates us to create more valuable content for you.

You can follow our Medium profile for more in-depth articles and updates.

Let’s stay connected & continue this amazing data science adventure together. Without further ado, let’s dive into the exploration of NumPy arrays.

NumPy arrays have powerful indexing & slicing capabilities that make it easy to work with multi-dimensional data.

Indexing & slicing are two common operations used to access & modify elements in NumPy arrays. Let’s directly jump to the code to clearly understand this:

Indexing:

Indexing refers to accessing a specific element of an array by specifying its position in that array. We use a square bracket notation with the index value of the element we want to access.

Accessing elements based on index position:

The indexing starts from ‘0’ for the first element as shown below:

# Creating an array of 10 elements:
arr = np.arange(10, 100, 10)

# Accessing first element:
arr[0]

Always remember that the indexing starts from 0 for the first element & goes further.

Suppose I want to get the value present at index 5, the code will be:

# Creating an array of 20 elements:
arr = np.arange(10, 100, 10)

# Accessing 6th element:
arr[5]

2-D Array:

arr_2d = np.random.randn(3, 2)
arr_2d

Output:

array([[-0.23992057, -0.95706701],
[-0.46159549, 0.33581123],
[-0.59853907, 0.70141613]])

If I want to get ‘-0.95706701’, then:

arr_2d[0][1]

Output:

-0.95706701

We took “arr_2d[0][1]”, where [0] = Row Number and [1] = Column Number.

As we know already that indexing starts from 0 & the number that we wanted was present in the first row and second column. So the first row is considered as 0 & second column is considered as 1.

Let’s take another example:

arr_2d = np.random.rand(3, 4)
arr_2d

Output:

array([[0.97385673, 0.7425182 , 0.4161874 , 0.39891707],
[0.50696289, 0.5470648 , 0.76083634, 0.47948281],
[0.40306711, 0.59404498, 0.5589046 , 0.82854785]])

Suppose, I want to grab ‘0.76083634’, then:

arr_2d[1][2]

Output:

0.7608363357297596

We can see that “0.76083634" is present at 2nd Row, 3rd Column so we wrote arr_2d[1][2] where ‘1’ indicates 2nd Row & 2 indicates 2nd column.

Modifying elements:

Suppose I have an array containing 10 elements that is from 1 to 10 and I want to modify the element 4. Instead of 4, I want 45, then:

# Creating an array of 10 elements:
arr = np.arange(1, 11)

# Accessing first element:
arr[3] = 45
arr

2-D Array:

arr_2d = np.random.randn(3, 5)
arr_2d

Output:

array([[-0.06531467, -0.80207436, -0.36538212,  0.12720764,  0.84508825],
[ 1.04259111, 0.06874642, 1.36946725, -0.75599644, -0.93775394],
[-1.19286774, 1.99151635, 0.84523393, 1.09023772, 0.17308087]])

I want to modify ‘1.99151635’ to 35.25:

arr_2d[2][1] = 35.25
arr_2d

Output:

array([[-0.06531467, -0.80207436, -0.36538212,  0.12720764,  0.84508825],
[ 1.04259111, 0.06874642, 1.36946725, -0.75599644, -0.93775394],
[-1.19286774, 35.25 , 0.84523393, 1.09023772, 0.17308087]])

Accessing the last element:

To access the last element, we can do negative indexing:

arr = np.arange(0, 11)
arr

Output:

array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10])

Last Element:

arr[-1]

Output:

10

The indexing (-1) indicates the last element and (-2) indicates the second-to-last element & so on.

Slicing:

Slicing refers to accessing a subset of elements from a NumPy array. Slicing is done by colon (:) notation, where we specify the start & end indices of the subset we want to extract. It selects all the elements starting from the start index, up to but, not including the end index.

The syntax for slicing is: [start: stop: step]

Accessing a range of elements:

import numpy as np

arr = np.array([10, 20, 30, 40, 50])

# Accessing a range of elements using slicing
print(arr[1:4])

Output:

[20, 30, 40]

Modifying the range:

import numpy as np

arr = np.array([10, 20, 30, 40, 50])

# Modifying a range of elements
arr[2:5] = [35, 45, 55]
print(arr)

Output:

[10, 20, 35, 45, 55]

Step size:

import numpy as np

arr = np.array([10, 20, 30, 40, 50, 60, 70, 80])

# Slicing with a step size of 2
print(arr[1:7:2])

Output:

[20, 40, 60]

Here, arr[1:7:2] indicates grab elements from index 1 to 7 which means it will grab all elements from index 1 which is 20 till index 7, but we already know that it excludes the last element so, it will exclude index 7 which is 80.

So it will traverse from 20 to 70 but with a step size of 2, that is it will grab the first element and grab every second element thereafter.

Negative Slicing:

import numpy as np

arr = np.array([10, 20, 30, 40, 50])

# Negative Slicing:
arr[-4 : -1]

Output:

array([20, 30, 40])

We know that the last element is considered as ‘-1’. So, ‘-4’ means 20. So, it will take elements from 20 all go all the way till the last element but excluding the last element.

If I want to reverse it, then :

arr[-4 : -1][::-1]

Output:

array([40, 30, 20])

Reverse Slicing:

To access elements in reverse we use a double colon(::) with a step size as ‘-1’:

arr = np.arange(0, 11)
arr[::-1]

Output:

array([10,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0])

Accessing a subset of an array using Slicing:

arr_2d = np.random.randn(4, 5)
arr_2d

Output:

array([[ 0.5020678 , -0.28276553, -0.16932261,  0.57546931, -2.41406507],
[ 0.497913 , -0.67755904, -0.06349287, 1.30148711, 1.25004996],
[-1.65378865, -2.07421301, -0.50811003, -0.02546988, -1.16072908],
[-0.96812867, 0.27469524, 0.85402241, 0.1792494 , -0.07490543]])

Suppose I want to grab elements:

[[-0.67755904, -0.06349287,  1.30148711],
[-2.07421301, -0.50811003, -0.02546988]]

Then our code will be:

arr_2d[1:3, 1:4]
  • [1:3] indicates row_indices and [1:4] indicates column_indices.
  • [1:3]= it means select elements from 2nd row (i.e; index = 1) & go upto last row (i.e; index = 3). Since it will exclude elements at the last index that is the reason we have mentioned from ‘1’ to ‘4’.
  • Remember, indexing in Python starts at 0 so the first row or column is considered as ‘0’ & Not ‘1’.
  • [1:4] = it means select elements from the 2nd column (i.e; index = 1) till the 4th column so [4] cause it will exclude the last column.

Let’s take another example:

arr_2d = np.random.randn(3, 5)
arr_2d

Output:

array([[-1.62716394, -2.64916411,  0.74365278, -1.06975271,  0.05895247],
[-0.27066018, -1.20494539, -0.2301947 , 0.71590738, 0.51119436],
[ 1.29978942, -1.1506307 , -1.24879267, 0.79348962, 0.27760007]])

I want to grab all the elements from 2nd column:

arr_2d[:, 1:]

Output:

array([[-2.64916411,  0.74365278, -1.06975271,  0.05895247],
[-1.20494539, -0.2301947 , 0.71590738, 0.51119436],
[-1.1506307 , -1.24879267, 0.79348962, 0.27760007]])

NOTE:

To grab all elements, we use a single [:].

Here, [:] indicates all rows, and [1:] indicates from the 2nd column onwards. That is grab all elements from the 2nd column till the end including the last row & column.

Exercise Questions:

  1. Create a 2d array containing elements from 1 to 25. Access elements:
[[13, 14,
18, 19]]

2. Create a (6 * 4) matrix containing random values from uniform distribution and access the elements present at the 3rd & 4th row of the last column.

3. Create a random (5 * 5) matrix from normal distribution & access all the even rows of that array.

4. Create a random (5 * 5) matrix from normal distribution & access all the even rows and odd columns of that array.

That’s it for Day — 04:

That’s it for today’s session. In the next session, we will be discussing about mathematical operations used in NumPy arrays in detail along with the code.

Please Subscribe/Follow, Like, Share, and Clap as it would encourage me to write more such blog posts, especially on data analytics and the data science field.

Stay Tuned!!

Thank you!

--

--

Data Science Delight

Content Creator | Sharing insights & tips on data science | Instagram: @datasciencedelight | YouTube: https://www.youtube.com/channel/UCpz2054mp5xfcBKUIctnhlw