Skip to main content

Python NumPy: How to Extract a Submatrix from a 2D Array

Extracting a specific portion, or submatrix, from a larger 2D NumPy array is a fundamental operation in data manipulation and scientific computing. Whether you need to select specific rows and columns based on their indices, or pick out a contiguous block of data, NumPy offers powerful and efficient indexing mechanisms.

This guide will comprehensively demonstrate two primary methods for extracting submatrices: the versatile numpy.ix_() function for constructing indexers from sequences of row and column indices, and standard NumPy array slicing for selecting contiguous or regularly stepped portions of the array. You'll learn how to apply both techniques to precisely select the submatrix you need.

Understanding Submatrix Extraction

A submatrix is essentially a smaller 2D array derived from a larger 2D array by selecting a specific set of its rows and columns. The goal is to isolate a rectangular portion of the original array.

Let's define a sample 2D NumPy array for our examples:

import numpy as np

main_array = np.array([
[10, 11, 12, 13, 14], # Row 0
[20, 21, 22, 23, 24], # Row 1
[30, 31, 32, 33, 34], # Row 2
[40, 41, 42, 43, 44], # Row 3
[50, 51, 52, 53, 54] # Row 4
])
print("Original 2D NumPy Array (main_array):")
print(main_array)

Output:

Original 2D NumPy Array (main_array):
[[10 11 12 13 14]
[20 21 22 23 24]
[30 31 32 33 34]
[40 41 42 43 44]
[50 51 52 53 54]]

Method 1: Using numpy.ix_() for Arbitrary Row and Column Selection

The numpy.ix_() function is particularly useful when you want to construct a submatrix from specific, potentially non-contiguous, rows and columns. You provide sequences of row indices and column indices, and np.ix_() creates an open mesh that can be used for indexing.

How numpy.ix_() Works

np.ix_(rows_list, cols_list) takes lists (or arrays) of row indices and column indices. It returns a tuple of arrays that, when used to index the original array, effectively selects elements at the intersection of the specified rows and columns.

import numpy as np

# main_array defined as above
main_array = np.array([
[10, 11, 12, 13, 14], # Row 0
[20, 21, 22, 23, 24], # Row 1
[30, 31, 32, 33, 34], # Row 2
[40, 41, 42, 43, 44], # Row 3
[50, 51, 52, 53, 54] # Row 4
])

# Example: Select rows 0 and 3, and columns 1 and 4
row_indices = [0, 3]
col_indices = [1, 4]

# What np.ix_() produces:
index_constructor = np.ix_(row_indices, col_indices)
print(f"Output of np.ix_({row_indices}, {col_indices}):")
print(index_constructor)

Output:

Output of np.ix_([0, 3], [1, 4]):
(array([[0],
[3]]), array([[1, 4]]))
note

This tuple of arrays, when used as an index, tells NumPy to pick:

  • From rows specified by the first array ([[0], [3]])
  • From columns specified by the second array ([[1, 4]]) This results in elements at (0,1), (0,4), (3,1), (3,4)

Extracting a Submatrix with Specific Row and Column Indices

Using the index_constructor from np.ix_() to index main_array:

import numpy as np

# main_array, row_indices, col_indices, and index_constructor defined as above
main_array = np.array([
[10, 11, 12, 13, 14], # Row 0
[20, 21, 22, 23, 24], # Row 1
[30, 31, 32, 33, 34], # Row 2
[40, 41, 42, 43, 44], # Row 3
[50, 51, 52, 53, 54] # Row 4
])
row_indices = [0, 3]
col_indices = [1, 4]
index_constructor = np.ix_(row_indices, col_indices)


submatrix_ix = main_array[index_constructor]
# Or more directly:
# submatrix_ix = main_array[np.ix_([0, 3], [1, 4])]

print("Submatrix using np.ix_([0, 3], [1, 4]):")
print(submatrix_ix)

Output:

Submatrix using np.ix_([0, 3], [1, 4]):
[[11 14]
[41 44]]

This submatrix consists of elements from:

  • Row 0, Column 1 (11) and Row 0, Column 4 (14)
  • Row 3, Column 1 (41) and Row 3, Column 4 (44)

More Examples of numpy.ix_()

  • Extract elements from rows 1 and 4, and columns 0 and 2:

    import numpy as np

    main_array = np.array([
    [10, 11, 12, 13, 14], # Row 0
    [20, 21, 22, 23, 24], # Row 1
    [30, 31, 32, 33, 34], # Row 2
    [40, 41, 42, 43, 44], # Row 3
    [50, 51, 52, 53, 54] # Row 4
    ])

    submatrix_ex2 = main_array[np.ix_([1, 4], [0, 2])]
    print("Submatrix from rows [1, 4] and columns [0, 2]:")
    print(submatrix_ex2)

    Output:

    Submatrix from rows [1, 4] and columns [0, 2]:
    [[20 22]
    [50 52]]
  • Extract a "checkerboard" pattern - rows 0, 2, 4 and columns 0, 2, 4:

    import numpy as np

    main_array = np.array([
    [10, 11, 12, 13, 14], # Row 0
    [20, 21, 22, 23, 24], # Row 1
    [30, 31, 32, 33, 34], # Row 2
    [40, 41, 42, 43, 44], # Row 3
    [50, 51, 52, 53, 54] # Row 4
    ])

    submatrix_ex3 = main_array[np.ix_([0, 2, 4], [0, 2, 4])]
    print("Submatrix from rows [0, 2, 4] and columns [0, 2, 4]:")
    print(submatrix_ex3)

    Output:

    Submatrix from rows [0, 2, 4] and columns [0, 2, 4]:
    [[10 12 14]
    [30 32 34]
    [50 52 54]]

Method 2: Using Standard NumPy Array Slicing

Standard NumPy slicing is excellent for extracting contiguous blocks of data or selecting elements with a regular step. The syntax is array[row_slice, col_slice], where each slice can be start:stop:step.

Basic Slicing for Contiguous Submatrices

To select a block of rows and columns. Remember that the stop index is exclusive.

import numpy as np

# main_array defined as above
main_array = np.array([
[10, 11, 12, 13, 14], # Row 0
[20, 21, 22, 23, 24], # Row 1
[30, 31, 32, 33, 34], # Row 2
[40, 41, 42, 43, 44], # Row 3
[50, 51, 52, 53, 54] # Row 4
])

# Example: Select rows 0 through 2 (exclusive of 3, so rows 0, 1, 2)
# and columns 1 through 3 (exclusive of 4, so columns 1, 2, 3)
submatrix_slice1 = main_array[0:3, 1:4] # Rows 0,1,2 and Columns 1,2,3

print("Submatrix using slice [0:3, 1:4]:")
print(submatrix_slice1)
print()

# Select first 2 rows and first 3 columns:
submatrix_slice2 = main_array[:2, :3] # Omitting start implies 0
print("Submatrix from first 2 rows, first 3 columns [:2, :3]:")
print(submatrix_slice2)

Output:

Submatrix using slice [0:3, 1:4]:
[[11 12 13]
[21 22 23]
[31 32 33]]

Submatrix from first 2 rows, first 3 columns [:2, :3]:
[[10 11 12]
[20 21 22]]

Slicing with Steps for Non-Contiguous Regular Selections

The step component of a slice allows you to pick elements at regular intervals.

import numpy as np

# main_array defined as above
main_array = np.array([
[10, 11, 12, 13, 14], # Row 0
[20, 21, 22, 23, 24], # Row 1
[30, 31, 32, 33, 34], # Row 2
[40, 41, 42, 43, 44], # Row 3
[50, 51, 52, 53, 54] # Row 4
])

# Example: Select every second row (starting from row 0)
# and every second column (starting from col 0)
# This effectively takes rows 0, 2, 4 and columns 0, 2, 4
submatrix_stepped_slice = main_array[0::2, 0::2]
# 0::2 means start at 0, go to end, step by 2.
# Can also be written as main_array[::2, ::2] if starting from beginning.

print("Submatrix using stepped slice [::2, ::2]:")
print(submatrix_stepped_slice)
print()

# Example: Select every third row from 0 to 3 (exclusive for stop)
# and every third col from 0 to 3 (exclusive for stop)
# On a 4x4 array, this would be arr[0:4:3, 0:4:3] -> rows 0,3 and cols 0,3
arr_4x4 = np.arange(1, 17).reshape(4, 4)
print("Original 4x4 array:")
print(arr_4x4)
print()

sub_4x4 = arr_4x4[0:4:3, 0:4:3] # Rows 0, 3. Columns 0, 3.
print("Submatrix from 4x4 array [0:4:3, 0:4:3]:")
print(sub_4x4)

Output:

Submatrix using stepped slice [::2, ::2]:
[[10 12 14]
[30 32 34]
[50 52 54]]

Original 4x4 array:
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]
[13 14 15 16]]

Submatrix from 4x4 array [0:4:3, 0:4:3]:
[[ 1 4]
[13 16]]

Choosing Between np.ix_() and Standard Slicing

  • Use numpy.ix_() when:

    • You need to select rows and columns based on arbitrary, potentially non-sequential lists of indices.
    • You want to form a submatrix from specific "chosen" rows and columns.
    • Example: Rows [0, 3, 4] and Columns [1, 5, 6] from a larger matrix.
  • Use Standard Slicing (array[start:stop:step, start:stop:step]) when:

    • You are selecting a contiguous block of rows and columns.
    • You are selecting rows/columns with a regular step (e.g., every other row).
    • It's generally more concise for these specific patterns.

If you try to use a list of indices directly with standard slicing for both dimensions like arr[[0,3], [1,3]], NumPy performs "advanced indexing" which pairs up the indices, returning [arr[0,1], arr[3,3]] (a 1D array), not the 2x2 submatrix that np.ix_ would give. This is a key difference.

Conclusion

Extracting submatrices is a core NumPy skill.

  • For selecting elements based on specific lists of row and column indices to form a rectangular submatrix, numpy.ix_() is the appropriate tool (e.g., array[np.ix_([row_idx_list], [col_idx_list])]).
  • For selecting contiguous blocks or regularly stepped elements, standard NumPy slicing (array[row_slice, col_slice]) is efficient and direct.

Understanding these two powerful indexing methods allows you to precisely select and manipulate any desired portion of your 2D NumPy arrays.