NumPy Indexing and Slicing Arrays¶
NumPy arrays (ndarray
) are grid-like structures that can store data of the same type. They are similar to Python lists but offer faster computation and more efficient storage. Indexing and slicing are crucial for accessing and manipulating data within these arrays.
Importing NumPy¶
Before we begin, we need to import the NumPy library. It's a common practice to import NumPy using the alias np
.
import numpy as np
This line imports the NumPy library and allows you to access its functions using the np
prefix.
1D Array Indexing and Slicing¶
One-dimensional (1D) arrays are the simplest form of NumPy arrays, similar to vectors. Indexing and slicing in 1D arrays work much like in standard Python lists.
Indexing Individual Elements¶
To access individual elements in a 1D array, you use square brackets []
with the index of the element. Remember that indexing starts at 0.
# Creating a 1D NumPy array
array_1d = np.array([10, 20, 30, 40, 50])
# Accessing the second element (index 1)
second_element = array_1d[1]
# Accessing the last element using negative indexing
last_element = array_1d[-1]
In the code above:
array_1d[1]
accesses the second element, which is20
.array_1d[-1]
accesses the last element, which is50
.
Slicing Arrays¶
Slicing allows you to access a range of elements in an array. The syntax for slicing is array[start:stop:step]
.
# Slicing elements from index 1 to 3 (exclusive)
slice_1 = array_1d[1:3]
print(slice_1)
# Slicing elements from the beginning to index 3
slice_2 = array_1d[:3]
# Slicing elements from index 2 to the end
slice_3 = array_1d[2:]
# Slicing every other element in the array
slice_4 = array_1d[::2]
print(slice_4)
[20 30] [10 30 50]
In these examples:
slice_1
contains elements at indices 1 and 2 (20
and30
).slice_2
contains elements at indices 0 to 2.slice_3
contains elements from index 2 to the end.slice_4
contains every second element (10
,30
,50
).
# Creating a 2D NumPy array
array_2d = np.array([
[1, 2, 3], # Row 0
[4, 5, 6], # Row 1
[7, 8, 9] # Row 2
])
# Accessing the element at row 1, column 2
element = array_2d[1][2]
print(element)
# Alternatively, using comma-separated indices
element_alt = array_2d[1, 2]
6
Here:
array_2d[1][2]
andarray_2d[1, 2]
both access the element6
at row 1, column 2.
Slicing Rows and Columns¶
You can slice subsets of a 2D array by specifying ranges for rows and columns.
# Selecting the first two rows
rows_slice = array_2d[:2, :]
# Selecting the first two columns
columns_slice = array_2d[:, :2]
# Selecting a subarray from rows 0-1 and columns 1-2
subarray = array_2d[0:2, 1:3]
In these examples:
rows_slice
contains all columns from rows 0 and 1.columns_slice
contains all rows from columns 0 and 1.subarray
is a 2x2 array containing elements from rows 0-1 and columns 1-2.
Explanation of the slicing syntax:
array_2d[start_row:end_row, start_col:end_col]
- If
start
orend
is omitted, it defaults to the beginning or end of that dimension. - The
:
operator indicates the entire range in that dimension.
Examples:
array_2d[1, :]
selects all columns in row 1.array_2d[:, 2]
selects all rows in column 2.array_2d[1:, 1:]
selects rows 1 to the end and columns 1 to the end.
Higher-Dimensional Arrays¶
NumPy arrays can have more than two dimensions. The indexing and slicing principles extend naturally to higher dimensions.
# Creating a 3D NumPy array
array_3d = np.array([
[ # Depth 0
[1, 2, 3], # Row 0
[4, 5, 6] # Row 1
],
[ # Depth 1
[7, 8, 9], # Row 0
[10, 11, 12] # Row 1
]
])
print(array_3d)
# Accessing an element at depth 1, row 0, column 2
element = array_3d[1][0][2]
print(element)
# Alternatively, using comma-separated indices
element_alt = array_3d[1, 0, 2]
[[[ 1 2 3] [ 4 5 6]] [[ 7 8 9] [10 11 12]]] 9
Here, element
and element_alt
both access the value 9
at the specified indices.
Slicing in higher dimensions follows the same pattern:
# Slicing the first layer (depth 0)
layer = array_3d[0, :, :]
# Slicing all depths, row 1, columns 0-1
slice_3d = array_3d[:, 1, :2]
Conditional Selection¶
Conditional selection allows you to select elements based on conditions rather than explicit indices. This is powerful for data analysis and preprocessing.
Using Comparison Operators¶
You can use comparison operators to create boolean arrays, which can then be used to index the array.
# Creating an array for demonstration
arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])
# Selecting elements less than 5
less_than_five = arr[arr < 5]
print(less_than_five)
# Selecting elements greater than or equal to 7
greater_or_equal_seven = arr[arr >= 7]
[1 2 3 4]
In these examples:
arr < 5
produces a boolean array where elements less than 5 areTrue
.less_than_five
contains the elements where the condition isTrue
.
Combining Conditions¶
You can combine multiple conditions using logical operators such as &
(and), |
(or), and ~
(not).
# Selecting even numbers
even_numbers = arr[arr % 2 == 0]
# Selecting odd numbers greater than 5
odd_numbers_gt_five = arr[(arr % 2 != 0) & (arr > 5)]
# Selecting numbers less than 3 or greater than 7
lt_three_or_gt_seven = arr[(arr < 3) | (arr > 7)]
Note: When combining conditions:
- Enclose each condition in parentheses.
- Use
&
for logical AND. - Use
|
for logical OR. - Use
~
to negate a condition.
Boolean Indexing¶
You can create a boolean array and use it to index your array.
# Creating a boolean array
bool_array = arr > 5
# Using the boolean array to index 'arr'
selected_elements = arr[bool_array]
In this example:
bool_array
is a boolean array where elements greater than 5 areTrue
.selected_elements
contains elements fromarr
wherebool_array
isTrue
.
# Creating an array
arr = np.array([10, 20, 30, 40, 50])
# Selecting elements at indices 1, 3, and 4
indices = [1, 3, 4]
selected_elements = arr[indices]
print(selected_elements)
[20 40 50]
Here, selected_elements
contains elements at indices 1, 3, and 4.
Fancy Indexing¶
Fancy indexing allows you to pass arrays of indices to select specific elements.
# Creating a 2D array
arr_2d = np.array([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
# Selecting elements at (0,0), (1,1), and (2,2)
row_indices = np.array([0, 1, 2])
col_indices = np.array([0, 1, 2])
diagonal_elements = arr_2d[row_indices, col_indices]
In this example, diagonal_elements
contains the elements 1
, 5
, and 9
.
Ellipsis (...
) in Indexing¶
The ellipsis ...
is used to represent as many colons as needed to produce a complete indexing tuple.
# For a 3D array
array_3d = np.random.rand(2, 3, 4)
# Selecting all elements in the first dimension
slice_ellipsis = array_3d[0, ...]
array([[0.67422635, 0.83676805, 0.55202531, 0.60142282], [0.78950226, 0.5517025 , 0.35208583, 0.17114877], [0.94841662, 0.99977504, 0.60292442, 0.15129033]])
Here, slice_ellipsis
selects all elements in depth 0 across all rows and columns.