Ordinary fancy indexing with two index arrays pairs them up element-wise, selecting specific (row, column) coordinate pairs rather than a rectangular block. np.ix_() reshapes each input array into a compatible broadcasting shape, a column vector for the first, a row vector for the second, and so on, so that indexing with the result instead selects every combination of the given rows crossed with the given columns — exactly the rectangular sub-block that naive two-array fancy indexing does not give you.
1Understanding np.ix_()
Ordinary fancy indexing with two index arrays pairs them up element-wise, selecting specific (row, column) coordinate pairs rather than a rectangular block. np.ix_() reshapes each input array into a compatible broadcasting shape, a column vector for the first, a row vector for the second, and so on, so that indexing with the result instead selects every combination of the given rows crossed with the given columns — exactly the rectangular sub-block that naive two-array fancy indexing does not give you.
Whenever you want 'these specific rows and these specific columns, every combination', reach for np.ix_() — plain fancy indexing with two lists gives you paired coordinates instead, which is a very different, easy-to-miss result.
import numpy as np
matrix = np.arange(16).reshape(4, 4)
rows = [0, 2]
cols = [1, 3]
print(matrix[np.ix_(rows, cols)])2Practical Example
Here is a real-world application of np.ix_() showing how it is used in production NumPy code.
import numpy as np
matrix = np.arange(16).reshape(4, 4)
rows = [0, 2]
cols = [1, 3]
print(matrix[rows, cols])3Best Practices
Follow these guidelines when working with np.ix_():
1. Use np.ix_() specifically when you want a rectangular cross-section of chosen rows and chosen columns, not paired coordinates
2. Compare the result's shape against your expectation after using np.ix_(), since the broadcasting-based mechanism it relies on can be non-obvious at first
3. Prefer simple slicing when the rows/columns you want happen to be contiguous — reach for np.ix_() specifically for arbitrary, non-contiguous selections
Tip: Whenever you want 'these specific rows and these specific columns, every combination', reach for np.ix_() — plain fancy indexing with two lists gives you paired coordinates instead, which is a very different, easy-to-miss result.
import numpy as np
matrix = np.arange(16).reshape(4, 4)
rows = [0, 2]
cols = [1, 3]
print(matrix[np.ix_(rows, cols)])