Last modified: May 18, 2025
This article is written in: 🇺🇸
In NumPy, arrays are data structures that store elements in a grid-like fashion. Understanding how to access and modify these elements is helpful for efficient data manipulation and analysis. NumPy arrays are 0-indexed, meaning the first element is accessed with index 0, the second with index 1, and so forth. Mastering indexing and slicing techniques allows you to retrieve, update, and manipulate specific parts of an array with ease.
Arrays in NumPy are best formalized as discrete tensor-valued functions
First let's introduce the shorthand
$$ [n_k]={0,1,\dots,n_k-1} $$
Then you can simply say
$$ A\colon\prod_{k=1}^d[n_k]\longrightarrow\mathbb F, \qquad (i_1,\dots,i_d) A_{i_1\cdots i_d} $$
Equivalently, one often abbreviates
$$ A = \bigl(A_{i_1\cdots i_d}\bigr)_{0\le i_k < n_k} \in \mathbb F^{n_1\times\cdots\times n_d} $$
where $\mathbb F=\mathbb R$ or $\mathbb C$. Indexing a single element is the evaluation $A(i_1,\dots ,i_d)=A_{i_1,\dots ,i_d}$; a slice such as
$$ A_{[r_1:r_2,c_1:c_2]} = {A_{i,j}\mid r_1 \le i < r_2,c_1 \le j < c_2} $$
is the restriction of $A$ to the Cartesian sub-domain $[r_1,r_2)\times[c_1,c_2)$, while assignment $A_{i_1,\dots ,i_d}\leftarrow \alpha$ re-defines the function at the chosen point(s). Because the domain is a regular grid that NumPy stores in row-major (C-order) or column-major (Fortran-order) contiguous memory, these operations reduce to constant-time pointer arithmetic, giving mathematically natural vector– and matrix-manipulations the computational efficiency of low-level loops without writing any Python loops at all.
# 1-D array (vector) layout and indexing
Index → 0 1 2 3
Value [ a0 ][ a1 ][ a2 ][ a3 ]
↑
A[1] = a1
# 3 × 4 matrix A and a 2 × 2 slice A[0:2,1:3]
columns →
0 1 2 3
row 0 [ a00 | a01 | a02 | a03 ]
row 1 [ a10 | a11 | a12 | a13 ]
row 2 [ a20 | a21 | a22 | a23 ]
Slice A[0:2,1:3] (rows 0–1, cols 1–2)
1 2
row 0 [ a01 | a02 ]
row 1 [ a11 | a12 ]
One-dimensional (1-D) arrays are simple lists of elements where each element can be accessed using its unique index. Accessing elements in a 1-D array is straightforward and forms the basis for more complex operations in multi-dimensional arrays.
import numpy as np
# Creating a 1D array
arr = np.array([1, 2, 3, 4])
# Accessing the second element (index 1)
print(arr[1])
Expected output:
2
arr[1]
accesses the element at index 1 of the array arr
, which is the second element, 2
.arr[0]
would return 1
.Two-dimensional (2-D) arrays, or matrices, consist of rows and columns, allowing for more complex data structures. Accessing elements in a 2-D array requires specifying both the row and column indices.
Let's consider the following matrix:
$$ \begin{bmatrix} 7 & 1 & 2 & 6 \\ 6 & 4 & 9 & 3 \\ 2 & 1 & 4 & 5 \\ 2 & 7 & 3 & 8 \\ \end{bmatrix} $$
To retrieve the value 9
from the matrix, which is located at the second row and third column:
# Creating a 2D array (matrix)
arr = np.array([
[7, 1, 2, 6],
[6, 4, 9, 3],
[2, 1, 4, 5],
[2, 7, 3, 8]
])
# Accessing the element at row index 1 and column index 2
print(arr[1, 2])
Expected output:
9
arr[1, 2]
accesses the element at the second row (index 1
) and third column (index 2
), which is 9
.One of the powerful features of NumPy arrays is their mutability, allowing you to change elements after the array has been created. Modifying array elements is as simple as assigning a new value to a specific index.
# Creating a 1D array
arr = np.array([1, 2, 3, 4])
# Modifying the third element (index 2)
arr[2] = 5
print(arr)
Expected output:
[1 2 5 4]
arr[2] = 5
assigns the value 5
to the element at index 2
, changing the third element from 3
to 5
.arr
is updated in place, reflecting the change immediately.Slicing is a technique used to extract portions of an array, resulting in a subarray that shares data with the original array. This method is efficient and allows for selective data manipulation without copying the entire array.
For one-dimensional arrays, slicing uses the start:stop:step
notation. Each parameter is optional and can be omitted to use default values:
0
if omitted.1
if omitted.
# Creating a 1D array
arr = np.array([1, 2, 3, 4])
# Slicing the array with different parameters
print(arr[::2]) # Every second element
print(arr[1:]) # From the second element to the end
print(arr[:-3]) # From the start to the third-last element
Expected output:
[1 3]
[2 3 4]
[1]
arr[::2]
retrieves every second element, resulting in [1, 3]
.arr[1:]
retrieves elements from the second element to the end, resulting in [2, 3, 4]
.arr[:-3]
retrieves elements from the start up to but not including the third-last element, resulting in [1]
.In two-dimensional arrays, slicing can be applied to both rows and columns simultaneously. The syntax arr[start_row:end_row, start_col:end_col]
allows for precise extraction of submatrices.
# Creating a 2D array (matrix)
arr = np.array([
[7, 1, 2, 6],
[6, 4, 9, 3],
[2, 1, 4, 5],
[2, 7, 3, 8]
])
# Slicing the array to get the first two rows and the second and third columns
print(arr[0:2, 1:3])
Expected output:
[[1 2]
[4 9]]
arr[0:2, 1:3]
slices the array to include rows with indices 0
and 1
(the first two rows) and columns with indices 1
and 2
(the second and third columns).
[[1 2]
[4 9]]
Exploring additional slicing scenarios can enhance your ability to manipulate arrays effectively.
# Slicing the array to get the first three rows and columns from the third onwards
print(arr[:3, 2:])
Expected output:
[[2 6]
[9 3]
[4 5]]
arr[:3, 2:]
slices the array to include rows with indices 0
, 1
, and 2
(the first three rows) and columns starting from index 2
to the end (the third and fourth columns).
[[2 6]
[9 3]
[4 5]]
Understanding how to access and modify array elements opens up a wide range of practical applications in data science, machine learning, engineering, and more. Here are some common scenarios where these techniques are essential.
Beyond single-element access, you can manipulate multiple elements simultaneously using slicing or advanced indexing techniques. This capability allows for efficient data updates and transformations.
# Creating a 1D array
arr = np.array([1, 2, 3, 4, 5, 6, 7, 8])
# Modifying multiple elements
arr[2:5] = [10, 11, 12]
print(arr)
Expected output:
[ 1 2 10 11 12 6 7 8]
arr[2:5] = [10, 11, 12]
assigns the values 10
, 11
, and 12
to the elements at indices 2
, 3
, and 4
, respectively.[1, 2, 3, 4, 5, 6, 7, 8]
is updated to [1, 2, 10, 11, 12, 6, 7, 8]
.Boolean indexing allows for selecting elements based on conditional statements, enabling dynamic and flexible data selection without explicit loops.
# Creating a 1D array
arr = np.array([1, 2, 3, 4, 5, 6, 7, 8])
# Boolean indexing
bool_idx = arr > 5
print(arr[bool_idx])
Expected output:
[6 7 8]
arr > 5
creates a boolean array [False, False, False, False, False, True, True, True]
.arr[bool_idx]
uses this boolean array to filter and retrieve elements where the condition arr > 5
is True
, resulting in [6, 7, 8]
.
Operation | Description | Example Code | Expected Output |
Access 1D | Access an element by index. | arr = np.array([1, 2, 3, 4]) arr[1] |
2 |
Access 2D | Access an element by row and column index. | arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) arr[1, 2] |
6 |
Modify Element | Change the value of an element. | arr = np.array([1, 2, 3, 4]) arr[2] = 5 |
[1, 2, 5, 4] |
Slice 1D | Slice a 1D array. | arr = np.array([1, 2, 3, 4]) arr[::2] , arr[1:] , arr[:-3] |
[1, 3] , [2, 3, 4] , [1] |
Slice 2D | Slice a 2D array. | arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) arr[0:2, 1:3] , arr[:3, 2:] |
[[2, 3], [5, 6]] , [[3], [6], [9]] |
Modify Multiple | Modify multiple elements. | arr = np.array([1, 2, 3, 4, 5, 6, 7, 8]) arr[2:5] = [10, 11, 12] |
[1, 2, 10, 11, 12, 6, 7, 8] |
Boolean Indexing | Access elements based on conditions. | arr = np.array([1, 2, 3, 6, 7, 8]) arr[arr > 5] |
[6, 7, 8] |